Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效

之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理 或者还没看过的朋友,请先去看一遍之前的文章 Android滑动菜单特效实现,仿人人客户端侧滑效果,史上 最简单的侧滑实现 ,因为我们今天要实现的滑动菜单框架也是基于同样的原理的。

之前的文章中在 最后也提到了,如果是你的应用程序中有很多个Activity都需要加入滑动菜单的功能,那么每个Activity都 要写上百行的代码才能实现效果,再简单的滑动菜单实现方案也没用。因此我们今天要实现一个滑动菜单的 框架,然后在任何Activity中都可以一分钟引入滑动菜单功能。

首先还是讲一下实现原理。说是滑动 菜单的框架,其实说白了也很简单,就是我们自定义一个布局,在这个自定义布局中实现好滑动菜单的功能 ,然后只要在Activity的布局文件里面引入我们自定义的布局,这个Activity就拥有了滑动菜单的功能了。 原理讲完了,是不是很简单?下面我们来动手实现吧。

在Eclipse中新建一个Android项目,项目名就 叫做RenRenSlidingLayout。

新建一个类,名叫SlidingLayout,这个类是继承自LinearLayout的,并 且实现了OnTouchListener接口,具体代码如下:

public class SlidingLayout extends LinearLayout implements OnTouchListener {  

    /**
     * 滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。
     */
    public static final int SNAP_VELOCITY = 200;  

    /**
     * 屏幕宽度值。
     */
    private int screenWidth;  

    /**
     * 左侧布局最多可以滑动到的左边缘。值由左侧布局的宽度来定,marginLeft到达此值之后,不能再减

少。
     */
    private int leftEdge;  

    /**
     * 左侧布局最多可以滑动到的右边缘。值恒为0,即marginLeft到达0之后,不能增加。
     */
    private int rightEdge = 0;  

    /**
     * 左侧布局完全显示时,留给右侧布局的宽度值。
     */
    private int leftLayoutPadding = 80;  

    /**
     * 记录手指按下时的横坐标。
     */
    private float xDown;  

    /**
     * 记录手指移动时的横坐标。
     */
    private float xMove;  

    /**
     * 记录手机抬起时的横坐标。
     */
    private float xUp;  

    /**
     * 左侧布局当前是显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。
     */
    private boolean isLeftLayoutVisible;  

    /**
     * 左侧布局对象。
     */
    private View leftLayout;  

    /**
     * 右侧布局对象。
     */
    private View rightLayout;  

    /**
     * 用于监听侧滑事件的View。
     */
    private View mBindView;  

    /**
     * 左侧布局的参数,通过此参数来重新确定左侧布局的宽度,以及更改leftMargin的值。
     */
    private MarginLayoutParams leftLayoutParams;  

    /**
     * 右侧布局的参数,通过此参数来重新确定右侧布局的宽度。
     */
    private MarginLayoutParams rightLayoutParams;  

    /**
     * 用于计算手指滑动的速度。
     */
    private VelocityTracker mVelocityTracker;  

