Android 仿酷狗滑动控件实现 流畅运行

最近又浏览了一遍github上的开源项目,有感于大神们的强大,所以想搞搞像kugou那样左右滑动的侧滑菜单控件。

打开移动设备的kugou软件,看了下整体效果,就知道了,我们要实现一个左侧主页,右侧菜单,拉动时以缩放式动画进场的控件。

创建项目等一些简单步骤就不说了。让我们来看下主要实现:
自定义一个控件DragLayout,继承自FrameLayout。

public class DragLayout extends FrameLayout {

    private boolean isShowShadow = false;

    /** * GestureDetectorCompat处理手势识别 * OnGestureListener有下面的几个动作: * 按下(onDown):刚刚手指接触到触摸屏的那一刹那,就是触的那一下。 * 抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。 * 长按(onLongPress): 手指按在持续一段时间,并且没有松开。 * 滚动(onScroll): 手指在触摸屏上滑动。 * 按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。 * 抬起(onSingleTapUp):手指离开触摸屏的那一刹那。 */
    private GestureDetectorCompat gestureDetector;
    private ViewDragHelper dragHelper; // 简化view拖拽操作的帮助类
    private DragListener dragListener;
    private int range; //菜单关闭时跟原点的距离
    private int width;
    private int height;  
    private int mainLeft; //侧菜单距左侧原点位置的距离
    private Context context;
    // private ImageView iv_shadow;
    private RelativeLayout vg_right;
    private MyRelativeLayout vg_main;
    private Status status = Status.Close;//默认菜单关闭
    private boolean isFirstInflate = true;//用来帮助mainLeft第一次获取距离时的
                                        //布尔型,初始化为真,在onSizeChanged()中改变

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

    public DragLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        this.context = context;
    }

    public DragLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        //初始化手势识别类与view拖曳类
        gestureDetector = new GestureDetectorCompat(context,
                new YScrollDetector());
        dragHelper = ViewDragHelper.create(this, dragHelperCallback);
    }
}

首先创建构造器,并初始化GestureDetectorCompat类与ViewDragHelper类

GestureDetectorCompat类是手势识别类,它对外提供了两个接口:OnGestureListener,OnDoubleTapListener,还有一个内部类SimpleOnGestureListener;SimpleOnGestureListener类是GestureDetector提供给我们的一个更方便的响应不同手势的类,它实现了上述两个接口。

ViewDragHelper类是android简化view拖拽操作的帮助类,作用在一个ViewGroup上,也就是说他不能直接作用到被拖拽的view, 其实这也很好理解,因为view在布局中的位置是父ViewGroup决定的。这个类初始化时,需将ViewDragHelper.Callback参数传进。这个回调也是控件实现侧滑的主要代码,它是连接ViewDragHelper与View之间的桥梁。

private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() {

        /** * 处理x方向拖动的,返回值该child现在的位置 */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (mainLeft + dx < 0) {  //右侧菜单距离原点的位置最小为0,最大为range
                return 0;
            } else if (mainLeft + dx > range) {
                return range;
            } else {
                return left;
            }
        }

        /** * tryCaptureView(View view, int pointerId) 表示尝试捕获子view,这里一定要返回true, * 返回true表示允许。 */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }

        /** * 获取边界 */
        @Override
        public int getViewHorizontalDragRange(View child) {
            return width;
        }

        /** * 该方法在手势拖动释放的时候被调用,可以在这里设置子View预期到达的位置, * 如果人为的手势拖动没有到达预期位置,我们可以让子View在人为的拖动结束后,再自动关的滑动到指定位置 */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            // if (xvel > 0) {
            // open();
            // } else if (xvel < 0) {
            // close();
            // } else
            if (releasedChild == vg_main && mainLeft < range * 0.3) {
                //当拉动主页时,如果mainLeft小于 0.3range,则自动打开 
                open();
            } else if (releasedChild == vg_right && mainLeft < range * 0.7) {
                //当拉动菜单页时,如果mainLeft大于 0.7range,则自动打开 
                open();
            } else {//其余都为关闭情况
                close();
            }
        }

        private final static String tag = "Draglayout";

        /** * 该方法在子view位置发生改变时都会被调用,可以在这个方法中做一些拖动过程中渐变的动画等操作 */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top,int dx, int dy) {

            //根据不同的子view,对mainLeft进行分别赋值
            if (changedView == vg_right) {  //右侧菜单
                mainLeft = left;
                Log.d(tag, "vg_main: " + mainLeft + " range: " + range);
            } else { //主页
                Log.d(tag, "vg_left1: " + mainLeft);
                mainLeft = mainLeft + left;
                Log.d(tag, "vg_left2: " + mainLeft + " left: " + left);
            }
            //mainLeft的取值范围
            if (mainLeft < 0) {
                mainLeft = 0;
            } else if (mainLeft > range) {
                mainLeft = range;
            }
            //
            // if (isShowShadow) {
            // iv_shadow.layout(mainLeft, 0, mainLeft + width, height);
            // }
            if (changedView == vg_main) {
                vg_main.layout(0, 0, width, height);
                vg_right.layout(mainLeft, 0, mainLeft + width, height);
            }

            dispatchDragEvent(mainLeft);
        }
    };

它主要是实现了以上几种方法,具体方法做何用,可看注释。
在这个方法里,我们需要判断操作的对象,是主界面还是菜单,根据对象的不同,给予mainLeft(侧菜单距左侧原点位置的距离)不同的赋值。在onViewReleased()方法里,判断mainLeft的大小,去打开或者关闭菜单。在onViewPositionChanged()中,对两个界面作拖动过程中的渐变动画。

因为将由ViewDragHelper处理view拖动事件,所以DragLayout的触摸事件也需一并给它处理。

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return dragHelper.shouldInterceptTouchEvent(ev)
                && gestureDetector.onTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        try {
            //必须将layout的触摸事件交由DragHelper类处理!
            dragHelper.processTouchEvent(e);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

事件都定义好了,我们还需要实现两个方法:onSizeChanged()和onLayout()

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = vg_main.getMeasuredWidth();
        height = vg_main.getMeasuredHeight();
        range = (int) (width * 0.8f);
        //第一次界面初始化时,根据获得的range值对mainLeft进行赋值
        if (isFirstInflate) {  
            mainLeft = range;
            isFirstInflate = false;
        }

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        vg_main.layout(0, 0, width, height);
        vg_right.layout(mainLeft, 0, mainLeft + width, height);

    }

在onSizeChanged()获取屏幕的高宽,并得到range值(菜单关闭时跟原点的距离)与初始化mainLeft.

其实,主要的实现代码也就是上面这些了,其他的比如渐变动画,以及点击按钮后的拉取与关闭菜单都比较简单,下面有代码下载链接,可以下载下来看看,这是从一个开源项目拷下来做了修改的,变成自己喜欢的风格。所以大神们勿喷哦!

效果图就不上了,不过保证可以使用哦。
最后发现gif好难弄,有大神会的,麻烦给我推荐个工具,可以生成移动设备上的简易gif动画。不胜感激!

具体的代码下载链接

你可能感兴趣的:(android,开源项目,kugou滑动菜单)