zoukankan      html  css  js  c++  java
  • android自定义SlideMenu源码详解之最简单侧滑实现

    实现原理:在一个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=(intmVelocityTracker.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.

     








    qq3061280@163.com
  • 相关阅读:
    深入new/delete:Operator new的全局重载
    c语言运算符优先级
    投影仪开关机码和波特率
    sqlyog mysql 外键引用列找不到想要的字段的原因
    idea 迁移maven项目出现导入仓库半天没反应的问题解决
    idea 解决 pom.xml 中,maven仓库无法导入的问题(红线)
    fastjson 使用记录
    idea git pull项目到本地时容易出现的问题
    JSONObject
    idea Cannot Resolve Symbol 问题解决
  • 原文地址:https://www.cnblogs.com/aibuli/p/6c44261f30169a3e46276898a4d91390.html
Copyright © 2011-2022 走看看