ViewDragHelper 的学习一

ViewDragHelper 的学习一

ViewDragHelper是一个在自定义viewgroup的时候比较神奇的一个类,可以帮我们的实现拖拽等的一些功能,比如实现拖动的效果等。在拖过的时候我们可以限制其可拖拽的范围等一些参数。通过学习这个我们可以自己手动实现一个简单的drawerlayout。

ViewDragHelper的介绍

SlidingPaneLayout和DrawerLayout,现在这俩个类被广泛的运用,其实研究他们的源码你会发现这两个类都运用了ViewDragHelper来处理拖动。是属于在v4包中的一个类。

ViewDragHelper的常见的用法

1 创建,初始化ViewDragHelper和ViewDragHelper.Callback,其中的ViewDragHelper是用来帮助拖拽的类,而ViewDragHelper.Callback的用来联系ViewDragHelper与viewgroup之间的桥梁,主要ViewDragHelper.Callback的监听方法实现我们需要的一系列操作。

//初始化ViewDragHelper
ViewDragHelper create(ViewGroup forParent, Callback cb);
//初始化Callback
private Callback mCallback = new Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return mView1 == child || mView2 == child;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return top;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if (releasedChild == mView2) {
                mHelper.settleCapturedViewAt(mView2Point.x, mView2Point.y);
                invalidate();
            }
            if (releasedChild == mView1) {
                mHelper.flingCapturedView(0, 0, 10000, 10000);
                invalidate();
            }


        }
    };

2 在viewGroup()中onTouchEvent();return true,为了能够消费事件

    public boolean onTouchEvent(MotionEvent event) {
        mHelper.processTouchEvent(event);
        return true;
    }

最后贴上整个完整的源码

public class VDHViewGroup extends RelativeLayout {
    private ViewDragHelper mHelper;
    private Point mView2Point;
    private View mView1;
    private View mView2;

    private Callback mCallback = new Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return mView1 == child || mView2 == child;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return top;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if (releasedChild == mView2) {
                mHelper.settleCapturedViewAt(mView2Point.x, mView2Point.y);
                invalidate();
            }
            if (releasedChild == mView1) {
                mHelper.flingCapturedView(0, 0, 10000, 10000);
                invalidate();
            }


        }
    };

    public VDHViewGroup(Context context) {
        this(context, null);
    }

    public VDHViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mHelper = ViewDragHelper.create(this, mCallback);
        mView2Point = new Point();
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mHelper.processTouchEvent(event);
        return true;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mView2Point.x = mView2.getLeft();
        mView2Point.y = mView2.getTop();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mView1 = getChildAt(0);
        mView2 = getChildAt(1);

    }


    //这里用来处理手离开的CaptureView的view回调的,fling的手势和回弹的模式,因为viewdraghelper内部时借助Scroller实现的。
    @Override
    public void computeScroll() {
        if (mHelper.continueSettling(true)) {
            invalidate();
        }
    }
}

这里第一view是可以处理fling的手势,第二个view的可以的回到原来的位置的。
这里写图片描述
其中大象的是具有滑动的惯性的view1,而小狗是手指离开的回返回到原来的位置。

3,viewgroup中有消费的事件的view (clickable = true 或者longclickable = true)关于消费事件可以去看下我以前的博客ViewGroup和View的事件传递原理,我们都是知道事件传递是会传递子view去消费的,这个时候viewgroup的是的onTouchEvent是不会触发的,这个时候我们就需要拦截了,可以写成的下面的这样子的

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return true;
    }

同样也就可以出现的上面效果但是呢?我们这样真的好吗?这个意味着我们子view的没有办法消费事件,比如点击事件,长按事件的。对吧,但是ViewDragHelper的帮我实现这样的功能,我们需要在

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mHelper.shouldInterceptTouchEvent(ev);
    }

但是这样是不够的,我们还需要Callback中的重新重写下面的方法

@Override
        public int getViewHorizontalDragRange(View child) {
            //写个大于0的值
            return 1;
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            //写个大于0的值
            return 1;
        }

这样就可以又可以实现了刚上来的效果,并且能够让子view实现消费事件

常见api的说明

ViewDragHelper

//创建ViewDragHelper的对象
ViewDragHelper create(ViewGroup forParent, Callback cb)
//ViewDragHelper处理onTouchEvent的,注意没有返回值的
 public void processTouchEvent(MotionEvent ev)
