美文网首页待写UI
Android 从 0 开始学习自定义 View(十) 可拖动回

Android 从 0 开始学习自定义 View(十) 可拖动回

作者: 是刘航啊 | 来源:发表于2021-05-31 11:55 被阅读0次

    效果图

    效果描述:当滑动距离\color{red}{小于}设定的距离( 默认后背景内容一半 )时会\color{red}{回弹到顶部}。当滑动的距离\color{red}{大于}设定的距离时会\color{red}{显示背景内容}。当列表向上滑动时,如果背景是\color{red}{展开状态}会先将背景关闭,之后再处理列表的滑动。

    效果实现分析

    1. 处理 ViewDragHelper
    2. 处理拖动以及拖动的范围
    3. 处理回弹效果
    4. 滑动冲突

    1、处理 ViewDragHelper

    public class VerticalDragView extends FrameLayout {
        
        private ViewDragHelper mDragHelper;
        
        public VerticalDragView(@NonNull Context context) {
            this(context, null);
        }
    
        public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mDragHelper = ViewDragHelper.create(this, mCallback);
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mDragHelper.processTouchEvent(event);
            return true;
        }
        
        private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(@NonNull View child, int pointerId) {
                return true;
            }
    
            @Override
            public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
                return top;
            }
    
            @Override
            public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
                return left;
            }
    
            @Override
            public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
    
            }
        };
    }
    

    这里只是创建了 ViewDragHelper,并没有做什么特殊处理,接下来处理可以拖动的 View 以及拖动的范围。

    2、处理拖动以及拖动的范围

    public class VerticalDragView extends FrameLayout {
        
        private ViewDragHelper mDragHelper;
        //可以拖动的View
        private View mDragView;
        //拖动范围
        int menuHeight = 0;
        private boolean mMenuIsOpen = false;
        
        public VerticalDragView(@NonNull Context context) {
            this(context, null);
        }
    
        public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mDragHelper = ViewDragHelper.create(this, mCallback);
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mDragHelper.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);
            if (changed) {
                menuHeight = getChildAt(0).getMeasuredHeight();
            }
        }
        
        private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(@NonNull View child, int pointerId) {
                return  return child == mDragView;
            }
    
            @Override
            public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
                //垂直滑动移动的位置
                if (top <= 0) {
                    top = 0;
                }
    
                if (top >= menuHeight) {
                    top = menuHeight;
                }
                return top;
            }
    
            @Override
            public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
    
            }
        };
    }
    

    需要注意的是 View 的高度在 onMeasere 才会被计算,如果直接获取是拿不到高度的。我是在 onLayout 获取高度,onMeasure 也可以,看个人选择。

    3、处理回弹效果

    public class VerticalDragView extends FrameLayout {
        
        private ViewDragHelper mDragHelper;
        //可以拖动的View
        private View mDragView;
        //拖动范围
        int menuHeight = 0;
        
        public VerticalDragView(@NonNull Context context) {
            this(context, null);
        }
    
        public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mDragHelper = ViewDragHelper.create(this, mCallback);
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mDragHelper.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);
            if (changed) {
                menuHeight = getChildAt(0).getMeasuredHeight();
            }
        }
        
        private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(@NonNull View child, int pointerId) {
                return  return child == mDragView;
            }
    
            @Override
            public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
                //垂直滑动移动的位置
                if (top <= 0) {
                    top = 0;
                }
    
                if (top >= menuHeight) {
                    top = menuHeight;
                }
                return top;
            }
    
            @Override
            public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
                if (mDragView.getTop() > menuHeight / 2) {
                    //滚动到菜单的高度(打开)
                    mDragHelper.settleCapturedViewAt(0, menuHeight);
                    mMenuIsOpen = true;
                } else {
                    //滚动到0(关闭)
                    mDragHelper.settleCapturedViewAt(0, 0);
                    mMenuIsOpen = false;
                }
                invalidate();
            }
        };
        
        @Override
        public void computeScroll() {
            if (mDragHelper.continueSettling(true)) {
                invalidate();
            }
        }
    }
    

    4、滑动冲突

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
    
            if (mMenuIsOpen) {
                return true;
            }
    
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownY = ev.getY();
                    mDragHelper.processTouchEvent(ev);
                    break;
                case MotionEvent.ACTION_MOVE:
                    float offSetY = ev.getY() - mDownY;
                    if (offSetY > 0 && !canChildScrollUp()) {
                        return true;
                    }
                    break;
            }
    
            return super.onInterceptTouchEvent(ev);
        }
    
        /**
         * 判断是否滚动到顶部
         *
         * @return
         */
        public boolean canChildScrollUp() {
    
            if (mDragView instanceof ListView) {
                return ListViewCompat.canScrollList((ListView) mDragView, -1);
            }
            return mDragView.canScrollVertically(-1);
        }
    

    滑动冲突主要是判断菜单的开合状态、滑动的状态( 向上滑动|向下划动 )、列表是否滑动到头部

    基本的代码都在上面,自定义仿可拖动回弹布局就介绍到这里了,如果有什么写得不对的,可以在下方评论留言,我会第一时间改正。

    Github 源码链接

    相关文章

      网友评论

        本文标题:Android 从 0 开始学习自定义 View(十) 可拖动回

        本文链接:https://www.haomeiwen.com/subject/sgkssltx.html