ViewDragHelper基本方法讲解

package com.adnonstop.draghelper.viewgroups;

import android.content.Context;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;

/**
 * Created by gzq on 2017/1/19.
 */

public class DragViewGroup extends FrameLayout {

    private ViewDragHelper dragHelper;
    private View mDragView;
    private View mAutoBackView;
    private View mEdgeTrackerView;
    private int mAutoBackViewOriginalPos_x;
    private int mAutoBackViewOriginalPos_y;

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

    public DragViewGroup(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        dragHelper = ViewDragHelper.create(this, new DragCallback());
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                initChild();
            }
        });
        //拖拽左边界回调
        dragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
    }

    /**
     * 在viewGroup绘制初始化过程中,要一个一个添加子view,有多少个子view,就会调用多少次onViewAdded;
     * 可以在次做一些view类型判断的工作。
     *
     * @param child
     */
    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
//        if (!(child instanceof ViewGroup))
//            throw new RuntimeException("the child most be a viewgroup!");
    }

    /**
     * 获取到子view。
     */
    private void initChild() {
        for (int index = 0; index < getChildCount(); index++) {
            View child = getChildAt(index);
        }
    }

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

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

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mAutoBackViewOriginalPos_x = mAutoBackView.getLeft();
        mAutoBackViewOriginalPos_y = mAutoBackView.getTop();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mDragView = getChildAt(0);
        mAutoBackView = getChildAt(1);
        mEdgeTrackerView = getChildAt(2);
    }

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

    private class DragCallback extends ViewDragHelper.Callback {
        /**
         * tryCaptureView 返回ture则表示可以捕获该view,你可以根据传入的第一个参数view决定哪些可以捕获
         *
         * @param child
         * @param pointerId
         * @return
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }

        /**
         * @param child
         * @param left  对移动的边界进行控制
         * @param dx
         * @return
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            int newLeft = Math.max(0, Math.min(left, getWidth() - child.getWidth()));
            return newLeft;
        }

        /**
         * @param child
         * @param top   控制移动的边界
         * @param dy
         * @return
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            int newTop = Math.max(0, Math.min(top, getHeight() - child.getHeight()));
            return newTop;
        }

        /**
         * @param releasedChild
         * @param xvel          松手时沿X轴的速度
         * @param yvel          松手时沿Y轴的速度
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (releasedChild == mAutoBackView) {
                dragHelper.settleCapturedViewAt(mAutoBackViewOriginalPos_x, mAutoBackViewOriginalPos_y);
                invalidate();
            }
        }

        /**
         * @param changedView
         * @param left        new left
         * @param top         new top
         * @param dx
         * @param dy
         */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }

        //        See also:
        //                STATE_IDLE 静止
        //                STATE_DRAGGING  拖拽中
        //                STATE_SETTLING 自动滚动
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        /*
        * 边界拖动回调:
        * 在viewGroup的左边界拖动时的回调。
        * */
        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            super.onEdgeDragStarted(edgeFlags, pointerId);
            System.out.println("******边界拖动回调**********");
        }
    }
}


点击打开链接



第一个View基本没做任何修改。

第二个View,我们在onLayout之后保存了最开启的位置信息,最主要还是重写了Callback中的onViewReleased,我们在onViewReleased中判断如果是mAutoBackView则调用settleCapturedViewAt回到初始的位置。大家可以看到紧随其后的代码是invalidate();因为其内部使用的是mScroller.startScroll,所以别忘了需要invalidate()以及结合computeScroll方法一起。

第三个View,我们在onEdgeDragStarted回调方法中,主动通过captureChildView对其进行捕获,该方法可以绕过tryCaptureView,所以我们的tryCaptureView虽然并为返回true,但却不影响。注意如果需要使用边界检测需要添加上mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);


到此,我们已经介绍了Callback中常用的回调方法了,当然还有一些方法没有介绍,接下来我们修改下我们的布局文件,我们把我们的TextView全部加上clickable=true,意思就是子View可以消耗事件。再次运行,你会发现本来可以拖动的View不动了,(如果有拿Button测试的兄弟应该已经发现这个问题了,我希望你看到这了,而不是已经提问了,哈~)。

原因是什么呢?主要是因为,如果子View不消耗事件,那么整个手势(DOWN-MOVE*-UP)都是直接进入onTouchEvent,在onTouchEvent的DOWN的时候就确定了captureView。如果消耗事件,那么就会先走onInterceptTouchEvent方法,判断是否可以捕获,而在判断的过程中会去判断另外两个回调的方法:getViewHorizontalDragRangegetViewVerticalDragRange,只有这两个方法返回大于0的值才能正常的捕获。

所以,如果你用Button测试,或者给TextView添加了clickable = true ,都记得重写下面这两个方法:

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

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值