//判断是否需要拦截孩子的事件的
public boolean shouldInterceptTouchEvent(MotionEvent ev)
//判断滑动是不是还在继续,和Scroller的public boolean computeScrollOffset() 相似,其实内部也是这么实现的。
public boolean continueSettling(boolean deferCallbacks);
//将CaptureView的Scroll到制定的位置
public boolean settleCapturedViewAt(int finalLeft, int finalTop)
//将CaptureView的fling到制定的位置,前提是CaptureView能够fling的
public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop)
//设置边缘触发,边缘出发方向
//EDGE_LEFT,EDGE_RIGHT,EDGE_TOP,EDGE_BOTTOM
public void setEdgeTrackingEnabled(int edgeFlags)

ViewDragHelper.Callback

1

//判断viewgroup中那些子view可以被拖拽
 public boolean tryCaptureView(View child, int pointerId)

2

public int clampViewPositionHorizontal(View child, int left, int dx) 

计算的实现的拖债过程的子view的left,有点难理解,我先说下参数吧,第一个child位拖拽的view,第二个left,是child拖拽前的位置left+move的事件的distancex,也就是实际手指拖拽的距离加之前的left的,第三个dx也就是distancex;所以这个方法为了处理一些边缘条件,比如我们需要让子view不能画出屏幕外(左右),就可以加上下面的条件

public int clampViewPositionHorizontal(View child, int left, int dx) {
      if (left < getLeft()) {
          left = getLeft();
      }
      if (left > getRight() - (child.getRight() - child.getLeft())) {
          left = getRight() - (child.getRight() - child.getLeft());
      }
      return left;
}

3

 public int clampViewPositionVertical(View child, int top, int dy)

和上面的同理,如果想限制上下的屏幕的可加以下的代码

public int clampViewPositionVertical(View child, int top, int dy) {
      if (top < getTop()) {
          top = getTop();
      }
      if (top > getBottom() - (child.getBottom() - child.getTop())) {
          top = getBottom();
      }
      return top;
}

4

//判断水平方向能否拖拽,
public int getViewHorizontalDragRange(View child) 

5

//判断垂直方向能否拖拽,
public int getViewVerticalDragRange(View child)

6

//手指离开拖拽的view的回调
public void onViewReleased(View releasedChild, float xvel, float yvel)

7

//手指刚接触的拖拽的view的回调
public void onViewCaptured(View capturedChild, int activePointerId)

8

//拖拽的状态发生改变的回调,三种状态
//STATE_IDLE
//STATE_DRAGGING
//STATE_SETTLING  fling or predefined non-interactive motion.
public void onViewDragStateChanged(int state)

9

//边缘开始拖拽的时候的回调(ViewGroup)
 public void onEdgeDragStarted(int edgeFlags, int pointerId) 

10

//边缘开始触摸的时候的回调(ViewGroup),注意与上面的区别
 public void onEdgeTouched(int edgeFlags, int pointerId)

11

//子view的位置发生改变的时候回调
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)

完整的例子

public class VDHViewGroup extends RelativeLayout {
    private ViewDragHelper mHelper;
    private Point mView2Point;
    private View mView1;
    private View mView2;

    private Callback mCallback = new Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return mView1 == child || mView2 == child;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return top;
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return 1;
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            return 1;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if (releasedChild == mView2) {
                mHelper.settleCapturedViewAt(mView2Point.x, mView2Point.y);
                invalidate();
            }
            if (releasedChild == mView1) {
                mHelper.flingCapturedView(0, 0, 10000, 10000);
                invalidate();
            }


        }
    };

    public VDHViewGroup(Context context) {
        this(context, null);
    }

    public VDHViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mHelper = ViewDragHelper.create(this, mCallback);
        mView2Point = new Point();
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mView2Point.x = mView2.getLeft();
        mView2Point.y = mView2.getTop();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mView1 = getChildAt(0);
        mView2 = getChildAt(1);

    }

    @Override
    public void computeScroll() {
        if (mHelper.continueSettling(true)) {
            invalidate();
        }
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<com.idreamo.rrtoyewx.studyviewdraghelper.VDHViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/animal_1"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@drawable/animal_2"/>

</com.idreamo.rrtoyewx.studyviewdraghelper.VDHViewGroup>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值