• 【Android 界面效果31】Android--侧滑菜单应用的实现


    侧滑菜单应用现在非常多,而且实现方式也多种多样。通过在网上的多方查找,我找到郭霖少侠的这篇文章:http://blog.csdn.net/guolin_blog/article/details/8744400,研究之后收获颇多。同时记得以前看过一篇讲Scroller实现滑屏的文章:http://www.cnblogs.com/wanqieddy/archive/2012/05/05/2484534.html

    那为何不用scroller来实现以下侧滑菜单?闲的蛋疼,那就试试吧,在这里先感谢以上两篇博文给我的启发。

    原理:通过scrollBy和scrollTo来移动右侧的content布局,实际上整个过程中,左侧的menu布局未发生滚动,这样出来的效果是右侧content布局覆盖住左侧menu布局。当然scroll的方式也可以实现menu和content同时平移的效果,这个只需要在布局文件上动动手脚就行了,在此先按下不表。

    下面是本文的实现效果

    activity_main.xml文件:

    首先放入menu布局,因为是RelativeLayout布局因此先放入的会被覆盖,然后是滑动布局,在滑动布局中加入content布局,因为content布局会随滑动布局一起移动。

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.     tools:context=".MainActivity" >  
    6.   
    7.     <include android:id="@+id/menu" layout="@layout/menu" />  
    8.       
    9.     <com.noter.layout.SlideLayout  
    10.         android:id="@+id/slide_layout"  
    11.         android:layout_width="fill_parent"  
    12.         android:layout_height="fill_parent" >  
    13.           
    14.         <include android:id="@+id/content" layout="@layout/content_diary" />  
    15.       
    16.     </com.noter.layout.SlideLayout>  
    17.   
    18. </RelativeLayout>  

    menu.xml和conent_diary.xml这两个布局文件就不用讲了,大家跟着感觉走,想放什么放什么吧。

     

    SlideLayout.java文件:大部分代码都很简单,看注释就能懂。这里只说几个要点,我也是调试过之后才明白的:

    1. getScrollX()得到的是当前View的最左边所在的X坐标。程序初始化后此值为0,View向右滑动后,相当于屏幕坐标系向负方向移动了一段,因此此时此值为负数;反之则相反。

    2. Scroller实际上只是保存和提供自动滑动时所需的数值,真正完成滑动的还是scrollTo和scrollBy两个函数。

    3. 我写的SlideLayout继承自RelativeLayout,其实也可以继承自ViewGroup,但是就需要自己重写onMeasure和onLayout函数,来布局子控件。这个实现中没有特殊的布局要求,所以用RelatiLayout就可以啦。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.noter.layout;  
    2.   
    3. import android.content.Context;  
    4. import android.graphics.Rect;  
    5. import android.util.AttributeSet;  
    6. import android.util.Log;  
    7. import android.view.MotionEvent;  
    8. import android.view.VelocityTracker;  
    9. import android.view.View;  
    10. import android.view.ViewConfiguration;  
    11. import android.widget.RelativeLayout;  
    12. import android.widget.Scroller;  
    13.   
    14. public class SlideLayout extends RelativeLayout {  
    15.     private static String TAG = "SlideMenuLayout";  
    16.       
    17.     private Context mContext;  
    18.     private Scroller mScroller;    //Android 提供的滑动辅助类  
    19.     private int mTouchSlop = 0 ;    //在被判定为滚动之前用户手指可以移动的最大值  
    20.     private VelocityTracker mVelocityTracker;    //用于计算手指滑动的速度  
    21.     public static final int SNAP_VELOCITY = 200;    //滚动显示和隐藏左侧布局时,手指滑动需要达到的速度:每秒200个像素点  
    22.     private int mMaxScrollX = 0;    //最大滚动距离,等于menu的宽度  
    23.     public void setMaxScrollX(int maxScrollX) {  
    24.         this.mMaxScrollX = maxScrollX;  
    25.     }  
    26.   
    27.     private float mDownX;    //一次按下抬起的动作中,按下时的X坐标,用于和抬起时的X比较,判断移动距离。少于mTouchSlop则判定为原地点击  
    28.     private float mLastX;    //记录滑动过程中的X坐标  
    29.       
    30.     private boolean isMenuOpen = false;    //菜单界面是否被打开,只有完全打开才为true  
    31.     public boolean isMenuOpen() {  
    32.         return isMenuOpen;  
    33.     }  
    34.   
    35.     private View mContent;  
    36.       
    37.     public SlideLayout(Context context, AttributeSet attrs) {  
    38.         super(context, attrs);  
    39.         mContext = context;  
    40.         init();  
    41.     }  
    42.       
    43.     private void init() {  
    44.         Log.v(TAG, "init start");  
    45.         mScroller = new Scroller(mContext);  
    46.         mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  
    47.     }  
    48.   
    49.     @Override  
    50.     public void computeScroll() {  
    51.         if (mScroller.computeScrollOffset()) {  
    52.             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  
    53.             postInvalidate();  
    54.         }  
    55.     }  
    56.       
    57.     @Override  
    58.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
    59.         super.onLayout(changed, l, t, r, b);  
    60.         if(changed){  
    61.             mContent = getChildAt(0);  
    62.         }  
    63.     }  
    64.       
    65.     @Override  
    66.     public boolean onTouchEvent(MotionEvent event) {  
    67.         createVelocityTracker(event);  
    68.         int curScrollX = getScrollX();  
    69.         // 检查触摸点是否在滑动布局(内容content)中,如果不是则返回false,即本View不处理该事件  
    70.         if (mContent != null) {  
    71.             Rect rect = new Rect();  
    72.             mContent.getHitRect(rect);  
    73.             if (!rect.contains((int)event.getX() + curScrollX, (int)event.getY())) {  
    74.                 return false;  
    75.             }  
    76.         }  
    77.   
    78.         float x = event.getX();    //取得本次event的X坐标  
    79.         switch (event.getAction()) {  
    80.         case MotionEvent.ACTION_DOWN:  
    81.             mDownX = x;  
    82.             mLastX = x;  
    83.             break;  
    84.         case MotionEvent.ACTION_MOVE:  
    85.             int deltaX = (int)(mLastX - x);  
    86.             if((curScrollX + deltaX) < -mMaxScrollX) {  
    87.                 deltaX = -mMaxScrollX - curScrollX;  
    88.             }  
    89.             if((curScrollX + deltaX) > 0){  
    90.                 deltaX = -curScrollX;  
    91.             }  
    92.               
    93.             if (deltaX != 0) {  
    94.                 scrollBy(deltaX, 0);  
    95.             }  
    96.             mLastX = x;  
    97.             break;  
    98.         case MotionEvent.ACTION_UP:  
    99.             int velocityX = getScrollVelocity();  
    100.             int offsetX = (int) (x - mDownX);  
    101.               
    102.             //成立表明移动距离已经达到被判断为滑动的最低标准  
    103.             //不成立表明不被判断为滑动,则认为是单一的点击,则关闭menu  
    104.             if(Math.abs(offsetX) >= mTouchSlop) {  
    105.                   
    106.                 //成立表明手指移动速度达标,则进行自动滑动;  
    107.                 //不成立表明速度不达标,但仍然需要判断当前SlideLayout的位置  
    108.                 //如果已经超过一半,则继续自动完成剩下的滑动,如果没有超过一半,则反向滑动  
    109.                 if(Math.abs(velocityX) >= SNAP_VELOCITY) {  
    110.                     if(velocityX > 0){  
    111.                         openMenu();  
    112.                     } else if(velocityX < 0) {  
    113.                         closeMenu();  
    114.                     }  
    115.                 } else {  
    116.                     if (curScrollX >= -mMaxScrollX / 2) {  
    117.                         closeMenu();  
    118.                     } else {  
    119.                         openMenu();  
    120.                     }  
    121.                 }  
    122.             } else {  
    123.                 closeMenu();  
    124.             }  
    125.   
    126.             recycleVelocityTracker();  
    127.             break;  
    128.         }  
    129.         return true;  
    130.     }  
    131.       
    132.     private void createVelocityTracker(MotionEvent event) {  
    133.         if (mVelocityTracker == null) {  
    134.             mVelocityTracker = VelocityTracker.obtain();  
    135.         }  
    136.         mVelocityTracker.addMovement(event);  
    137.     }  
    138.       
    139.     //获取手指在View上的滑动速度,以每秒钟移动了多少像素值为单位  
    140.     private int getScrollVelocity() {  
    141.         mVelocityTracker.computeCurrentVelocity(1000);  
    142.         return (int) mVelocityTracker.getXVelocity();  
    143.     }  
    144.       
    145.     private void recycleVelocityTracker() {  
    146.         mVelocityTracker.recycle();  
    147.         mVelocityTracker = null;  
    148.     }  
    149.       
    150.     //打开Menu布局  
    151.     public void openMenu() {  
    152.         int curScrollX = getScrollX();  
    153.         scrollToDestination(-mMaxScrollX - curScrollX);  
    154.         isMenuOpen = true;  
    155.     }  
    156.       
    157.     //关闭Menu布局  
    158.     public void closeMenu() {  
    159.         int curScrollX = getScrollX();  
    160.         scrollToDestination(-curScrollX);  
    161.         isMenuOpen = false;  
    162.     }  
    163.       
    164.     private void scrollToDestination(int x) {  
    165.         if (x == 0)  
    166.             return;  
    167.   
    168.         mScroller.startScroll(getScrollX(), 0, x, 0, Math.abs(x));  
    169.         invalidate();  
    170.     }  
    171. }  


    最后是代码下载,Enjoy it!

    侧滑菜单实例

    侧滑菜单(修正版)  修改了1楼提到的bug

    侧滑菜单(平移版)

    Le王冬冬 博客分享地址: http://www.cnblogs.com/dongdong230/ 每个人都应做一天攻城狮
  • 相关阅读:
    .NET设计模式系列文章
    [转]给年轻工程师的十大忠告
    [你必须知道的.NET]第二十回:学习方法论
    写给开发者看的关系型数据库设计
    AjaxPro使用说明
    Spring.Net入门篇(一) [转]
    [从设计到架构] 必须知道的设计模式
    4月1日SharePoint Designer将开始免费
    12月累计更新的一个导出导入网站的问题在2月累计更新中修复了
    修复错误1093 “Unable to get the private bytes memory limit for the W3WP process”
  • 原文地址:https://www.cnblogs.com/dongdong230/p/4183047.html
Copyright © 2020-2023  润新知