    /**
     * 重写SlidingLayout的构造函数,其中获取了屏幕的宽度。
     *
     * @param context
     * @param attrs
     */
    public SlidingLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        screenWidth = wm.getDefaultDisplay().getWidth();
    }  

    /**
     * 绑定监听侧滑事件的View,即在绑定的View进行滑动才可以显示和隐藏左侧布局。
     *
     * @param bindView
     *            需要绑定的View对象。
     */
    public void setScrollEvent(View bindView) {
        mBindView = bindView;
        mBindView.setOnTouchListener(this);
    }  

    /**
     * 将屏幕滚动到左侧布局界面,滚动速度设定为30.
     */
    public void scrollToLeftLayout() {
        new ScrollTask().execute(30);
    }  

    /**
     * 将屏幕滚动到右侧布局界面,滚动速度设定为-30.
     */
    public void scrollToRightLayout() {
        new ScrollTask().execute(-30);
    }  

    /**
     * 左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效。
     *
     * @return 左侧布局完全显示返回true,完全隐藏返回false。
     */
    public boolean isLeftLayoutVisible() {
        return isLeftLayoutVisible;
    }  

    /**
     * 在onLayout中重新设定左侧布局和右侧布局的参数。
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            // 获取左侧布局对象
            leftLayout = getChildAt(0);
            leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams();
            // 重置左侧布局对象的宽度为屏幕宽度减去leftLayoutPadding
            leftLayoutParams.width = screenWidth - leftLayoutPadding;
            // 设置最左边距为负的左侧布局的宽度
            leftEdge = -leftLayoutParams.width;
            leftLayoutParams.leftMargin = leftEdge;
            leftLayout.setLayoutParams(leftLayoutParams);
            // 获取右侧布局对象
            rightLayout = getChildAt(1);
            rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams();
            rightLayoutParams.width = screenWidth;
            rightLayout.setLayoutParams(rightLayoutParams);
        }
    }  

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        createVelocityTracker(event);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 手指按下时,记录按下时的横坐标
            xDown = event.getRawX();
            break;
        case MotionEvent.ACTION_MOVE:
            // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局
            xMove = event.getRawX();
            int distanceX = (int) (xMove - xDown);
            if (isLeftLayoutVisible) {
                leftLayoutParams.leftMargin = distanceX;
            } else {
                leftLayoutParams.leftMargin = leftEdge + distanceX;
            }
            if (leftLayoutParams.leftMargin < leftEdge) {
                leftLayoutParams.leftMargin = leftEdge;
            } else if (leftLayoutParams.leftMargin > rightEdge) {
                leftLayoutParams.leftMargin = rightEdge;
            }
            leftLayout.setLayoutParams(leftLayoutParams);
            break;
        case MotionEvent.ACTION_UP:
            // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局 

            xUp = event.getRawX();
            if (wantToShowLeftLayout()) {
                if (shouldScrollToLeftLayout()) {
                    scrollToLeftLayout();
                } else {
                    scrollToRightLayout();
                }
            } else if (wantToShowRightLayout()) {
                if (shouldScrollToContent()) {
                    scrollToRightLayout();
                } else {
                    scrollToLeftLayout();
                }
            }
            recycleVelocityTracker();
            break;
        }
        return isBindBasicLayout();
    }  

    /**
     * 判断当前手势的意图是不是想显示右侧布局。如果手指移动的距离是负数,且当前左侧布局是可见的,则认为当前手势是想要显示右侧布局。
     *
     * @return 当前手势想显示右侧布局返回true,否则返回false。
     */
    private boolean wantToShowRightLayout() {
        return xUp - xDown < 0 && isLeftLayoutVisible;
    }  

    /**
     * 判断当前手势的意图是不是想显示左侧布局。如果手指移动的距离是正数,且当前左侧布局是不可见的,则认为当前手势是想要显示左侧布局。
     *
     * @return 当前手势想显示左侧布局返回true,否则返回false。
     */
    private boolean wantToShowLeftLayout() {
        return xUp - xDown > 0 && !isLeftLayoutVisible;
    }  

    /**
     * 判断是否应该滚动将左侧布局展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
     * 就认为应该滚动将左侧布局展示出来。
     *
     * @return 如果应该滚动将左侧布局展示出来返回true,否则返回false。
     */
    private boolean shouldScrollToLeftLayout() {
        return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;
    }  

    /**
     * 判断是否应该滚动将右侧布局展示出来。如果手指移动距离加上leftLayoutPadding大于屏幕的1/2,
     * 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将右侧布局展示出来。
     *
     * @return 如果应该滚动将右侧布局展示出来返回true,否则返回false。
     */
    private boolean shouldScrollToContent() {
        return xDown - xUp + leftLayoutPadding > screenWidth / 2
                || getScrollVelocity() > SNAP_VELOCITY;
    }  

    /**
     * 判断绑定滑动事件的View是不是一个基础layout,不支持自定义layout,只支持四种基本layout,
     * AbsoluteLayout已被弃用。
     *
     * @return 如果绑定滑动事件的View是LinearLayout,RelativeLayout,FrameLayout,
     *         TableLayout之一就返回true,否则返回false。
     */
    private boolean isBindBasicLayout() {
        if (mBindView == null) {
            return false;
        }
        String viewName = mBindView.getClass().getName();
        return viewName.equals(LinearLayout.class.getName())
                || viewName.equals(RelativeLayout.class.getName())
                || viewName.equals(FrameLayout.class.getName())
                || viewName.equals(TableLayout.class.getName());  

    }  

    /**
     * 创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。
     *
     * @param event
     *            右侧布局监听控件的滑动事件
     */
    private void createVelocityTracker(MotionEvent event) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
    }  

    /**
     * 获取手指在右侧布局的监听View上的滑动速度。
     *
     * @return 滑动速度,以每秒钟移动了多少像素值为单位。
     */
    private int getScrollVelocity() {
        mVelocityTracker.computeCurrentVelocity(1000);
        int velocity = (int) mVelocityTracker.getXVelocity();
        return Math.abs(velocity);
    }  

    /**
     * 回收VelocityTracker对象。
     */
    private void recycleVelocityTracker() {
        mVelocityTracker.recycle();
        mVelocityTracker = null;
    }  

    class ScrollTask extends AsyncTask<Integer, Integer, Integer> {  

        @Override
        protected Integer doInBackground(Integer... speed) {
            int leftMargin = leftLayoutParams.leftMargin;
            // 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。
            while (true) {
                leftMargin = leftMargin + speed[0];
                if (leftMargin > rightEdge) {
                    leftMargin = rightEdge;
                    break;
                }
                if (leftMargin < leftEdge) {
                    leftMargin = leftEdge;
                    break;
                }
                publishProgress(leftMargin);
                // 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。 

                sleep(20);
            }
            if (speed[0] > 0) {
                isLeftLayoutVisible = true;
            } else {
                isLeftLayoutVisible = false;
            }
            return leftMargin;
        }  

        @Override
        protected void onProgressUpdate(Integer... leftMargin) {
            leftLayoutParams.leftMargin = leftMargin[0];
            leftLayout.setLayoutParams(leftLayoutParams);
        }  

        @Override
        protected void onPostExecute(Integer leftMargin) {
            leftLayoutParams.leftMargin = leftMargin;
            leftLayout.setLayoutParams(leftLayoutParams);
        }
    }  

    /**
     * 使当前线程睡眠指定的毫秒数。
     *
     * @param millis
     *            指定当前线程睡眠多久,以毫秒为单位
     */
    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索linearlayout动态布局
