实现原理:在一个Activity的布局中需要有两部分,一个是菜单(menu)的布局,一个是内容(content)的布局。两个布局横向排列,菜单布局在左,内容布局在右。初始化的时候将菜单布局向左偏移,以至于能够完全隐藏,这样内容布局就会完全显示在Activity中。然后通过监听手指滑动事件,来改变菜单布局的左偏移距离,从而控制菜单布局的显示和隐藏。
下来来实现这个效果:
1.打开layout下的activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".MainActivity" >
    <LinearLayout 
        android:id="@+id/ll_menu"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        android:background="@drawable/menu"    
                ></LinearLayout>
    <LinearLayout 
        android:id="@+id/ll_content"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/content"
        android:orientation="vertical"
        ></LinearLayout>
</LinearLayout>
这个布局文件的最外层布局是一个LinearLayout,排列方向是水平方向排列。这个LinearLayout下面嵌套了两个子LinearLayout,分别就是菜单的布局和内容的布局。
2.打开MainActivity.java
public class MainActivity extends Activity implements OnTouchListener {
    //滚动显示和隐藏Menu,手指滑动需要达到的速度
    public static final int SNAP_VELOCITY=200;
    
    //屏幕宽度
    private int screenWidth;
    
    //menu最多可以滑动的左边缘
    private int leftEdge;
    
    //menu最多可以滑动的右边缘
    private int rightEdge=0;
    
    //menu完全显示时,留给content的宽度值
    private int menuPadding=80;
    
    //主内容布局
    private View content;
    //menu布局
    private View menu;
    
    //menu布局的参数,通过这个参数来更改leftMargin的值
    private LinearLayout.LayoutParams menuParams;
    
    //记录手指按下的横坐标
    private float xDown;
    
    //记录手指移动的横坐标
    private float xMove;
    
    //记录手指抬起的横坐标
    private float xUp;
    
    //menu当前是显示还是隐藏
    private boolean isMenuVisible;
    
