侧滑面板很其实现在容易找到成熟的第三方框架了,但是我们自己做一下,写一些核心代码,有助于我们的理解
1,简单介绍
写一个类继承ViewGroup
复写以下三个方法
onMeasure -> onLayout -> onDraw
1,测量左面板和主面板
左面板宽是指定的值240, 高度是屏幕高度
主面板宽高就是屏幕的宽高
2,摆放两个子控件
左面板的位置: 当前屏幕左边界0, 往左-240
主面板位置 : 当前控件的位置
3,处理触摸事件 (左右移动两个控件)
按下时, 获取按下的坐标downX = 20
移动时,
获取当前最新x坐标 moveX = 30
计算要执行的偏移量 - (30 - 20) = -10
让偏移生效scrollBy(-10, 0)
moveX 赋值给 downX, 作为新的按下点
抬起时, 根据当前偏移位置, 执行动画 (执行平滑动画)
二,核心代码
/** * 侧滑面板 * @author poplar * */ public class SlideMenu extends ViewGroup { private final int MAIN_CONTENT = 0; // 主面板状态 private final int MENU_CONTENT = 1; // 左菜单状态 int currentState = MAIN_CONTENT; // 当前状态 private Scroller scroller; private int downX; // 按下的X值坐标 private int downY; // 按下的Y值坐标 public SlideMenu(Context context) { super(context); init(); } public SlideMenu(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SlideMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { // 滚动模拟器 scroller = new Scroller(getContext()); // startX, x轴从什么位置开始滚动 -100 // startY, y轴从什么位置开始滚动 // dx, 从开始位置 到 目标位置的 偏移量 -100 -> 50 // 偏移量 150 = (50 - (-100)) // 偏移量 = (目标位置 x) - (开始位置x) // dy, // duration , 动画执行时长 dx * 10 // scroller.startScroll(startX, startY, dx, dy, duration); } /** * 测量子控件 * widthMeasureSpec : 当前控件的宽度属性 * heightMeasureSpec : 当前控件的高度属性 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 获取到左面板 View leftMenu = getChildAt(0); // 宽度自身宽度, 高度屏幕的高度 leftMenu.measure(leftMenu.getLayoutParams().width, heightMeasureSpec); // 测量主面板 View main = getChildAt(1); main.measure(widthMeasureSpec, heightMeasureSpec); } /** * 摆放两个子控件 * * left 控件左边界位置 * top 控件上边界位置 * right 控件右边界位置 * bottom 控件下边界位置 * */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 摆放左面板 View leftMenu = getChildAt(0); leftMenu.layout(0 - leftMenu.getMeasuredWidth(), 0, 0, b); // 摆放主面板 View main = getChildAt(1); main.layout(l, t, r, b); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 按下时的x坐标 downX = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: // 移动后的x坐标 int moveX = (int) event.getX(); // 变化量 int scrollX = - (moveX - downX); // ------------------------------------------------- 重点 ↓ // 计算变化后的位置 int newScrollX = getScrollX() + scrollX; int leftLimit = -getChildAt(0).getMeasuredWidth(); if(newScrollX < leftLimit){ // < -240 scrollTo(leftLimit, 0); } else if (newScrollX > 0) { // >0 限定右边界 scrollTo(0, 0); } else { // 让变化量生效 scrollBy(scrollX, 0); } // ------------------------------------------------- 以上 ↑ //把上一个moveX赋值给downX downX = moveX; break; case MotionEvent.ACTION_UP: // 抬起时, 根据当前偏移位置, 执行动画 int leftMenuCenter = - getChildAt(0).getMeasuredWidth() / 2; int currentScrollX = getScrollX(); System.out.println("currentScrollX: " + currentScrollX); System.out.println("leftMenuCenter: " + leftMenuCenter); if(currentScrollX > leftMenuCenter){ // 如果当前滚动的位置在一半的右边 , 显示主面板 , scrollX > leftMenuCenter currentState = MAIN_CONTENT; System.out.println("执行显示主面板动画"); updateCurrentContent(); }else { // 如果当前滚动的位置在一半的左边 , 显示左菜单 , scrollX <= leftMenuCenter currentState = MENU_CONTENT; System.out.println("执行显示左菜单动画"); updateCurrentContent(); } break; default: break; } return true; } private void updateCurrentContent() { int scrollX = getScrollX(); int dx = 0; if(currentState == MAIN_CONTENT){ // 执行动画,显示主面板 // scrollTo(0, 0); // 目标值 - 当前值 dx = 0 - scrollX; } else if(currentState == MENU_CONTENT){ // 执行动画,显示左菜单 // scrollTo(- getChildAt(0).getMeasuredWidth(), 0); // 目标值 - 当前值 // - 240 - dx = -getChildAt(0).getMeasuredWidth() - scrollX; System.out.println("dx: " + dx + " scrollX: " + scrollX); } // ------------------------------------------------- 重点 ↓ // 开始模拟数据 scroller.startScroll(scrollX, 0, dx, 0, Math.abs(dx * 10)); // -100 -> -240 >> -150 // 使scroller模拟的数据生效 invalidate(); // drawChild -> child.draw( -> computeScroll // ------------------------------------------------- 以上 ↑ } // ------------------------------------------------- 重点 ↓ @Override public void computeScroll() { if(scroller.computeScrollOffset()){ // 动画还没执行完 // 得到当前模拟的数值 int currX = scroller.getCurrX(); scrollTo(currX, 0); // System.out.println("currX: " + currX); // 重绘界面, 递归调用 invalidate(); } } // ------------------------------------------------- 以上 ↑ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 只有在用户水平滑动的时候才拦截 switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) ev.getX(); downY = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: int moveX = (int) ev.getX(); int moveY = (int) ev.getY(); int diffX = Math.abs(moveX - downX); int diffY = Math.abs(moveY - downY); if(diffX > diffY && diffX > 10){ return true; } break; case MotionEvent.ACTION_UP: break; default: break; } return super.onInterceptTouchEvent(ev); } }
时间: 2024-10-12 08:28:30