2017-05-17-Android拖动view实现

一、拖动实现的原理

实现拖拽view,在Android中至少可以俩种实现方案:
1.可以直接去拖动view
2,利用viewgroup获取子view利用viewgroup移动子view的位置
Android推介我们使用的是第二种方法。
原因是view本身是放在ViewGroup中的,因此无论view如何拖动都不能超出ViewGroup的范围

二、实现的方式

1.自定义ViewGroup,继承ViewGroup,当然也可以继承其他布局Layout来实现

2.初始化Android拖拽处理类ViewDragHelper,并传入ViewDragHelper.Callback

3.重写ViewGroup的onFinishInflate方法,获取需要拖拽的View的ID和View。
如果需要拖拽之后会到原来的位置,则需要记录此时View的位置

4,重写ViewGroup的onInterceptTouchEvent(是否在当前ViewGroup处理Touch事件)和onTouchEvent(具体处理touch事件的方法) 5.实现ViewDragHelper.Callback中的方法。
指定哪些view可以被拖动,控制view拖拽的边界 拖拽完成等等方法
6.可以通过onLayout方法获取ViewGroup的真实宽和高
7.如果需要实现在拖拽完成之后,平滑的移动到另一个位置调用mViewDragHelper.settleCapturedViewAt(left, top)
同时需要重写ViewGroup的computeScroll()方法刷新viewGroup

以上是实现的简单逻辑:

三、代码
public class MyDragLayout extends RelativeLayout {

    protected ViewDragHelper mViewDragHelper;
    private final Point mPoint = new Point();
    
    public MyDragLayout(Context context) {
        super(context);
        init();
    }
    
    public MyDragLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    
    public MyDragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    
    private void init() {
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, new mViewGragCallback());
        mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
    }
    
    private int mDragViewId;
    private View mView;
    
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mDragViewId = R.id.drag_id;
        mView = findViewById(mDragViewId);
        mPoint.x = mView.getLeft();
        mPoint.y = mView.getTop();
    }
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mViewDragHelper.cancel();
            return false;
        }
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
    
    private final class mViewGragCallback extends ViewDragHelper.Callback {
    
        public mViewGragCallback() {
        }
    
        @Override
        public boolean tryCaptureView(final View child, int pointerId) {
            return child.getId() == mDragViewId;
        }
    
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //取得左边界的坐标
            final int leftBound = getPaddingLeft();
            //取得右边界的坐标
            final int rightBound = getWidth() - child.getWidth() - leftBound;
            //这个地方的含义就是 如果left的值 在leftbound和rightBound之间 那么就返回left
            //如果left的值 比 leftbound还要小 那么就说明 超过了左边界 那我们只能返回给他左边界的值
            //如果left的值 比rightbound还要大 那么就说明 超过了右边界,那我们只能返回给他右边界的值
            return Math.min(Math.max(left, leftBound), rightBound);
        }
    
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int topBound = getPaddingTop();
            final int bottomBound = getHeight() - child.getHeight() - topBound;
            return Math.min(Math.max(top, topBound), bottomBound);
        }
    
    
        @Override
        public int getViewHorizontalDragRange(View child) {
            if(child.getId() == mDragViewId) {
                return mWidth;
            }
            return 0;
        }
    
        @Override
        public int getViewVerticalDragRange(View child) {
            if(child.getId() == mDragViewId) {
                return mHeight;
            }
            return 0;
        }
    
        /*@Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            super.onEdgeDragStarted(edgeFlags, pointerId);
            //在onEdgeDragStarted回调方法中,主动通过captureChildView对其进行捕获,该方法可以绕过tryCaptureView,所以我们的tryCaptureView虽然并为返回true,但却不影响
            //注意如果需要使用边界检测需要添加上mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);。
            mViewDragHelper.captureChildView(mView, pointerId);
        }*/
    
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            onDragFinish(releasedChild, mDragViewId, mView);
        }
    }
    
    /**
     * 如果需要定制拖动view之后的位置,则需要重写此方法
     * @param releasedChild
     * @param dragId
     * @param dragView
     */
    protected void onDragFinish(View releasedChild, int dragId, View dragView) {
        //super.onViewReleased(releasedChild, xvel, yvel);//松手的时候 判断如果是这个view 就让他靠边停靠
        if(releasedChild.getId() == mDragViewId) {
            //这边代码你跟进去去看会发现最终调用的是startScroll这个方法 所以我们就明白还要在computeScroll方法里刷新
            // 拖动距离大于屏幕的一半右移,拖动距离小于屏幕的一半左移
            int left = releasedChild.getLeft();
            int top = releasedChild.getTop();
            int childWidth = releasedChild.getWidth();
            if (left + childWidth / 2 > mWidth / 2) {
                left = mWidth - childWidth;
    
            } else {
                left = 0;
            }
            mViewDragHelper.settleCapturedViewAt(left, top);
            invalidate();
            LayoutParams params = (LayoutParams) mView.getLayoutParams();
            params.addRule(ALIGN_PARENT_RIGHT, 0);
            params.addRule(ALIGN_PARENT_TOP, 0);
            params.addRule(BELOW, 0);
            params.leftMargin = left;
            params.topMargin = top;
            mView.setLayoutParams(params);
        }
    };
    
    protected int mWidth = -1;
    protected int mHeight = -1;
    
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (mWidth == -1 || mHeight == -1) {
            mWidth = r - l;
            mHeight = b - t;
        }
    }
    
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            invalidate();
        }
    }
}

你可能感兴趣的:(2017-05-17-Android拖动view实现)