现在的位置: 首页 > 综合 > 正文

android 高仿QQ 5.0侧拉效果

2018年05月21日 ⁄ 综合 ⁄ 共 8727字 ⁄ 字号 评论关闭

本来想快点网上搞一个炫一点的侧拉框架,思路来源

http://blog.csdn.net/lmj623565791/article/details/39257409


后来发现往项目里面加 根本不能用,用户体验较差。

本人改进了一下:

1. 菜单隐藏状态,左侧边缘开始拖动才能打开菜单。

2.菜单打开状态,右侧开始多动才能隐藏菜单。

3.菜单打开状态,点击右侧也能隐藏菜单。

4.打开和隐藏的边界修改的更人性化。

5.解决连续点击,屏幕快闪的问题。


核心代码如下:

public class SlidingMenu extends HorizontalScrollView {
    /** 屏幕宽度 */
    private int mScreenWidth;
    /** 右侧预留距离dp */
    private int mMenuRightPadding;
    /** 菜单的宽度 */
    private int mMenuWidth;
    /** 菜单的展开隐藏边界 */
    private int mSwtichMenuWidth;
    /** 在被判定为滚动之前用户手指可以移动的最大值。 */
    private int touchSlop;
    /** 标识 */
    private boolean down = false;
    /** 记录按下手指下去时候的X轴滑动起点坐标 */
    private int startScrollX;
    /** 记录按下手指下去时候的X轴坐标 */
    private int downX;
    /** 从屏幕边缘滑动[打开状态为右侧,隐藏状态为左侧] */
    private boolean dragFromEdge = false;
    /** 菜单状态[true打开状态,false隐藏状态] */
    private boolean isOpen = false;
    /** 滑动状态[true正在滑动,false完成滑动] */
    private boolean isAnim = false;
    /** 滑动开关[true开启,false禁止] */
    private boolean isAbleToSliding = true;
    /** 滑动动画时间 此时段内屏蔽SlidingMenu点击事件 */
    private static final int ANIM_TIME = 200;

    private boolean once;

    private ViewGroup mMenu;
    private ViewGroup mContent;

    Handler handler = new Handler();

    public OnSlidingFinishedListener listener;

    public interface OnSlidingFinishedListener {
        void onFinished(boolean isOpen);
    }

    public void setOnSlidingFinishedListener(OnSlidingFinishedListener listener) {
        this.listener = listener;
    }

