美文网首页
Android 从 0 开始学习自定义 View(九) 仿 QQ

Android 从 0 开始学习自定义 View(九) 仿 QQ

作者: 是刘航啊 | 来源:发表于2021-05-21 17:08 被阅读0次
    效果图
    实现思路
    1. 继承 HorizontalScrollView 实现横向滚动效果
    2. 处理手势快速滑动控制菜单的显示和隐藏
    3. 处理内容点击关闭菜单
    4. 处理阴影
    5. 处理阴影透明度
    SlideMenu 处理横向滚动
    public class SlideMenu extends HorizontalScrollView {
        public SlideMenu(Context context) {
            this(context, null);
        }
    
        public SlideMenu(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initMenu(context, attrs);
        }
    
        private void initMenu(Context context, AttributeSet attrs) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlideMenu);
            float rightPadding = array.getDimension(R.styleable.SlideMenu_rightPadding, dp2px(50));
            mMenuWidth = (int) (getScreenWidth() - rightPadding);
            array.recycle();
            //初始化手势
            mGestureDetector = new GestureDetector(context, new GestureListener());
        }
    }
    
    GestureListener 处理手势快速滑动
    //手势处理
    private class GestureListener extends GestureDetector.SimpleOnGestureListener {
    
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (mMenuIsOpen) {
                //当往左滑动时关闭菜单
                if (velocityX < 0) {
                    closeMenu();
                    return true;
                }
            } else {
                //当往右滑动时打开菜单
                if (velocityX > 0) {
                    openMenu();
                    return true;
                }
            }
            return super.onFling(e1, e2, velocityX, velocityY);
        }
    }
    
    onTouchEvent 处理内容点击
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //如果点击被拦截,就不触发之后的操作
            if (mIsIntercept) {
                return true;
            }
    
            //触发快速滑动,下面就不要执行
            if (mGestureDetector.onTouchEvent(ev)) {
                return true;
            }
    
            switch (ev.getAction()) {
                case MotionEvent.ACTION_UP:
                    // 手指抬起获取滚动的位置
                    int currentScrollX = getScrollX();
                    if (currentScrollX > mMenuWidth / 2) {
                        // 关闭菜单
                        closeMenu();
                    } else {
                        // 打开菜单
                        openMenu();
                    }
                    return false;
            }
    
            return super.onTouchEvent(ev);
    }
    
    onFinishInflate 处理阴影
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //获取最外层布局
        ViewGroup container = (ViewGroup) this.getChildAt(0);
        //统计子布局的数量
        int containerChildCount = container.getChildCount();
        if (containerChildCount > 2) {
            throw new IllegalStateException("SlideMenu ChildView More Than 2");
        }
        //获取菜单布局
        mMenuView = container.getChildAt(0);
        //菜单宽度 = 屏幕宽度 - 右边 Padding
        mMenuView.getLayoutParams().width = mMenuWidth;
        //获取内容布局
        mContentView = container.getChildAt(1);
        //获取 LayoutParams
        ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
        //移除原来内容布局
        container.removeView(mContentView);
        //创建新的 RelativeLayout
        RelativeLayout contentView = new RelativeLayout(getContext());
        //创建阴影布局
        mShadowView = new View(getContext());
        //设置阴影颜色
        mShadowView.setBackgroundColor(Color.parseColor("#80000000"));
        //设置透明度
        mShadowView.setAlpha(0.0f);
        //将内部加入布局
        contentView.addView(mContentView);
        //将阴影加入布局
        contentView.addView(mShadowView);
        //内容宽度 = 屏幕宽度
        layoutParams.width = getScreenWidth();
        //设置 layoutParams
        contentView.setLayoutParams(layoutParams);
        container.addView(contentView);
    }
    

    处理阴影的大致原理可分成 5 个步骤 :

    1. 拿到内容布局
    2. 创建一个新的 RelativeLayout
    3. 创建一个阴影布局
    4. 将内容布局与阴影布局添加到 RelativeLayout
    5. 将 RelativeLayout 添加到原来的位置
    onScrollChanged 处理阴影透明度
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float scale = 1f * l / mMenuWidth;
        float alphaScale = 1 - scale;
        mShadowView.setAlpha(alphaScale);
    }
    

    根据菜单的宽度和坐标计算偏移量

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

    Github 源码链接

    相关文章

      网友评论

          本文标题:Android 从 0 开始学习自定义 View(九) 仿 QQ

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