ViewDragHelper 详解(一)

一、ViewDragHelper是什么
>看到这个类,我们首先应该想到它是什么?

从字面意思中我们可以大概猜到了一点。那么它具体是做什么的呢?官方文档对这个类进行了简单的阐述:

ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.

ViewDragHelper是一个实用的类,用来开发自定义ViewGroup。它定义了一组有用的操作和状态追踪,允许用户在父ViewGroup中拖动并且重新定位子View(child view)。

从官方的介绍中我们了解到了几个关键点:

  1. 自定义ViewGroup
  2. 拖动以及重新定位
  3. 操作和状态追踪

二、ViewHelper的看的见的地方有哪些?

当我们看到一个未知的东西,我们的心里总是充满疑惑,想要探究一下可以看得见和摸得着的感知。 在我的系列文章中也将会给大家奉献看得见的GIF,将相关函数和具体表现形象的展示出来。

  1. DrawerLayout
  2. QQ5.0以上的侧滑效果

三、 从实例来讲解ViewDragHelper

我们将会看到常用函数的用法

步骤一: 创建我们自定义的LinearLayout

public class VDHLinearLayout extends LinearLayout {

    private ViewDragHelper mViewDragHelper = null;

    public VDHLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        createVDH(null);
    }

    public void createVDH(ViewDragHelper.Callback callback) {
        if (callback == null) {
            mViewDragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
                @Override
                public boolean tryCaptureView(View child, int pointerId) {
                    return true;
                }
            });
        } else {
            mViewDragHelper = ViewDragHelper.create(this, 1.0f, callback);
        }
    }

      @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mViewDragHelper == null) {
            return super.dispatchTouchEvent(event);
        }
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mViewDragHelper == null) {
            return super.onInterceptTouchEvent(ev);
        }
        int keyCode = ev.getAction();
        if (keyCode == MotionEvent.ACTION_CANCEL) {
            mViewDragHelper.cancel();
            return false;
        }
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }
    public ViewDragHelper getViewDragHelper() {
        return mViewDragHelper;
    }
}

步骤二:创建布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<com.demonli.vdh.view.VDHLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    tools:context="com.demonli.vdh.MainActivity"
    android:id="@+id/root_vdh"
    android:paddingTop="10dp"
    android:paddingBottom="30dp"
    android:paddingLeft="15dp" 
    android:paddingRight="20dp"
    >

    <TextView
        android:id="@+id/child1"
        android:layout_margin="10dp"
        android:gravity="center"
        android:layout_gravity="center"
        android:background="@color/colorPrimary"
        android:text="CommonDragView"
        android:layout_width="100dp"
        android:layout_height="100dp"/>

    <TextView
        android:id="@+id/child2"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:background="@color/colorAccent"
        android:text="AutobackView"
        android:layout_width="100dp"
        android:layout_height="100dp"/>

    <TextView
        android:id="@+id/child3"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:background="@android:color/holo_red_dark"
        android:text="EdgeTrackerView"
        android:layout_width="100dp"
        android:layout_height="100dp"/>

</com.demonli.vdh.view.VDHLinearLayout>

步骤三:创建MainActivity

public class MainActivity extends AppCompatActivity {

    private VDHLinearLayout mVDHLiearLayout;
    private TextView mDragView;
    private TextView mAutobackView;
    private TextView mEdgeView;

