关于从边测划出控件

时间:2022-01-17 18:08:14
最近使用滴滴打车,因为要打印发票才发现原来滴滴打车还有个侧边栏,不过不能划开,需要点击打开。估计是因为和里面的地图冲突,所以才这么设计的。于是自己实现了个小的控件去进行了改善。主要思想就是监听滑动事件,如果是在边上则当前的view处理,如果超过了边缘则传递给子view去处理。后面又发现了viewDragerhelper这个东东,于是更简单了~
public class MyView extends ViewGroup {
    private ViewDragHelper mDragger;
    //被滑动的view
    private View mDragView;
    //边缘是否被触摸
    private boolean isEdgeTouched;
    //监听滑动速度
    private VelocityTracker vTracker = null;
    //是否打开
    private boolean isOpen;
    //自定义属性,可以打开的距离
    float cap;

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.MyView);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.MyView_cap:
                    //获得可以打开的距离,转化为px单位
                    cap = a.getDimensionPixelSize(attr, 250);
            }
        }
        a.recycle();
        mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {

            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                //只有边缘被触摸,才返回滑动的view
                if (isEdgeTouched)
                    return mDragView == child;
                else
                    return false;
            }

            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                final int leftBound = getPaddingLeft();
                return Math.min(Math.max(left, leftBound), getWidth() - (int) cap);
            }

            @Override
            public void onEdgeDragStarted(int edgeFlags, int pointerId) {
                isEdgeTouched = true;
                mDragger.captureChildView(mDragView, pointerId);
            }

            //手指释放的时候回调
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                if (releasedChild == mDragView) {
                    //如果正向滑动,没有打开并且速度大于每秒5,直接打开view                    if (xvel > 5 && !isOpen) {
                        //这里以自定义属性gap为打开的距离
                        mDragger.settleCapturedViewAt(getWidth() - (int) cap, getTop());
                        invalidate();
                        isOpen = true;
                        return;
                    }
                    //如果负向滑动,已经打开并且速度大于每秒5,直接关闭view                    if (xvel < -5 && isOpen) {
                        mDragger.settleCapturedViewAt(0, getTop());
                        isEdgeTouched = false;
                        invalidate();
                        isOpen = false;
                        return;
                    }
                    //判断滑动的距离是否超过最大距离的一半,有则打开
                    if (mDragView.getLeft() > (getWidth() - cap) / 2) {
                        mDragger.settleCapturedViewAt(getWidth() - (int) cap, getTop());
                        invalidate();
                        isOpen = true;
                        return;
                    }
                    //判断滑动的距离是否小于等于最大距离的一半,是则关闭
                    if (mDragView.getLeft() <= (getWidth() - cap) / 2) {
                        mDragger.settleCapturedViewAt(0, getTop());
                        isEdgeTouched = false;
                        invalidate();
                        isOpen = false;
                        return;
                    }
                }
            }
        });
        mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int count = this.getChildCount();

        for (int i = 0; i < count; i++) {
            View child = this.getChildAt(i);

            child.measure(widthMeasureSpec, heightMeasureSpec);

        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        for (int i = 0, size = getChildCount(); i < size; i++) {
            if (i == 0) {
                View view = getChildAt(i);
                view.layout(left, top, right - (int) cap, bottom);
            } else {
                View view = getChildAt(i);
                view.layout(left, top, right, bottom);
                mDragView = view;
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        //如果view打开了,则当前view处理滑动事件,也就是说view打开后可以通过手势滑回去,把关注点放在打开的view        if (isOpen) {
            return true;
        } else
            return mDragger.shouldInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (vTracker == null) {
            vTracker = VelocityTracker.obtain();
        } else {
            vTracker.clear();
        }
        //监听滑动速度,以每秒多少个像素为单位
        vTracker.addMovement(event);
        vTracker.computeCurrentVelocity(1000);//10001000毫秒
        mDragger.processTouchEvent(event);
        return true;
    }

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

}