, private
, 自定义布局怎么实现
, return
, 滑动
, onlayout
, 鼠标滚动监听
, android布局的实现
, motionevent
, doinbackground
, 滚动
, 布局 activity view
, 左侧
右侧
绿幕特效一分钟多少钱、仿京东分类滑动特效、手机上下滑动翻页特效、淘宝css3鼠标滑动特效、js仿京东分类滑动特效,以便于您获取更多的相关知识。

时间: 2024-10-04 00:52:57

Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效的相关文章

Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能

以下是我自己花功夫编写了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享一下.相信在阅读完本篇文章之后,大家都可以在自己的项目中一分钟引入下拉刷新功能   最近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不 怎么理想.有些是因为功能不完整或有Bug,有些是因为使用起来太复杂,十全十美的还真没找到.因此我也是放弃了在网上找现成代码的想法,自己花功夫编写 了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享

Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效

记得在很早之前,我写了一篇关于Android滑动菜单的文章,其中有一个朋友在评论中留言,希望我可以 帮他将这个滑动菜单改成双向滑动的方式.当时也没想花太多时间,简单修改了一下就发给了他,结果没想 到后来却有一大批的朋友都来问我要这份双向滑动菜单的代码.由于这份代码写得很不用心,我发了部分朋 友之后实在不忍心继续发下去了,于是决定专门写一篇文章来介绍更好的Android双向滑动菜单的实现方法. 在开始动手之前先来讲一下实现原理,在一个Activity的布局中需要有三部分,一个是左侧菜单的布 局,一

Android ImageLoader第三方框架解析_Android

本文实例为大家分享了Android ImageLoader框架的使用方法,供大家参考,具体内容如下 1.准备工作 1)导入universal-image-loader-1.9.5.jar到项目中 2)创建MyApplication继承Application,在oncreate()中初始化ImageLoader public class MyApplication extends Application { @Override public void onCreate() { super.onCr

Android 滑动拦截实例代码解析_Android

废话不多说了,直接给大家贴代码了,具体代码如下所示: package demo.hq.com.fby; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; /** * Created by huqing on 2016/12/7.

Android 滑动拦截实例代码解析

废话不多说了,直接给大家贴代码了,具体代码如下所示: package demo.hq.com.fby; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; /** * Created by huqing on 2016/12/7.

Android App中使用ViewPager实现滑动分页的要点解析_Android

以前如果要做 Tab 分页的话,必须要用一个很难用的 TabActivity,而且做出来的效果很差,弹性也很小 忘了从什么时候开始,Google release 了 ViewPager 这好东西取代了以前难用的 Gallery 元件,加上从 Honeycomb 导入的 Fragment 之后终于能够简单做出好看又好用的 Layout 了! 这里我们采用PagerTabStrip ,做出来的效果如下 特色就是使用简单,出来的效果则是目前显示的分页 Tab 的文字会自动置中,然后分别在左右显示上一个

Android ActionBar制作时钟实例解析_Android

本文实例为大家分享了Android ActionBar制作时钟的具体代码,供大家参考,具体内容如下 1. MainActivity.java   package com.example.days19actionbar07custom; import com.example.days19actionbar07custom.R; import android.app.Activity; import android.os.Bundle; import android.view.Menu; impor

Android通用流行框架大全【整理】_Android

Android通用流行框架大全 1. 缓存 DiskLruCacheJava实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader一个强大的加载,缓存,展示图片的库Picasso一个强大的图片下载与缓存的库Fresco一个用于管理图像和他们使用的内存的库Glide一个图片加载和缓存的库 3. 图片处理 Picasso-transformations一个为Picasso提供多种图片变换的库Glide-transformations一个为Glide提供多

Android 高仿微信(QQ)滑动弹出编辑、删除菜单效果,增加下拉刷新功能

  不可否认,微信.QQ列表的滑动删除.编辑功能着实很经典(从IOS那边模仿过来的),然.Android这边,对列表的操作,其实大多还停留上下文菜单来实现.    Android如何实现list item的滑动呢?介绍一个很简单实用的开源项目:   https://github.com/baoyongzhang/SwipeMenuListView   使用该开源项目,我们只需要很简单的一些逻辑,就可以实现滑动菜单功能了!先看下效果图:       1. 下载开源项目,并将其中的liberary导