time 
设为首页】【收藏本站
当前位置: 主页 > 电脑网络 > 操作系统 > 嵌入式 > Android > Android一分钟实现滑动菜单特效

Android一分钟实现滑动菜单特效

时间:2014-06-18 10:47 点击:1023次 字体:[ ]




之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章 http://www.fengfly.com/plus/view-215082-1.html,因为我们今天要实现的滑动菜单框架也是基于同样的原理的。

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

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

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

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

  1. public class SlidingLayout extends LinearLayout implements OnTouchListener {  
  2.  
  3.     /**  
  4.      * 滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。  
  5.      */ 
  6.     public static final int SNAP_VELOCITY = 200;  
  7.  
  8.     /**  
  9.      * 屏幕宽度值。  
  10.      */ 
  11.     private int screenWidth;  
  12.  
  13.     /**  
  14.      * 左侧布局最多可以滑动到的左边缘。值由左侧布局的宽度来定,marginLeft到达此值之后,不能再减少。  
  15.      */ 
  16.     private int leftEdge;  
  17.  
  18.     /**  
  19.      * 左侧布局最多可以滑动到的右边缘。值恒为0,即marginLeft到达0之后,不能增加。  
  20.      */ 
  21.     private int rightEdge = 0;  
  22.  
  23.     /**  
  24.      * 左侧布局完全显示时,留给右侧布局的宽度值。  
  25.      */ 
  26.     private int leftLayoutPadding = 80;  
  27.  
  28.     /**  
  29.      * 记录手指按下时的横坐标。  
  30.      */ 
  31.     private float xDown;  
  32.  
  33.     /**  
  34.      * 记录手指移动时的横坐标。  
  35.      */ 
  36.     private float xMove;  
  37.  
  38.     /**  
  39.      * 记录手机抬起时的横坐标。  
  40.      */ 
  41.     private float xUp;  
  42.  
  43.     /**  
  44.      * 左侧布局当前是显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。  
  45.      */ 
  46.     private boolean isLeftLayoutVisible;  
  47.  
  48.     /**  
  49.      * 左侧布局对象。  
  50.      */ 
  51.     private View leftLayout;  
  52.  
  53.     /**  
  54.      * 右侧布局对象。  
  55.      */ 
  56.     private View rightLayout;  
  57.  
  58.     /**  
  59.      * 用于监听侧滑事件的View。  
  60.      */ 
  61.     private View mBindView;  
  62.  
  63.     /**  
  64.      * 左侧布局的参数,通过此参数来重新确定左侧布局的宽度,以及更改leftMargin的值。  
  65.      */ 
  66.     private MarginLayoutParams leftLayoutParams;  
  67.  
  68.     /**  
  69.      * 右侧布局的参数,通过此参数来重新确定右侧布局的宽度。  
  70.      */ 
  71.     private MarginLayoutParams rightLayoutParams;  
  72.  
  73.     /**  
  74.      * 用于计算手指滑动的速度。  
  75.      */ 
  76.     private VelocityTracker mVelocityTracker;  
  77.  
  78.     /**  
  79.      * 重写SlidingLayout的构造函数,其中获取了屏幕的宽度。  
  80.      *   
  81.      * @param context  
  82.      * @param attrs  
  83.      */ 
  84.     public SlidingLayout(Context context, AttributeSet attrs) {  
  85.         super(context, attrs);  
  86.         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
  87.         screenWidth = wm.getDefaultDisplay().getWidth();  
  88.     }  
  89.  
  90.     /**  
  91.      * 绑定监听侧滑事件的View,即在绑定的View进行滑动才可以显示和隐藏左侧布局。  
  92.      *   
  93.      * @param bindView  
  94.      *            需要绑定的View对象。  
  95.      */ 
  96.     public void setScrollEvent(View bindView) {  
  97.         mBindView = bindView;  
  98.         mBindView.setOnTouchListener(this);  
  99.     }  
  100.  
  101.     /**  
  102.      * 将屏幕滚动到左侧布局界面,滚动速度设定为30.  
  103.      */ 
  104.     public void scrollToLeftLayout() {  
  105.         new ScrollTask().execute(30);  
  106.     }  
  107.  
  108.     /**  
  109.      * 将屏幕滚动到右侧布局界面,滚动速度设定为-30.  
  110.      */ 
  111.     public void scrollToRightLayout() {  
  112.         new ScrollTask().execute(-30);  
  113.     }  
  114.  
  115.     /**  
  116.      * 左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效。  
  117.      *   
  118.      * @return 左侧布局完全显示返回true,完全隐藏返回false。  
  119.      */ 
  120.     public boolean isLeftLayoutVisible() {  
  121.         return isLeftLayoutVisible;  
  122.     }  
  123.  
  124.     /**  
  125.      * 在onLayout中重新设定左侧布局和右侧布局的参数。  
  126.      */ 
  127.     @Override 
  128.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  129.         super.onLayout(changed, l, t, r, b);  
  130.         if (changed) {  
  131.             // 获取左侧布局对象  
  132.             leftLayout = getChildAt(0);  
  133.             leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams();  
  134.             // 重置左侧布局对象的宽度为屏幕宽度减去leftLayoutPadding  
  135.             leftLayoutParams.width = screenWidth - leftLayoutPadding;  
  136.             // 设置最左边距为负的左侧布局的宽度  
  137.             leftEdge = -leftLayoutParams.width;  
  138.             leftLayoutParams.leftMargin = leftEdge;  
  139.             leftLayout.setLayoutParams(leftLayoutParams);  
  140.             // 获取右侧布局对象  
  141.             rightLayout = getChildAt(1);  
  142.             rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams();  
  143.             rightLayoutParams.width = screenWidth;  
  144.             rightLayout.setLayoutParams(rightLayoutParams);  
  145.         }  
  146.     }  
  147.  
  148.     @Override 
  149.     public boolean onTouch(View v, MotionEvent event) {  
  150.         createVelocityTracker(event);  
  151.         switch (event.getAction()) {  
  152.         case MotionEvent.ACTION_DOWN:  
  153.             // 手指按下时,记录按下时的横坐标  
  154.             xDown = event.getRawX();  
  155.             break;  
  156.         case MotionEvent.ACTION_MOVE:  
  157.             // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局  
  158.             xMove = event.getRawX();  
  159.             int distanceX = (int) (xMove - xDown);  
  160.             if (isLeftLayoutVisible) {  
  161.                 leftLayoutParams.leftMargin = distanceX;  
  162.             } else {  
  163.                 leftLayoutParams.leftMargin = leftEdge + distanceX;  
  164.             }  
  165.             if (leftLayoutParams.leftMargin < leftEdge) {  
  166.                 leftLayoutParams.leftMargin = leftEdge;  
  167.             } else if (leftLayoutParams.leftMargin > rightEdge) {  
  168.                 leftLayoutParams.leftMargin = rightEdge;  
  169.             }  
  170.             leftLayout.setLayoutParams(leftLayoutParams);  
  171.             break;  
  172.         case MotionEvent.ACTION_UP:  
  173.             // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局  
  174.             xUp = event.getRawX();  
  175.             if (wantToShowLeftLayout()) {  
  176.                 if (shouldScrollToLeftLayout()) {  
  177.                     scrollToLeftLayout();  
  178.                 } else {  
  179.                     scrollToRightLayout();  
  180.                 }  
  181.             } else if (wantToShowRightLayout()) {  
  182.                 if (shouldScrollToContent()) {  
  183.                     scrollToRightLayout();  
  184.                 } else {  
  185.                     scrollToLeftLayout();  
  186.                 }  
  187.             }  
  188.             recycleVelocityTracker();  
  189.             break;  
  190.         }  
  191.         return isBindBasicLayout();  
  192.     }  
  193.  
  194.     /**  
  195.      * 判断当前手势的意图是不是想显示右侧布局。如果手指移动的距离是负数,且当前左侧布局是可见的,则认为当前手势是想要显示右侧布局。  
  196.      *   
  197.      * @return 当前手势想显示右侧布局返回true,否则返回false。  
  198.      */ 
  199.     private boolean wantToShowRightLayout() {  
  200.         return xUp - xDown < 0 && isLeftLayoutVisible;  
  201.     }  
  202.  
  203.     /**  
  204.      * 判断当前手势的意图是不是想显示左侧布局。如果手指移动的距离是正数,且当前左侧布局是不可见的,则认为当前手势是想要显示左侧布局。  
  205.      *   
  206.      * @return 当前手势想显示左侧布局返回true,否则返回false。  
  207.      */ 
  208.     private boolean wantToShowLeftLayout() {  
  209.         return xUp - xDown > 0 && !isLeftLayoutVisible;  
  210.     }  
  211.  
  212.     /**  
  213.      * 判断是否应该滚动将左侧布局展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,  
  214.      * 就认为应该滚动将左侧布局展示出来。  
  215.      *   
  216.      * @return 如果应该滚动将左侧布局展示出来返回true,否则返回false。  
  217.      */ 
  218.     private boolean shouldScrollToLeftLayout() {  
  219.         return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;  
  220.     }  
  221.  
  222.     /**  
  223.      * 判断是否应该滚动将右侧布局展示出来。如果手指移动距离加上leftLayoutPadding大于屏幕的1/2,  
  224.      * 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将右侧布局展示出来。  
  225.      *   
  226.      * @return 如果应该滚动将右侧布局展示出来返回true,否则返回false。  
  227.      */ 
  228.     private boolean shouldScrollToContent() {  
  229.         return xDown - xUp + leftLayoutPadding > screenWidth / 2 
  230.                 || getScrollVelocity() > SNAP_VELOCITY;  
  231.     }  
  232.  
  233.     /**  
  234.      * 判断绑定滑动事件的View是不是一个基础layout,不支持自定义layout,只支持四种基本layout,  
  235.      * AbsoluteLayout已被弃用。  
  236.      *   
  237.      * @return 如果绑定滑动事件的View是LinearLayout,RelativeLayout,FrameLayout,  
  238.      *         TableLayout之一就返回true,否则返回false。  
  239.      */ 
  240.     private boolean isBindBasicLayout() {  
  241.         if (mBindView == null) {  
  242.             return false;  
  243.         }  
  244.         String viewName = mBindView.getClass().getName();  
  245.         return viewName.equals(LinearLayout.class.getName())  
  246.                 || viewName.equals(RelativeLayout.class.getName())  
  247.                 || viewName.equals(FrameLayout.class.getName())  
  248.                 || viewName.equals(TableLayout.class.getName());  
  249.     }  
  250.  
  251.     /**  
  252.      * 创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。  
  253.      *   
  254.      * @param event  
  255.      *            右侧布局监听控件的滑动事件  
  256.      */ 
  257.     private void createVelocityTracker(MotionEvent event) {  
  258.         if (mVelocityTracker == null) {  
  259.             mVelocityTracker = VelocityTracker.obtain();  
  260.         }  
  261.         mVelocityTracker.addMovement(event);  
  262.     }  
  263.  
  264.     /**  
  265.      * 获取手指在右侧布局的监听View上的滑动速度。  
  266.      *   
  267.      * @return 滑动速度,以每秒钟移动了多少像素值为单位。  
  268.      */ 
  269.     private int getScrollVelocity() {  
  270.         mVelocityTracker.computeCurrentVelocity(1000);  
  271.         int velocity = (int) mVelocityTracker.getXVelocity();  
  272.         return Math.abs(velocity);  
  273.     }  
  274.  
  275.     /**  
  276.      * 回收VelocityTracker对象。  
  277.      */ 
  278.     private void recycleVelocityTracker() {  
  279.         mVelocityTracker.recycle();  
  280.         mVelocityTracker = null;  
  281.     }  
  282.  
  283.     class ScrollTask extends AsyncTask<Integer, Integer, Integer> {  
  284.  
  285.         @Override 
  286.         protected Integer doInBackground(Integer... speed) {  
  287.             int leftMargin = leftLayoutParams.leftMargin;  
  288.             // 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。  
  289.             while (true) {  
  290.                 leftMargin = leftMargin + speed[0];  
  291.                 if (leftMargin > rightEdge) {  
  292.                     leftMargin = rightEdge;  
  293.                     break;  
  294.                 }  
  295.                 if (leftMargin < leftEdge) {  
  296.                     leftMargin = leftEdge;  
  297.                     break;  
  298.                 }  
  299.                 publishProgress(leftMargin);  
  300.                 // 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。  
  301.                 sleep(20);  
  302.             }  
  303.             if (speed[0] > 0) {  
  304.                 isLeftLayoutVisible = true;  
  305.             } else {  
  306.                 isLeftLayoutVisible = false;  
  307.             }  
  308.             return leftMargin;  
  309.         }  
  310.  
  311.         @Override 
  312.         protected void onProgressUpdate(Integer... leftMargin) {  
  313.             leftLayoutParams.leftMargin = leftMargin[0];  
  314.             leftLayout.setLayoutParams(leftLayoutParams);  
  315.         }  
  316.  
  317.         @Override 
  318.         protected void onPostExecute(Integer leftMargin) {  
  319.             leftLayoutParams.leftMargin = leftMargin;  
  320.             leftLayout.setLayoutParams(leftLayoutParams);  
  321.         }  
  322.     }  
  323.  
  324.     /**  
  325.      * 使当前线程睡眠指定的毫秒数。  
  326.      *   
  327.      * @param millis  
  328.      *            指定当前线程睡眠多久,以毫秒为单位  
  329.      */ 
  330.     private void sleep(long millis) {  
  331.         try {  
  332.             Thread.sleep(millis);  
  333.         } catch (InterruptedException e) {  
  334.             e.printStackTrace();  
  335.         }  
  336.     }  
  337. }  

看到这里,我相信大家一定会觉得这些代码非常熟悉。没错,基本上这些代码和之前那篇文章的代码大同小异,只不过以前这些代码是写在Activity里的,而现在我们移动到了自定义的View当中。



本文地址 : http://www.fengfly.com/plus/view-215084-1.html
标签: Android
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码: