[关闭]
@weidong 2017-07-19T03:37:54.000000Z 字数 4679 阅读 365

ViewDragHelper 工具类

Android课程


https://juejin.im/entry/57c3e8ac165abd00666ae71e
https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis/blob/master/article/ViewDragHelper%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md

ViewDragHelper作为官方推出的手势滑动辅助工具,极大的简化了我们对手势滑动的处理逻辑

代码一:实现拖动子View

  1. public class DragLayout extends LinearLayout {
  2. private ViewDragHelper mDragHelper;
  3. public DragLayout(Context context, AttributeSet attrs) {
  4. super(context, attrs);
  5. //参数一:指的当前的ViewGroup,
  6. //参数二:滑动检测的敏感度,越大越敏感
  7. //参数三:用户的触摸过程中会回调相关方法
  8. mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
  9. // 对触摸view判断,如果需要当前触摸的子View进行拖拽移动就返回true,否则返回false
  10. // 返回值为true就代表可以滑动 为false 则不能滑动
  11. @Override
  12. public boolean tryCaptureView(View child, int pointerId) {
  13. return true;
  14. }
  15. // 拖拽的子View在所属方向上移动的位置,child为拖拽的子View,
  16. // left为child即将移动到的水平位置(x坐标)的值
  17. @Override
  18. public int clampViewPositionHorizontal(View child, int left, int dx) {
  19. return left;
  20. }
  21. //top为子view应该到达的y坐标
  22. @Override
  23. public int clampViewPositionVertical(View child, int top, int dy) {
  24. return top;
  25. }
  26. });
  27. }
  28. @Override
  29. public boolean onInterceptTouchEvent(MotionEvent event) {
  30. return mDragHelper.shouldInterceptTouchEvent(event);
  31. }
  32. @Override
  33. public boolean onTouchEvent(MotionEvent event) {
  34. mDragHelper.processTouchEvent(event);
  35. return true;
  36. }
  37. }

布局代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <com.jf.weidong.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent">
  7. <LinearLayout
  8. android:layout_width="100dp"
  9. android:layout_height="100dp"
  10. android:background="@color/colorAccent" />
  11. </com.jf.weidong.DragLayout>
  1. 在View初始化方法中创建ViewDragHelper对象
  2. 实现ViewDragHelper.Callback的相关方法。
  3. 在onInterceptTouchEvent方法中调用mViewDragHelper.shouldInterceptTouchEvent(ev)将事件传给ViewDragHelper
  4. 在onTouchEvent方法中调用ViewDragHelper.processTouchEvent方法并返回true。

代码二:控制部分View可以拖动

关键代码
在ViewDragHelper.Callback 回调的tryCaptureView方法里面进行判断,如果只有类型为RelativeLayout的View才可以拖动

  1. // 对触摸view判断,如果需要当前触摸的子View进行拖拽移动就返回true,否则返回false
  2. // 返回值为true就代表可以滑动 为false 则不能滑动
  3. @Override
  4. public boolean tryCaptureView(View child, int pointerId) {
  5. if(child instanceof RelativeLayout) {
  6. return true;
  7. }
  8. return false;
  9. }

XML 代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <com.jf.weidong.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent">
  7. <LinearLayout
  8. android:layout_width="100dp"
  9. android:layout_height="100dp"
  10. android:background="@color/colorAccent" />
  11. <RelativeLayout
  12. android:layout_width="100dp"
  13. android:layout_height="100dp"
  14. android:background="@color/colorPrimaryDark" />
  15. </com.jf.weidong.DragLayout>

代码三:控制View的拖动范围

关键代码

在ViewDragHelper.Callback 回调的clampViewPositionHorizontal()、clampViewPositionVertical()方法里面进行判断,最大拖动范围为屏幕边界(View边界)

  1. // 拖拽的子View在所属方向上移动的位置,child为拖拽的子View,
  2. // left为child即将移动到的水平位置(x坐标)的值
  3. @Override
  4. public int clampViewPositionHorizontal(View child, int left, int dx) {
  5. final int leftBound = getPaddingLeft();
  6. final int rightBound = getWidth() - child.getWidth() - leftBound;
  7. //在leftBound和rightBound之间 那么就返回left
  8. //如果left的值 比 leftbound还要小 那么就说明超过了左边界 那么返回左边界的值
  9. //如果left的值 比rightbound还要大 那么就说明超过了右边界,那么返回右边界的值
  10. final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
  11. return newLeft;
  12. }
  13. //同上只是这里是垂直方向(y坐标)
  14. @Override
  15. public int clampViewPositionVertical(View child, int top, int dy) {
  16. final int topBound = getPaddingTop();
  17. final int bottomBound = getHeight() - child.getHeight() - topBound;
  18. return Math.min(Math.max(top, topBound), bottomBound);
  19. }

代码三:View拖动后复位

需要重写ViewDragHelper.Callback的onViewReleased方法,该方法在View拖动结束后会回调
在该方法里面对View复位(移动回0,0位置)

关键代码

  1. @Override
  2. public void onViewReleased(View releasedChild, float xvel, float yvel) {
  3. //手指释放时可以自动复位
  4. if (releasedChild instanceof LinearLayout) {
  5. //设置View返回到0,0的位置
  6. mDragHelper.settleCapturedViewAt(0, 0);
  7. invalidate();//重新绘制
  8. }
  9. }

重写View的方法

  1. //view.invalidate(),会触发onDraw和computeScroll()
  2. @Override
  3. public void computeScroll() {
  4. ////如果滚动还没有结束
  5. if (mDragHelper.continueSettling(true)) {
  6. invalidate();
  7. }
  8. }

代码四:View边界拖动

需要重写ViewDragHelper.Callback的onEdgeDragStarted方法,在View的边界上拖动会回调该方法,但是必须设置属性:
mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);

边界拖动需要获取指定的View,可以重写View里面的onFinishInflate进行获取

  1. //在边界拖动时回调
  2. @Override
  3. public void onEdgeDragStarted(int edgeFlags, int pointerId)
  4. {
  5. mDragHelper.captureChildView(rl3, pointerId);
  6. }

获取View的方法

  1. @Override
  2. protected void onFinishInflate() {
  3. super.onFinishInflate();
  4. rl3 = findViewById(R.id.rl3);
  5. }

代码五:Buttom点击事件冲突

如果将拖动的View换成Buttom,那么拖动效果就失效了
主要是因为Buttom消耗了拖动事件
重写两个方法

  1. //获取被拖拽View child 的水平拖拽范围,返回0表示无法被水平拖拽
  2. @Override
  3. public int getViewHorizontalDragRange(View child)
  4. {
  5. return getMeasuredWidth()-child.getMeasuredWidth();
  6. }
  7. //获取被拖拽View child 的垂直拖拽范围,返回0表示无法被水平拖拽
  8. @Override
  9. public int getViewVerticalDragRange(View child)
  10. {
  11. return getMeasuredHeight()-child.getMeasuredHeight();
  12. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注