zoukankan      html  css  js  c++  java
  • Android -- SlidingMenu

    实现原理                                                                              

    在一个Activity的布局中需要有两部分,一个是菜单(menu)的布局,一个是内容(content)的布局。两个布局横向排列,菜单布局在左,内容布局在右初始化的时候将菜单布局向左偏移,以至于能够完全隐藏,这样内容布局就会完全显示在Activity中。然后通过监听手指滑动事件,来改变菜单布局的左偏移距离,从而控制菜单布局的显示和隐藏。

    布局                                                                                   

    <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=".MyActivity">
    
        <LinearLayout
            android:id="@+id/menu"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#ff00ff00"
            android:orientation="vertical"></LinearLayout>
    
        <LinearLayout
            android:id="@+id/content"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#ffff0000"
            android:orientation="vertical"></LinearLayout>
    
    </LinearLayout>

    这个布局文件的最外层布局是一个LinearLayout,排列方向是水平方向排列。这个LinearLayout下面嵌套了两个子LinearLayout,分别就是菜单的布局和内容的布局。这里为了要让布局尽量简单,菜单布局和内容布局里面没有加入任何控件。

    Code                                                                                   

    public class MyActivity extends Activity implements View.OnTouchListener {
        /**
         * 手指每单位时间滑动200个像素
         */
        private static final int SPEED = 200;
        /**
         * 屏幕宽度
         */
        private int mScreenWidth;
        /**
         * menu的layout
         */
        private LinearLayout mMenuLayout;
        /**
         * content的layout
         */
        private LinearLayout mContentLayout;
        /**
         * menu的layout的Paramters
         */
        private LinearLayout.LayoutParams mMenuParams;
        /**
         * menu完全显示的时候给content的宽度值
         */
        private int mMenuPadding = 80;
        /**
         * menu最多滑到左边缘,值由menu布局的宽度决定,marginLeft到达此值之后,不能在减少
         */
        private int mLeftEdge;
        /**
         * 测速度的对象
         */
        private VelocityTracker mVelocityTracker;
        /**
         * 手指按下的X坐标
         */
        private float mXDown;
        /**
         * 手指移动时候的X坐标
         */
        private float mXMove;
        /**
         * 手指抬起的X坐标
         */
        private float mXUp;
        /**
         * menu是否再显示
         */
        private boolean mIsMenuVisible = false;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_my);
            initViewsAndValues();
            mContentLayout.setOnTouchListener(this);
        }
    
        /**
         * 初始化menu和content并且设置他们的位置
         */
        private void initViewsAndValues() {
            //得到windowManager
            WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
            //得到屏幕宽度
            mScreenWidth = window.getDefaultDisplay().getWidth();
            //找到控件
            mMenuLayout = (LinearLayout) findViewById(R.id.menu);
            mContentLayout = (LinearLayout) findViewById(R.id.content);
            //得到menu的paramter
            mMenuParams = (LinearLayout.LayoutParams) mMenuLayout.getLayoutParams();
            //将menu的宽度设置为屏幕宽度减去mMenuPading
            mMenuParams.width = mScreenWidth - mMenuPadding;
            //左边缘的值复制为menu宽度的负数,这样的话就可以将menu隐藏
            mLeftEdge = -mMenuParams.width;
            //将margin设置为mLeftEdge
            mMenuParams.leftMargin = mLeftEdge;
            //将content显示再屏幕上
            mContentLayout.getLayoutParams().width = mScreenWidth;
        }
    
    
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            createVelocityTracker(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //记录X坐标
                    mXDown = event.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mXMove = event.getRawX();
                    int distanceX = (int) (mXMove - mXDown);
                    if (mIsMenuVisible) {
                        mMenuParams.leftMargin = distanceX;
                    } else {
                        mMenuParams.leftMargin = mLeftEdge + distanceX;
                    }
                    if (mMenuParams.leftMargin < mLeftEdge) {
                        //因为leftEdge是负数,就是menu已经隐藏完毕了,不能再往左隐藏了
                        mMenuParams.leftMargin = mLeftEdge;
                    } else if (mMenuParams.leftMargin > 0) {
                        //menu显示完全了,不能再往右移动了
                        mMenuParams.leftMargin = 0;
                    }
                    mMenuLayout.setLayoutParams(mMenuParams);
                    break;
                case MotionEvent.ACTION_UP:
                    mXUp = event.getRawX();
                    if (wantToShowMenu()) {
                        if (shouldScrollToMenu()) {
                            scrollToMenu();
                        } else {
                            //条件不满足,把menu继续隐藏掉
                            scrollToContent();
                        }
                    } else if (wantToShowContent()) {
                        if (shouldScrollToContent()) {
                            scrollToContent();
                        } else {
                            scrollToMenu();
                        }
                    }
                    break;
            }
            recycleVelocityTracker();
            return true;
        }
    
        /**
         * 创建VelocityTracker对象,针对于content的界面的滑动事件
         *
         * @param event
         */
        private void createVelocityTracker(MotionEvent event) {
            if (null == mVelocityTracker) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(event);
        }
    
        /**
         * 判断手势是不是想要显示Content && menu处于显示状态
         *
         * @return
         */
        private boolean wantToShowContent() {
            return mXUp - mXDown < 0 && mIsMenuVisible;
        }
    
        /**
         * 是不是要显示menu && menu处于隐藏状态
         *
         * @return
         */
        private boolean wantToShowMenu() {
            return mXUp - mXDown > 0 && !mIsMenuVisible;
        }
    
        /**
         * 是否应该滑动出menu
         *
         * @return
         */
        private boolean shouldScrollToMenu() {
            return mXUp - mXDown > mScreenWidth / 2 || getScrollVelocity() > SPEED;
        }
    
        /**
         * 是否应该让content全部显示出来
         *
         * @return
         */
        private boolean shouldScrollToContent() {
            return mXDown - mXUp < mScreenWidth / 2 || getScrollVelocity() > SPEED;
        }
    
        /**
         * 显示出menu
         */
        private void scrollToMenu() {
            new ScrollAsyncTask().execute(30);
        }
    
        /**
         * 隐藏掉menu
         */
        private void scrollToContent() {
            new ScrollAsyncTask().execute(-30);
        }
    
    
        /**
         * 得到手指滑动速度,每秒移动多少单位像素
         *
         * @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 ScrollAsyncTask extends AsyncTask<Integer, Integer, Integer> {
    
            @Override
            protected Integer doInBackground(Integer[] speed) {
                //得到当前margin
                int leftMargin = mMenuParams.leftMargin;
                //不断更改margin的值
                while (true) {
                    leftMargin = leftMargin + speed[0];
                    if (leftMargin > 0) {
                        leftMargin = 0;
                        break;
                    }
                    if (leftMargin < mLeftEdge) {
                        leftMargin = mLeftEdge;
                        break;
                    }
                    publishProgress(leftMargin);
                    sleep();
                }
                if (speed[0] > 0) {
                    mIsMenuVisible = true;
                } else {
                    mIsMenuVisible = false;
                }
                return leftMargin;
            }
    
            @Override
            protected void onPostExecute(Integer integer) {
                mMenuParams.leftMargin = integer;
                mMenuLayout.setLayoutParams(mMenuParams);
            }
    
            @Override
            protected void onProgressUpdate(Integer... values) {
                mMenuParams.leftMargin = values[0];
                mMenuLayout.setLayoutParams(mMenuParams);
            }
        }
    
        /**
         * sleep线程睡眠一下
         */
        private void sleep() {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

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

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

    我是天王盖地虎的分割线                                                             

    1

    源代码:http://pan.baidu.com/s/1dD1Qx01

    SlidingMenuDemo.zip

    参考:http://blog.csdn.net/guolin_blog/article/details/8714621

  • 相关阅读:
    java 数字加密
    java 计算旅途时间
    java练习题3
    java练习题
    java练习
    java练习题
    删除记录模块的设计与实现
    修改记录模块的设计与实现
    提交页面
    采用PreparedStatement的对象实现记录的查询操作
  • 原文地址:https://www.cnblogs.com/yydcdut/p/4136338.html
Copyright © 2011-2022 走看看