美文网首页
自定义RelativeLayout实现多级抽屉显示控件

自定义RelativeLayout实现多级抽屉显示控件

作者: 玖玖君 | 来源:发表于2023-10-16 13:50 被阅读0次

    实现左右上下抽屉效果,实现三级显示模式 随惯性拖动

    public class SlidingDrawerLayout extends RelativeLayout {
    
        public static final int DIRECTION_BOTTOM = 0;
        public static final int DIRECTION_TOP = 1;
        public static final int DIRECTION_LEFT = 2;
        public static final int DIRECTION_RIGHT = 3;
        private int mDirection = DIRECTION_BOTTOM;//上下左右四个方向抽屉
        private int mIsAllShow = 2;
        private ValueAnimator mAnim;
        private int mVisibleLength = 0;//隐藏后可视部分长度
        int lastY;//上一个事件Y坐标
        int lastX;//上一个事件Y坐标
        private View mBodyView;
        private GestureDetector mGestureDetector;
        private float downY;
        private float pivot;
    
        public SlidingDrawerLayout(Context context) {
            super(context);
        }
    
        public SlidingDrawerLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray a = context.obtainStyledAttributes(attrs,
                    R.styleable.SlidingDrawerLayout);
            mDirection = a.getInt(R.styleable.SlidingDrawerLayout_direction, DIRECTION_BOTTOM);
            mVisibleLength = (int) a.getDimension(R.styleable.SlidingDrawerLayout_visiableLength, 36);
        }
    
        public SlidingDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
    
        public void init() {
            mBodyView = getChildAt(0);
            mGestureDetector = new GestureDetector(getContext(), new MoreGestureListener());
            post(new Runnable() {
                @Override
                public void run() {
                    hide();//默认隐藏
                }
            });
        }
    
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            init();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            Log.d("SlidingDrawerLayout", "ev.action = " + ev.getAction());
            int distance = getDistance(ev);
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downY = ev.getY();
                    //判断落在子view上
                    if (!isInView(mBodyView, ev)) {
                        return super.onTouchEvent(ev);
                    }
                    cancleAnimator();
                    mGestureDetector.onTouchEvent(ev);
                    return true;
                case MotionEvent.ACTION_MOVE:
                    caulMargin(distance);
                    mGestureDetector.onTouchEvent(ev);//为什么不放在最前面,因为会比关闭/打开动画先触发
                    return true;
                case MotionEvent.ACTION_UP:
                    calAnimation(mIsAllShow);
                    mGestureDetector.onTouchEvent(ev);
                    return true;
                case MotionEvent.ACTION_CANCEL:
                    break;
            }
            return super.onTouchEvent(ev);
        }
    
        private int getDistance(MotionEvent ev) {
            int y = (int) ev.getRawY();
            int x = (int) ev.getRawX();
            int distance = 0;
            switch (mDirection) {
                case DIRECTION_BOTTOM:
                    distance = lastY - y;
                    break;
                case DIRECTION_TOP:
                    distance = y - lastY;
                    break;
                case DIRECTION_LEFT:
                    distance = x - lastX;
                    break;
                case DIRECTION_RIGHT:
                    distance = lastX - x;
                    break;
            }
            lastY = y;
            lastX = x;
            return distance;
        }
    
        /**
         * 动画的方法是否改变,高度/宽度 减去可以长度的一半为分界线
         *
         * @return
         */
        private boolean isAnimationDirectionChange() {
            switch (mDirection) {
                case DIRECTION_BOTTOM:
                case DIRECTION_TOP:
                    return getBodyMargin() > (getHeight() - mVisibleLength) / 2;
                case DIRECTION_LEFT:
                case DIRECTION_RIGHT:
                    return getBodyMargin() > (getWidth() - mVisibleLength) / 2;
            }
            return false;
        }
    
        private void calAnimation(int isShow) {
            switch (isShow) {
                case 0:
                    //全显
                    startAnimator(0);
                    break;
                case 1:
                    //一个item
                    startAnimator(220);
                    break;
                case 2:
                    //重置
                    startAnimator(getMaxLength());
                    break;
            }
        }
    
        private void caulMargin(int distance) {
            Log.d("SlidingDrawerLayout", "getBodyMrgin = " + getBodyMargin());
            Log.d("SlidingDrawerLayout", "distance = " + distance);
            int margin = getBodyMargin() - distance;
            if (isAllowMargin(margin)) {//拖动区域限制
                setBodyMargin(margin);
            }
        }
    
    
        /**
         * 收起或打开动画
         *
         * @param end 结束高度
         */
        public void startAnimator(final int end) {
    
            if (mAnim != null && mAnim.isRunning()) {
                mAnim.cancel();
            }
            mAnim = ValueAnimator.ofInt(getBodyMargin(), end);
            mAnim.setDuration(200);
            mAnim.setInterpolator(new AccelerateDecelerateInterpolator());
            mAnim.setTarget(this);
            mAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int currentValue = (Integer) animation.getAnimatedValue();
                    setBodyMargin(currentValue);
                }
            });
            mAnim.start();
    
        }
    
        public void cancleAnimator() {
            if (mAnim != null && mAnim.isRunning()) {
                mAnim.cancel();
            }
        }
    
    
        /**
         * 返回是否在允许范围内
         *
         * @param margin
         * @return
         */
        private boolean isAllowMargin(int margin) {
    
            return margin >= 0 && margin <= (getHeight() - mVisibleLength);
        }
    
        public int getBodyMargin() {
    
            LayoutParams paramsBody = (LayoutParams) mBodyView.getLayoutParams();
            int topMargin = paramsBody.topMargin;
    
            switch (mDirection) {
                case DIRECTION_BOTTOM:
                    topMargin = paramsBody.topMargin;
                    break;
                case DIRECTION_TOP:
                    topMargin = paramsBody.bottomMargin;
                    break;
                case DIRECTION_LEFT:
                    topMargin = paramsBody.rightMargin;
                    break;
                case DIRECTION_RIGHT:
                    topMargin = paramsBody.leftMargin;
                    break;
            }
            return topMargin;
        }
    
        public void setBodyMargin(int margin) {
            Log.d("SlidingDrawerLayout", "margin = " + margin);
            LayoutParams paramsBody = (LayoutParams) mBodyView.getLayoutParams();
            switch (mDirection) {
                case DIRECTION_BOTTOM:
                    paramsBody.topMargin = margin;
                    break;
                case DIRECTION_TOP:
                    paramsBody.bottomMargin = margin;
                    break;
                case DIRECTION_LEFT:
                    paramsBody.rightMargin = margin;
                    break;
                case DIRECTION_RIGHT:
                    paramsBody.leftMargin = margin;
                    break;
            }
            mBodyView.setLayoutParams(paramsBody);
        }
    
        public int getMaxLength() {
            switch (mDirection) {
                case DIRECTION_BOTTOM:
                case DIRECTION_TOP:
                    return getHeight() - mVisibleLength;
                default:
                    return getWidth() - mVisibleLength;
    
            }
        }
    
    
        class MoreGestureListener implements GestureDetector.OnGestureListener {
    
            @Override
            public boolean onDown(MotionEvent ev) {
                return false;
            }
    
            @Override
            public void onShowPress(MotionEvent e) {
            }
    
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                calAnimation(mIsAllShow);
                return false;
            }
    
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    
                return false;
            }
    
            @Override
            public void onLongPress(MotionEvent e) {
    
            }
    
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                //当头部显示并且向上fling时候关闭头部(惯性视觉)
                switch (mDirection) {
                    case DIRECTION_BOTTOM:
                        if (velocityY > 200 && velocityY < 800) {
                            changeType(false);
                        } else if (velocityY > 800) {
                            mIsAllShow = 2;
                        } else if (velocityY < -800) {
                            mIsAllShow = 0;
                        } else if (velocityY < -200 && velocityY > -800) {
                            changeType(true);
                        }
                        break;
                    case DIRECTION_TOP:
                        mIsAllShow = velocityY > 0 ? 0 : 1;
                        break;
                    case DIRECTION_LEFT:
                        mIsAllShow = velocityX > 0 ? 0 : 1;
                        break;
                    case DIRECTION_RIGHT:
                        mIsAllShow = velocityX < 0 ? 0 : 1;
                        break;
                }
                calAnimation(mIsAllShow);
                return true;
            }
        }
    
        private void changeType(boolean isTop) {
            if (isTop) {
                switch (mIsAllShow) {
                    case 1:
                        mIsAllShow = 0;
                        break;
                    case 2:
                        mIsAllShow = 1;
                        break;
                }
            } else {
                switch (mIsAllShow) {
                    case 0:
                        mIsAllShow = 1;
                        break;
                    case 1:
                        mIsAllShow = 2;
                        break;
                }
            }
        }
    
        /**
         * 设置可视长度/高度
         *
         * @param visibleLength
         */
        public void setVisibleLength(int visibleLength) {
            mVisibleLength = visibleLength;
        }
    
        /**
         * 关闭
         */
        public void hide() {
            mIsAllShow = 2;
            calAnimation(mIsAllShow);
        }
    
    
        /**
         * 判断触摸的点是否在view范围内
         */
        private boolean isInView(View v, MotionEvent event) {
            Rect frame = new Rect();
            v.getHitRect(frame);
            float eventX = event.getX();
            float eventY = event.getY();
            return frame.contains((int) eventX, (int) eventY);
        }
    }
    
    

    相关文章

      网友评论

          本文标题:自定义RelativeLayout实现多级抽屉显示控件

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