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;
        }
    }

}

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


这里写图片描述

我们会发现:
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();
            }
        }

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


这里写图片描述

我们可以看出来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);
这里写图片描述

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

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

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

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

onViewCaptured()
当captureview被捕获时回调

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容