    public SlidingMenu(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScreenWidth = ScreenUtils.getScreenWidth(context);
        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle,

        0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
            case R.styleable.SlidingMenu_rightPadding:
                // 默认50
                mMenuRightPadding = a.getDimensionPixelSize(attr,
                        (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, getResources().getDisplayMetrics()));
                break;
            }
        }
        a.recycle();
    }

    public SlidingMenu(Context context) {
        this(context, null, 0);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /** 显示的设置一个宽度 */
        if (!once) {
            LinearLayout wrapper = (LinearLayout) getChildAt(0);
            mMenu = (ViewGroup) wrapper.getChildAt(0);
            mContent = (ViewGroup) wrapper.getChildAt(1);

            mMenuWidth = mScreenWidth - mMenuRightPadding;
            mSwtichMenuWidth = mMenuWidth / 5;
            mMenu.getLayoutParams().width = mMenuWidth;
            mContent.getLayoutParams().width = mScreenWidth;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            // 将菜单隐藏
            this.scrollTo(mMenuWidth, 0);
            once = true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!isAnim && isAbleToSliding) {
            int action = ev.getAction();
            switch (action) {
            case MotionEvent.ACTION_DOWN:
                // LogUtils.d("SlidingMenu ACTION_DOWN");
                /**
                 * 记录手指按下时候的X轴坐标 , X轴滑动起点坐标
                 */
                downX = (int) ev.getRawX();
                startScrollX = getScrollX();
                down = true;

                if (isOpen && downX > mMenuWidth) {
                    // LogUtils.d("打开状态从右侧边缘[向左滑]");
                    dragFromEdge = true;
                } else if (!isOpen && downX < mScreenWidth / 5) {
                    // LogUtils.d("隐藏状态从左侧边缘[向右滑]");
                    dragFromEdge = true;
                } else {
                    // LogUtils.d("从中间不触发滑动");
                    dragFromEdge = false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                /**
                 * fix : 从屏幕左侧中间滑动不会触发 onTouchEvent ACTION_DOWN
                 * [原因被leftMenu事件截断]
                 */
                if (!down) {
                    downX = (int) ev.getRawX();
                    startScrollX = getScrollX();
                    down = true;

                    if (isOpen && downX > mMenuWidth) {
                        // LogUtils.d("打开状态从右侧边缘[向左滑]");
                        dragFromEdge = true;
                    } else if (!isOpen && downX < mScreenWidth / 5) {
                        // LogUtils.d("隐藏状态从左侧边缘[向右滑]");
                        dragFromEdge = true;
                    } else {
                        // LogUtils.d("从中间不触发滑动");
                        dragFromEdge = false;
                    }
                }

                if (!dragFromEdge) {
                    // LogUtils.d("从中间拖动不触发滑动");
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                LogUtils.d("SlidingMenu ACTION_UP");
                /**
                 * fix : 轻触不会触发 onTouchEvent ACTION_MOVE <br>
                 * [原因被轻触屏幕不会触发 ACTION_MOVE]
                 */
                if (!down) {
                    downX = (int) ev.getRawX();
                    startScrollX = getScrollX();
                    down = true;
                }

                int moveScrollX = startScrollX - getScrollX();

                int upX = (int) ev.getRawX();
                if (isOpen && downX < mMenuWidth && Math.abs(downX - upX) < touchSlop) {
                    LogUtils.d("直接点击左侧内容页");
                } else if (isOpen && downX >= mMenuWidth && Math.abs(downX - upX) < touchSlop) {
                    LogUtils.d("直接点击右侧内容页"); // 需要放在[想要隐藏 但是移动距离不够]之前判断
                    closeMenu();
                } else if (!isOpen && moveScrollX >= mSwtichMenuWidth) {
                    LogUtils.d("打开 ");
                    openMenu();
                } else if (!isOpen && moveScrollX < mSwtichMenuWidth) {
                    LogUtils.d("想要打开 但是移动距离不够 ");
                    closeMenu();
                } else if (!isOpen && moveScrollX < 0) {
                    LogUtils.d("隐藏状态,手指向左滑");
                    closeMenu();
                } else if (isOpen && -moveScrollX >= mSwtichMenuWidth) {
                    LogUtils.d("隐藏 ");
                    closeMenu();
                } else if (isOpen && -moveScrollX < mSwtichMenuWidth) {
                    LogUtils.d("想要隐藏 但是移动距离不够");
                    openMenu();
                } else if (isOpen && moveScrollX > 0) {
                    LogUtils.d("打开状态 手指向右滑");
                    openMenu();
                } else {
                    LogUtils.d("其他情况 还原"); // 一般不会到这里
                    if (isOpen) {
                        openMenu();
                    } else {
                        closeMenu();
                    }
                }

                /**
                 * fix : 重置按下事件标识
                 */
                down = false;

                return true;
            }
            return super.onTouchEvent(ev);
        } else {
            // 动画执行中
            return true;
        }

    }

    /**
     * 打开菜单
     */
    public void openMenu() {
        this.smoothScrollTo(0, 0);
        isOpen = true;
        if (listener != null) {
            listener.onFinished(isOpen);
        }
        letAnimPlay();
    }

    /**
     * 隐藏菜单
     */
    public void closeMenu() {
        this.smoothScrollTo(mMenuWidth, 0);
        isOpen = false;
        if (listener != null) {
            listener.onFinished(isOpen);
        }
        letAnimPlay();
    }

    /**
     * 切换菜单状态
     */
    public void toggle() {
        if (isOpen) {
            closeMenu();
        } else {
            openMenu();
        }
    }

    /**
     * 设置是否开启滑动功能
     */
    public void setAbleToSliding(boolean able) {
        isAbleToSliding = able;
    }

    private void letAnimPlay() {
        isAnim = true;
        handler.postDelayed(overAnimPlay, ANIM_TIME);
    }

    private Runnable overAnimPlay = new Runnable() {
        @Override
        public void run() {
            isAnim = false;
        }
    };

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float scale = l * 1.0f / mMenuWidth;
        float leftScale = 1 - 0.3f * scale;
        float rightScale = 0.8f + scale * 0.2f;

        ViewHelper.setScaleX(mMenu, leftScale);
        ViewHelper.setScaleY(mMenu, leftScale);
        ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * (1 - scale));
        ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.7f);

        ViewHelper.setPivotX(mContent, 0);
        ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
        ViewHelper.setScaleX(mContent, rightScale);
        ViewHelper.setScaleY(mContent, rightScale);
    }
}

关于改变动画执行速度。源码里面没有提供对外方法, 利用反射原理来修改速度。

public class SlidingMenuScroller extends OverScroller {
    private int mScrollDuration = 400; // 滑动速度

    /**
     * 设置速度速度
     *
     * @param duration
     */
    public void setScrollDuration(int duration) {
        this.mScrollDuration = duration;
    }

    public SlidingMenuScroller(Context context) {
        this(context, null);
    }

    public SlidingMenuScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, mScrollDuration);
    }

    public void initViewPagerScroll(HorizontalScrollView horizontalScrollView) {
        try {
            Field mScroller = HorizontalScrollView.class.getDeclaredField("mScroller");
            mScroller.setAccessible(true);
            mScroller.set(horizontalScrollView, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
slidingMenu = (SlidingMenu) findViewById(R.id.sliding_menu);
SlidingMenuScroller slidingMenuScroller = new SlidingMenuScroller(this);
slidingMenuScroller.initViewPagerScroll(slidingMenu);

到这里就差不多了


抱歉!评论已关闭.