    Point mAutobackPoint = new Point();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mDragView = (TextView) findViewById(R.id.child1);
        mAutobackView = (TextView) findViewById(R.id.child2);
        mEdgeView = (TextView) findViewById(R.id.child3);
        mVDHLiearLayout = (VDHLinearLayout) findViewById(R.id.root_vdh);
        init();
    }

    protected void init() {
        mVDHLiearLayout.createVDH(new VDHCallback());
        mAutobackView.postDelayed(new Runnable() {
            @Override
            public void run() {
                mAutobackPoint.set(mAutobackView.getLeft(), mAutobackView.getTop());
            }
        }, 0);
    }

    class VDHCallback extends ViewDragHelper.Callback {

        /**
         * 此例中 mEdgeView将无法被拖动
         * 此函数必须实现
         *
         * @param child
         * @param pointerId
         * @return if false,那么child view将无法拖动(Drag)
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            if (child == mEdgeView) {//
                return false;
            }
            return true;
        }

        /**
         * 实现对child view x 轴方向的控制
         *
         * @param child
         * @param left
         * @param dx
         * @return
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            int leftBound = mVDHLiearLayout.getPaddingLeft();
            int rightBound = mVDHLiearLayout.getWidth() - mVDHLiearLayout.getPaddingRight() - child.getWidth();
//                if(left<leftBound){
//                    left = leftBound;
//                }else if(left>rightBound){
//                    left = rightBound;
//                }
            //更加简洁的写法
            left = Math.min(Math.max(left, leftBound), rightBound);
            return left;
        }

        /**
         * 对child view y 轴方向的控制
         *
         * @param child
         * @param top
         * @param dy
         * @return
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            int topBound = mVDHLiearLayout.getPaddingTop();
            int bottomBound = mVDHLiearLayout.getHeight() - child.getHeight() - mVDHLiearLayout.getPaddingBottom();
            if (top < topBound) {
                top = topBound;
            } else if (top > bottomBound) {
                top = bottomBound;
            }
            return top;
        }
    }

}

经过步骤一、二、三之后我们可以看到以下的效果:

《ViewDragHelper 详解(一)》 这里写图片描述

我们会发现:
1、mDragView和mAutobackView可以Drag,而mEdgeView没有移动。
2、同时我们会主要到mDragView和mAutobackView无法到达屏幕的边界。

1、tryCaptureView( ) :试图捕获child view的drag 事件,如果指定子view 返回false,则会出现像mEdgeView那样无法Drag的情况。
2、布局文件activity_main.xml中我们定义根布局中的paddingLeft等,并且在MainActivity中实现了clampViewPositionVertical()与clampViewPositionHorizontal()两个函数。函数的具体意义见代码中的注释。

步骤四:实现mAutobackView的拖动后自动返回到原来的位置:

在VDHLinearLayout中添加:

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

在MainActivty中添加:

 @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if(releasedChild==mAutobackView){
                mVDHLiearLayout.getViewDragHelper().settleCapturedViewAt(mAutobackPoint.x,mAutobackPoint.y);
                mVDHLiearLayout.invalidate();
            }
        }

接下来我们会看到这样的效果:

《ViewDragHelper 详解(一)》 这里写图片描述

我们可以看出来mAutobackView自动的回到原来的位置。

1、首先我们保存了mAutobackView的初始坐标

mAutobackView.postDelayed(new Runnable() {
            @Override
            public void run() {
                mAutobackPoint.set(mAutobackView.getLeft(), mAutobackView.getTop());
            }
        }, 0);

然后我们在VDHCallback实现了:

 /**
         * 此函数用于捕捉用户的ACTION_UP
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if(releasedChild==mAutobackView){
                mVDHLiearLayout.getViewDragHelper().settleCapturedViewAt(mAutobackPoint.x,mAutobackPoint.y);
                mVDHLiearLayout.invalidate();
            }
        }

步骤五:实现mEdgeView从屏幕边界滑动时的移动效果

 @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            // super.onEdgeDragStarted(edgeFlags, pointerId);
            mVDHView.getViewDragHelper().captureChildView(mEageView, pointerId);

        }
mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);

《ViewDragHelper 详解(一)》 这里写图片描述

这样我们的mEdgeView便可拖动了。

对于ViewDragHelper.Callback中没有讲到的方法如下:

onViewDragStateChanged()
当ViewDragHelper状态发生变化时回调(IDLE,DRAGGING,SETTING[自动滚动时])

onViewPositionChanged
当captureview的位置发生改变时回调

onViewCaptured()
当captureview被捕获时回调

onEdgeTouched()
当触摸到边界时回调。

onEdgeLock()
true的时候会锁住当前的边界,false则unLock。

getOrderedChildIndex()
改变同一个坐标(x,y)去寻找captureView位置的方法。(具体在:findTopChildUnder方法中)

    原文作者:我是吸血鬼
    原文地址: https://www.jianshu.com/p/0372488cebcf
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