    //用于计算手指滑动的速度
    private VelocityTracker mVelocityTracker;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        content.setOnTouchListener(this);
    }
    
    private void init(){
        WindowManager wm=getWindowManager();
        screenWidth=wm.getDefaultDisplay().getWidth();
        content=findViewById(R.id.ll_content);
        menu=findViewById(R.id.ll_menu);
        menuParams=(LayoutParams) menu.getLayoutParams();
        //将menu的宽度设置为屏幕宽度减去menuPadding
        menuParams.width=screenWidth-menuPadding;
        leftEdge=-menuParams.width;
        menuParams.leftMargin=leftEdge;
        //将content的宽度设置为屏幕宽度
        content.getLayoutParams().width=screenWidth;
    }
    @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:
            xMove=event.getRawX();
            int distanceX=(int)(xMove-xDown);// 移动的距离
            if(isMenuVisible){//判断当前Menu是否已经显示,如果true,说明已经显示
                menuParams.leftMargin=distanceX;   
            }
            else{
                menuParams.leftMargin=leftEdge+distanceX;
            }
            if(menuParams.leftMargin<leftEdge){
                menuParams.leftMargin=leftEdge;
                
            }
            else if(menuParams.leftMargin>rightEdge){
                menuParams.leftMargin=rightEdge;
            }
            menu.setLayoutParams(menuParams);
            break;
        case MotionEvent.ACTION_UP:
            xUp=event.getRawX();
            if(wantToShowMenu()){
                if(shouldScrollToMenu()){
                    scrollToMenu();
                }
                else{
                    scrollToContent();
                }
            }
            else if(wantToShowContent()){
                if(shouldScrollToContent()){
                    scrollToContent();
                }
                else{
                    scrollToMenu();
                }
            }
            recycleVelocityTracker();
            break;
        default:
            break;
        }
        return true;
    }
    
      /** 
     * 判断当前手势的意图是不是想显示content。如果手指移动的距离是负数,且当前menu是可见的,则认为当前手势是想要显示content。 
     *  
     * @return 当前手势想显示content返回true,否则返回false。 
     */  
    private boolean wantToShowContent() {  
        return xUp - xDown < 0 && isMenuVisible;  
    }  
    
       /** 
     * 判断当前手势的意图是不是想显示menu。如果手指移动的距离是正数,且当前menu是不可见的,则认为当前手势是想要显示menu。 
     *  
     * @return 当前手势想显示menu返回true,否则返回false。 
     */  
    private boolean wantToShowMenu(){
        return xUp-xDown>0&&!isMenuVisible;
    }
    
      /** 
     * 判断是否应该滚动将menu展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY, 
     * 就认为应该滚动将menu展示出来。 
     *  
     * @return 如果应该滚动将menu展示出来返回true,否则返回false。 
     */ 
    private boolean shouldScrollToMenu(){
        return xUp-xDown>screenWidth/2||getScrollVelocity()>SNAP_VELOCITY;
    }
    
    
      /** 
     * 判断是否应该滚动将content展示出来。如果手指移动距离加上menuPadding大于屏幕的1/2, 
     * 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将content展示出来。 
     *  
     * @return 如果应该滚动将content展示出来返回true,否则返回false。 
     */  
    private boolean shouldScrollToContent(){
        return xDown-xUp+menuPadding>screenWidth/2||getScrollVelocity()>SNAP_VELOCITY;
    }
    
    //将屏幕滚动到menu界面,滚动速度设为30
    private void scrollToMenu(){
        new ScrollTask().execute(30);
    }
    //将屏幕滚到到content界面,滚动速度为-30
    private void scrollToContent(){
        new ScrollTask().execute(-30);
    }
    
    //创建velocityTracker对象,并将触摸content界面的滑动事件加入velocityTracker当中
    private void createVelocityTracker(MotionEvent event){
        if(mVelocityTracker==null){
            mVelocityTracker=VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
    }
    //获取手指在Content界面滑动的速度
    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=menuParams.leftMargin;
            //根据传入速度来滚动界面,当滚动达到左边界或右边界,跳出循环
            while(true){
                leftMargin=leftMargin+speed[0];
                if(leftMargin>rightEdge){
                    leftMargin=rightEdge;
                    break;
                }
                if(leftMargin<leftEdge){
                    leftMargin=leftEdge;
                    break;
                }
                publishProgress(leftMargin);
                sleep(10);
            }
            if(speed[0]>0){
                isMenuVisible=true;
            }
            else{
                isMenuVisible=false;
            }
            return leftMargin;
        }
        
        @Override
        protected void onProgressUpdate(Integer... values) {
            menuParams.leftMargin=values[0];
            menu.setLayoutParams(menuParams);
        }
        @Override
        protected void onPostExecute(Integer result) {
            menuParams.leftMargin=result;
            menu.setLayoutParams(menuParams);
        }
        
        private void sleep(long mills){
            try {
                Thread.sleep(mills);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

初始化的时候调用initValues方法,在这里面将内容布局的宽度设定为屏幕的宽度,菜单布局的宽度设定为屏幕的宽度减去menuPadding值,这样可以保证在菜单布局展示的时候,仍有一部分内容布局可以看到。如果不在初始化的时候重定义两个布局宽度,就会按照layout文件里面声明的一样,两个布局都是fill_parent,这样就无法实现滑动菜单的效果了。然后将菜单布局的左偏移量设置为负的菜单布局的宽度,这样菜单布局就会被完全隐藏,只有内容布局会显示在界面上。

之后给内容布局注册监听事件,这样当手指在内容布局上滑动的时候就会触发onTouch事件。在onTouch事件里面,根据手指滑动的距离会改变菜单布局的左偏移量,从而控制菜单布局的显示和隐藏。当手指离开屏幕的时候,会判断应该滑动到菜单布局还是内容布局,判断依据是根据手指滑动的距离或者滑动的速度.

当前Demo只适用于单个Activity.





android自定义SlideMenu源码详解之最简单侧滑实现的更多相关文章

  1. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  2. saltstack源码详解一

    目录 初识源码流程 入口 1.grains.items 2.pillar.items 2/3: 是否可以用python脚本实现 总结pillar源码分析: @(python之路)[saltstack源 ...

  3. Activiti架构分析及源码详解

    目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...

  4. 源码详解系列(六) ------ 全面讲解druid的使用和源码

    简介 druid是用于创建和管理连接,利用"池"的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制.连接可靠性测试.连接泄露控制.缓存语句等功能,另外,druid还扩展 ...

  5. 源码详解系列(七) ------ 全面讲解logback的使用和源码

    什么是logback logback 用于日志记录,可以将日志输出到控制台.文件.数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性. logback ...

  6. 源码详解系列(八) ------ 全面讲解HikariCP的使用和源码

    简介 HikariCP 是用于创建和管理连接,利用"池"的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制.连接可靠性测试.连接泄露控制.缓存语句等功能,另外,和 dr ...

  7. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...

  8. RocketMQ源码详解 | Producer篇 · 其二:消息组成、发送链路

    概述 在上一节 RocketMQ源码详解 | Producer篇 · 其一:Start,然后 Send 一条消息 中,我们了解了 Producer 在发送消息的流程.这次我们再来具体下看消息的构成与其 ...

  9. RocketMQ源码详解 | Broker篇 · 其一:线程模型与接收链路

    概述 在上一节 RocketMQ源码详解 | Producer篇 · 其二:消息组成.发送链路 中,我们终于将消息发送出了 Producer,在短暂的 tcp 握手后,很快它就会进入目的 Broker ...

随机推荐

  1. EAIntroView–高度可定制的iOS应用欢迎页通用解决方案

    简介 高度可定制的应用欢迎页通用解决方案,可高度定制,不要仅限于现有的demo. 项目主页: EAIntroView 最新示例: 点击下载 入门 安装 安装后,引入” EAIntroView.h”并设 ...

  2. web攻击技术与防护

    一.跨站脚本攻击(XSS) 跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器运行非法的HTML标签或JavaScript进行的一种攻击.动态创建的HTML部分有可能隐藏着安全漏洞.就这样, ...

  3. HDU.3177Crixalis's Equipment(贪心)

    题目来源:3177 题目分析:一只蝎子要搬动一堆装备到一个容量为V的洞里面,每个装备有两个属性,一个是固有体积A,放置之后洞的剩余空间就会减少A,一个是移动体积B,只有当体积B小于等于当前洞的剩余体积 ...

  4. Maven - pom.xml常用元素

    基本坐标信息:

  5. python中正则表达式在中文字符串匹配时的坑

    之前一直有使用python 正则表达式来做中文字符串或者中英文数字混合的字符串的匹配,发现有不少情况下会匹配失灵或者结果混乱,并且在不同操作系统上匹配结果也不一致,查了很久都不知道是什么原因.今天终于 ...

  6. JavaScript数组常用的方法

    改变原数组: ※ push,pop,shif,unshift,sort,reverse ※ splice 不改变原数组: ※ concat,join→split,toString,slice push ...

  7. 一件安装lnmp

    wget -c http://soft.vpser.net/lnmp/lnmp1.2-full.tar.gz && tar zxf lnmp1.2-full.tar.gz && ...

  8. C++基础 对象的管理——单个对象的管理

    1. 为什么要有构造函数和析构函数 面向对象的思想是从生活中来,手机.车出厂时,是一样的. 这些对象都是被初始化后才上市的,初始化是对象普遍存在的一个状态. 普通方案: 对每个类提供一个 init 函 ...

  9. 状压DP详解(位运算)

    前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...

  10. https://www.cnblogs.com/gaoxiang12/p/3695962.html

    https://www.cnblogs.com/gaoxiang12/p/3695962.html