三级侧滑菜单

这里写图片描述

侧滑菜单仿照阿里巴巴国际版的菜单,做的时候老是动画很卡,最近被一个同事搞出来,主要是用到了viewDragHelper这个类,

具体可以看部分代码

package com.csc_app.view;

import android.content.Context;
import android.content.res.Resources;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.csc_app.util.LogUtil;
import com.csc_app.util.UIUtils;


/**
 * Created by date on 2016/3/28.
 *
 * @author WuYinRong
 */
public class SeekProductMenu extends ViewGroup {
    private static final String TAG = "SeekProductMenu";
    private ViewDragHelper viewDragHelper;
    private FrameLayout twoMenuView;
    private FrameLayout threeMenuView;
    private int creenWidth;
    private int creenHeight;
    private FrameLayout oneMenuView;
    private ListView oneMenuList;
    private ListView twoMenuList;
    private int twoMenuViewWidth;
    private boolean twoMenuIsOpen = false;//给二级菜单的开关标志
    private boolean threeMenuIsOpen = false;//给二级菜单的开关标志
    private int threeMenuViewWidth;
    private ListView threeMenuList;
    private int twoMenuViewLeft;//二级菜单距离屏幕左边的距离
    private int threeMenuViewLeft;//三级菜单距离屏幕左边的距离
    private int dx;//down事件时的坐标
    //二级和三级菜单的开关状态
    private static final int ALL_CLOSE = 0;//二级菜单和三级菜单都处于关闭状态
    private static final int TWO_OPEN = 1;//只有二级菜单打开
    private static final int TWO_THREE_OPEN = 2;//二级菜单和三级菜单都打开是的状态
    private int nowState = ALL_CLOSE;//当前处于的状态 //3
    private int threeMenuLeftAxis;
    private int twoMenuLeftAxis;

    private ViewDragHelper viewDragHelper2;
    private int downY;

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

    public SeekProductMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 获取 viewDragHelper实例
        viewDragHelper = ViewDragHelper.create(this, new MyCallBack());
        viewDragHelper2 = ViewDragHelper.create(this, new MyCallBack());
        //获取屏幕宽高
        int[] creenSize = getScreenSize();
        //宽
        creenWidth = creenSize[0];
        //高
        creenHeight = creenSize[1];


    }

    /**
     * 该方法在布局文件加载完毕后被回调
     */
    @Override
    protected void onFinishInflate() {
        //获取一级菜单实例
        oneMenuView = (FrameLayout) getChildAt(0);
        oneMenuList = (ListView) oneMenuView.getChildAt(0);
        //获取二级菜单实例
        twoMenuView = (FrameLayout) getChildAt(1);
        twoMenuList = (ListView) twoMenuView.getChildAt(0);
        //获取三级菜单实例
        threeMenuView = (FrameLayout) getChildAt(2);
        threeMenuList = (ListView) threeMenuView.getChildAt(0);


    }

    /**
     * 测量子视图
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        //测量一级菜单
        oneMenuView.measure(widthMeasureSpec, heightMeasureSpec);

        //测量二级菜单
        //设置二级菜单为屏幕的4/5宽
        twoMenuViewWidth = (int) (creenWidth * 0.8 + 0.5) + 20;
        int twoMenuViewWidthSpec = MeasureSpec.makeMeasureSpec(twoMenuViewWidth, MeasureSpec.EXACTLY);
        twoMenuView.measure(twoMenuViewWidthSpec, heightMeasureSpec);

        //测量三级菜单
        //设置三级菜单为屏幕的3/5
        threeMenuViewWidth = (int) (creenWidth * 0.55 + 0.5)+30;
        int threeMenuViewWidthSpec = MeasureSpec.makeMeasureSpec(threeMenuViewWidth, MeasureSpec.EXACTLY);
        threeMenuView.measure(threeMenuViewWidthSpec, heightMeasureSpec);

        //设置视图本身
        setMeasuredDimension(creenWidth, creenHeight);
    }

    /**
     * 给子视图布局
     *
     * @param b
     * @param i
     * @param i1
     * @param i2
     * @param i3
     */
    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        //给一级菜单布局
        oneMenuView.layout(0, 0, creenWidth, creenHeight);

        //给二级菜单布局
        if (nowState != TWO_OPEN&&nowState != TWO_THREE_OPEN) {
            twoMenuView.layout(creenWidth, 0, creenWidth + twoMenuViewWidth, creenHeight);
        } else {
            LogUtil.Error("Test","二级菜单布局");
            twoMenuView.layout((int) (creenWidth * 0.2 + 0.5) - 20, 0, creenWidth, creenHeight);
        }

        if(nowState != TWO_THREE_OPEN){
            //给三级菜单布局
            threeMenuView.layout(creenWidth, 0, creenWidth + threeMenuViewWidth, creenHeight);
        }else {
            threeMenuView.layout((int) (creenWidth * 0.45 + 0.5), 0, creenWidth , creenHeight);
        }

    }

    /**
     * 重写onInterceptTouchEvent自定义事件拦截规则
     *
     * @param ev
     * @return
     */
    int downX = 0;

    /**
     * 自定义事件分发
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = dx = (int) (ev.getX() + 0.5f);
                downY = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:

                //判断当用户左右滑动的时候拦截子视图事件(也就是当用户左右滑动的时候拦截ListView的事件走自己的事件)
                //上下滑动或者点击的时候走ListView的事件
                int moveY = (int) ev.getY();
                int moveX = (int) (ev.getX() + 0.5f);
                int moveDistanceX = (downX - moveX) > 0 ? (downX - moveX) : (moveX - downX);
                int moveDistanceY = (downY - moveY) > 0 ? (downY - moveY) : (moveY - downY);

                //当上下滑动时,不拦截事件,走ListView的事件
                if (moveDistanceY > 20) {
                    return false;
                }
//当左右滑动的时候,拦截子控件事件,也就是拦截ListView的事件,走自己的左右滑动事件
                if (moveDistanceX > 10 && moveDistanceY < 20) {
                    downX = (int) (ev.getX() + 0.5f);
                    return true;
                }
                break;
        }
        return false;
    }

    /**
     * 重写onTouchEvent自定义滑动事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                View view = getTouchView(event, downX); //获取被触摸到的View
                slideEvent(event, view); //处理被触摸到的View的滑动事件
                break;
            case MotionEvent.ACTION_UP:

                switch (nowState) {
                    case TWO_THREE_OPEN:
                        /**
                         * 当二级菜单和三级菜单都处于打开状态时的事件处理
                         */
                        if (getTouchView(event, downX) == threeMenuView) {//当二级菜单和三级菜单都处于打开状态,并且手指触摸到的是三级菜单时的事件处理
                            if (threeMenuViewLeft < creenWidth / 2 + UIUtils.px2dip(50)) {
                                viewDragHelper.smoothSlideViewTo(threeMenuView, (int) (creenWidth * 0.45 + 0.5), 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                threeMenuViewLeft = (int) (creenWidth * 0.45 + 0.5);
                                threeMenuIsOpen = true;
                                nowState = TWO_THREE_OPEN;
                            } else {
                                viewDragHelper.smoothSlideViewTo(threeMenuView, creenWidth, 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                threeMenuIsOpen = false;
                                nowState = TWO_OPEN;
                                threeMenuViewLeft = creenWidth;

                                //接口回调,监听菜单被关闭时,恢复菜单item默认背景颜色
                                //  //恢复二级菜单item默认背景颜色
                                if(recoverListViewItemBackground!=null){
                                    recoverListViewItemBackground.onRecoverTwoListViewItemBackgroundListener();
                                }

                            }
                        } else if (getTouchView(event, downX) == twoMenuView) {

                            //当二级菜单和三级菜单都处于打开状态,并且手指触摸到的是二级菜单时的事件处理

                            if (twoMenuViewLeft < creenWidth / 2) {
                                viewDragHelper.smoothSlideViewTo(twoMenuView, (int) (creenWidth * 0.2 + 0.5) - 20, 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                viewDragHelper2.smoothSlideViewTo(threeMenuView, (int) (creenWidth * 0.45 + 0.5), 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                twoMenuViewLeft = (int) (creenWidth * 0.2 + 0.5);
                                threeMenuViewLeft = (int) (creenWidth * 0.45 + 0.5);
                                threeMenuIsOpen = true;
                                twoMenuIsOpen = true;
                                nowState = TWO_THREE_OPEN;
                            } else {
                                viewDragHelper.smoothSlideViewTo(twoMenuView, creenWidth, 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                viewDragHelper2.smoothSlideViewTo(threeMenuView, creenWidth +UIUtils.px2dip(300), 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                twoMenuViewLeft = creenWidth;
                                threeMenuViewLeft = creenWidth;
                                threeMenuIsOpen = false;
                                twoMenuIsOpen = false;
                                nowState = ALL_CLOSE;

                                //接口回调,监听菜单被关闭时,恢复菜单item默认背景颜色
                                //恢复一级菜单item默认背景颜色
                                if(recoverListViewItemBackground!=null){
                                    recoverListViewItemBackground.onRecoverOneListViewItemBackgroundListener();
                                }
                            }
                        }
                        break;
                    case TWO_OPEN:
                        /**
                         * 当只有二级菜单处于打开状态并且三级菜单处于关闭状态时的事件处理
                         */
                        if (getTouchView(event, downX) == twoMenuView) { //当手指触摸到的是二级菜单时的事件处理
                            if (twoMenuViewLeft < creenWidth / 2) {
                                viewDragHelper.smoothSlideViewTo(twoMenuView, (int) (creenWidth * 0.2 + 0.5)-20, 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                twoMenuViewLeft = (int) (creenWidth * 0.2 + 0.5);
                                twoMenuIsOpen = true;
                                nowState = TWO_OPEN;
                            } else {
                                viewDragHelper.smoothSlideViewTo(twoMenuView, (int) creenWidth, 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                viewDragHelper2.smoothSlideViewTo(threeMenuView, (int) creenWidth + UIUtils.dip2px(300), 0);
                                ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
                                twoMenuViewLeft = creenWidth;
                                twoMenuIsOpen = false;
                                nowState = ALL_CLOSE;

                                //接口回调,监听菜单被关闭时,恢复菜单item默认背景颜色
                                //恢复一级菜单item默认背景颜色

                                LogUtil.Error("Test","recoverListViewItemBackground="+recoverListViewItemBackground);
                                if(recoverListViewItemBackground!=null){
                                    recoverListViewItemBackground.onRecoverOneListViewItemBackgroundListener();
                                }
                            }
                        }
                }
        }
        return super.onTouchEvent(event);
    }


    /**
     * 滑动事件处理
     *
     * @param ev    MotionEvent
     * @param child 被触摸到的View
     * @return 返回被触摸到的View距离屏幕左边的距离
     */
    public int slideEvent(MotionEvent ev, View child) {

        int moveX = (int) (ev.getX() + 0.5);//滑动后的X坐标
        int slideDistance = downX - moveX;//滑动的距离
        //如果被触摸到的View是二级菜单并且只有二级菜单处于打开状态时
        if (child == twoMenuView && nowState == TWO_OPEN) {

            //计算二级菜单移动的位置
            twoMenuLeftAxis = twoMenuViewLeft - slideDistance;
            //边界限定
            twoMenuLeftAxis = Math.max((int) (creenWidth * 0.2 + 0.5)-20, twoMenuLeftAxis);

            //重新布局
            twoMenuView.layout(twoMenuLeftAxis, 0, creenWidth + twoMenuLeftAxis, creenHeight);
            downX = (int) (ev.getX() + 0.5f);
            twoMenuViewLeft = twoMenuLeftAxis;
            ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
            return twoMenuViewLeft;
        } else if (child == twoMenuView && nowState == TWO_THREE_OPEN) {
            /**
             * 这种情况是当手指触摸到二级菜单并且三级菜单也是打开状态时
             */
            //计算二级菜单移动的位置
            int twoMenuLeftAxis = twoMenuViewLeft - slideDistance;
            //计算三级菜单到屏幕左边的距离
            threeMenuLeftAxis = threeMenuViewLeft - slideDistance;
            //二级菜单边界限定
            twoMenuLeftAxis = Math.max((int) (creenWidth * 0.2 + 0.5)-20, twoMenuLeftAxis);
            //三级菜单边界限定
            threeMenuLeftAxis = Math.max((int) (creenWidth * 0.45 + 0.5), threeMenuLeftAxis);
            //重新布局
            twoMenuView.layout(twoMenuLeftAxis, 0, creenWidth + twoMenuLeftAxis, creenHeight);
            threeMenuView.layout(threeMenuLeftAxis, 0, creenWidth + threeMenuLeftAxis, creenHeight);
            downX = (int) (ev.getX() + 0.5f);
            twoMenuViewLeft = twoMenuLeftAxis;
            threeMenuViewLeft = threeMenuLeftAxis;

        } else if (child == threeMenuView) {
            threeMenuLeftAxis = threeMenuViewLeft - slideDistance;
            //三级菜单边界限定
            threeMenuLeftAxis = Math.max((int) (creenWidth * 0.45 + 0.5), threeMenuLeftAxis);
            threeMenuView.layout(threeMenuViewLeft - slideDistance, 0, creenWidth + threeMenuLeftAxis, creenHeight);
            downX = (int) (ev.getX() + 0.5f);
            threeMenuViewLeft = threeMenuLeftAxis;
        }
        return 0;
    }


    /**
     * 获取当手指按下时触摸到的子视图,用于用户手指按下触摸到的是那个菜单
     * 当在down事件时调用该方法
     *
     * @param ev
     * @param dx
     * @return
     */
    public View getTouchView(MotionEvent ev, int dx) {
        if (!twoMenuIsOpen && !threeMenuIsOpen) {
            //当二级菜单和三级菜单都处于关闭状态时
            nowState = ALL_CLOSE;
            return oneMenuView;
        } else if (twoMenuIsOpen && !threeMenuIsOpen) {
            //当二级菜单处于打开并且三级菜单处于关闭时
            nowState = TWO_OPEN;
            return dx < twoMenuViewLeft ? oneMenuView : twoMenuView;
        } else if (twoMenuIsOpen && threeMenuIsOpen) {
            nowState = TWO_THREE_OPEN;
            if (dx < twoMenuViewLeft) {
                return oneMenuView;
            } else if (dx > twoMenuViewLeft && threeMenuViewLeft > dx) {
                return twoMenuView;
            } else if (dx > threeMenuViewLeft) {
                return threeMenuView;
            }
        }
        return null;
    }


    /**
     * 获取手机屏幕宽高
     *
     * @return
     */
    public int[] getScreenSize() {
        Resources resources = this.getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        int width = dm.widthPixels;//获得的是PX 需转换为dp
        int height = dm.heightPixels;
        int[] ScreenSize = {width, height};
        return ScreenSize;
    }

    @Override
    public void computeScroll() {
        //是否固定
        if (viewDragHelper.continueSettling(true)) {
            //该方法作用和invalidate()一样,但是用invalidate()方法有的机型没有效果,所以建议使用以下方法
            ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
        }
        if (viewDragHelper2.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
        }
    }

    /**
     * 当二级菜单和三级菜单都关闭时,点击listView item 相应的事件
     */

    public void OnClickOneMenu() {
        if (nowState == TWO_THREE_OPEN) {
            viewDragHelper.smoothSlideViewTo(threeMenuView, creenWidth +10, 0);
            ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
            threeMenuViewLeft = creenWidth + UIUtils.dip2px(10);
            twoMenuIsOpen = true;
            threeMenuIsOpen = false;
            nowState = TWO_OPEN;
            LogUtil.Error("Test","走的是这里");
            return;
        }

        twoMenuViewLeft = (int) (creenWidth * 0.2 + 0.5) - 20;//获取屏幕的1/5
        viewDragHelper.smoothSlideViewTo(twoMenuView, twoMenuViewLeft, 0);
        ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
        twoMenuIsOpen = true;//当前为二级菜单打开状态,打上标志
        nowState = TWO_OPEN;


    }

    /**
     * 点击二级菜单是相应的事件
     */
    public void OnClickTwoMenu() {
        threeMenuViewLeft = (int) (creenWidth * 0.45 + 0.5);
        viewDragHelper.smoothSlideViewTo(threeMenuView, threeMenuViewLeft, 0);
        ViewCompat.postInvalidateOnAnimation(SeekProductMenu.this);
        threeMenuIsOpen = true;//标注三级菜单为打开状态
        nowState = TWO_THREE_OPEN;
    }

    class ViewHolder {
        TextView textView;
    }

    class MyCallBack extends ViewDragHelper.Callback {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }
    }

    private RecoverListViewItemBackground recoverListViewItemBackground;

    public void setOnRecoverListViewItemBackgroundListener(RecoverListViewItemBackground recoverListViewItemBackground) {
        this.recoverListViewItemBackground =recoverListViewItemBackground;
    }

    /**
     * 接口回调恢复listView item的背景颜色
     */
    public interface RecoverListViewItemBackground {
        void onRecoverOneListViewItemBackgroundListener();//恢复一级菜单item默认背景回调
        void onRecoverTwoListViewItemBackgroundListener();//恢复二级菜单item默认背景回调
    }
}

对于ViewDragHelper 的介绍可以看:
ViewDragHelper详解(一)- 可拖动的view

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值