Android 记录3 Android滑动菜单特效实现

来源:互联网 发布:xnview mac 破解 编辑:程序博客网 时间:2024/06/05 05:14

                                                                                                                     Android滑动菜单特效实现

       首先谢谢大牛的开源精神,参考:http://blog.csdn.net/guolin_blog/article/details/8714621。

            废话少说,开始记录今天的学习情况。

            今天主要学习滑动菜单,在主页面手指向右边滑动,就可以将菜单显示出来,而主界面会被隐藏大部分,但是仍有左侧的一小部分同菜单一起展示。


     在贴代码之前,先去了解以下知识点:

    1. MotionEvent 的getX()和getRawX()的区别在哪里?

     getX()是表示Widget相对于自身左上角的x坐标,而getRawX()是表示相对于屏幕左上角的x坐标值(注意:这个屏幕左上角是手机屏幕左上角,不管activity是否有titleBar或是否全屏幕),getY(),getRawY()一样的道理

   

      开始整个功能的实现过程:

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

      2. 新建一个Android项目,修改layout文件  activity_main.xml,改为如下代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/main_layout"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal"    tools:context=".MainActivity" >    <LinearLayout        android:id="@+id/menu"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:orientation="horizontal"        android:background="@drawable/menu">    </LinearLayout>        <LinearLayout        android:id="@+id/content"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:orientation="horizontal"        android:background="@drawable/content">    </LinearLayout></LinearLayout>

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

      3. 在Activity 的加入如下代码:

package com.wty.demo.slidemenudemo;import android.os.AsyncTask;import android.os.Bundle;import android.app.Activity;import android.content.Context;import android.util.DisplayMetrics;import android.view.Menu;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.View.OnTouchListener;import android.view.WindowManager;import android.widget.LinearLayout;public class MainActivity extends Activity implements OnTouchListener{/* *屏幕宽度值  */private int screenWidth;/* * 主内容的布局 */private View content;/* * menu布局 */private View menu;private View main;/* * menu的布局参数,通过此参数来更改leftMargin的值 */private LinearLayout.LayoutParams menuParams;    /*     * menu完全显示时,menu右边边缘距离屏幕右边边缘的距离     */private int menuPadding = 100;/* * menu最多可以滑到的左边缘。由menu布局的宽度来定。 marginleft到达此值之后,不能再减少, 为负数 */private int leftEdge;/* * menu最多可以滑到的右边缘。值恒定为0,当marginleft到达0之后不能增加  */private int rightEdge = 0;/* * 手指按下时的横坐标 */private float xDown;/* * 手指移动时的横坐标 */private float xMove;/* * 手指抬起时的横坐标 */private float xUp;/* * 滚动显示和隐藏menu时,手指滑动需要达到的速度  */public static final int SNAP_VELOCITY = 200;/* * 用于计算手指滑动速度 */private VelocityTracker mVelocityTracker;    /*     *  menu当前是显示还是隐藏      */      private boolean isMenuVisible; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initValues();content.setOnTouchListener(this);//main.setOnTouchListener(this);//这样设置当显示menu的时候也可以在menu表面滑动}/* * 初始化一些关键数据,包括获取屏幕的宽度 * 给content布局重新设置宽度,给menu布局重新设置宽度以及偏移度等 * */private void initValues() {// TODO Auto-generated method stubWindowManager window = (WindowManager)getSystemService(Context.WINDOW_SERVICE);DisplayMetrics dm = new DisplayMetrics();window.getDefaultDisplay().getMetrics(dm);screenWidth = dm.widthPixels;main= findViewById(R.id.main_layout);content = findViewById(R.id.content);menu = findViewById(R.id.menu);menuParams = (LinearLayout.LayoutParams)menu.getLayoutParams();//将menu的宽度设置为屏幕宽度减去menupanddingmenuParams.width=screenWidth-menuPadding;// 左边缘的值赋值为menu宽度的负数leftEdge = -menuParams.width;// menu的leftmargin设置为左边缘的值,这样menu初始化就不可见menuParams.leftMargin = leftEdge;// content 宽度设置为屏幕宽度content.getLayoutParams().width = screenWidth;}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onTouch(View v, MotionEvent event) {if(/*v.getId()==R.id.content*/true){createVelocityTracker(event);switch (event.getAction()){case MotionEvent.ACTION_DOWN://手指按下时,记录按下的横坐标xDown = event.getRawX();break;case MotionEvent.ACTION_MOVE://手指移动时,对比按下时的横坐标,计算出移动的距离,//来调整menu的leftMargin值,从而显示或者隐藏menuxMove = event.getRawX();//移动的距离,从左到右为正,从右到左为负int distanceX=(int)(xMove-xDown);if(isMenuVisible){ //当menu是显示时,menu左边缘的距离就是rightEdge+distanceXmenuParams.leftMargin = rightEdge+distanceX;} else {//当menu隐藏的时候,左边缘的距离就是leftEdge+distanceXmenuParams.leftMargin = leftEdge+distanceX;}//对menu的左边缘距离进行有效性判断if(menuParams.leftMargin<leftEdge){ //menu左边缘已经滑动到最左边,不能再左了menuParams.leftMargin = leftEdge;} else if(menuParams.leftMargin>rightEdge){ //menu已经整个完全显示,menu不能再右menuParams.leftMargin=rightEdge;}menu.setLayoutParams(menuParams);break;case MotionEvent.ACTION_UP://手指抬起时,判断当前手势的意图,从而决定是滚动到menu界面还是滚动到content界面xUp = event.getRawX();if(wantToShowMenu()){if(shouldScrollToMenu()){scrollToMenu();}else{scrollToContent();}} else if(wantToShowContent()){if(shouldScrollToContent()){scrollToContent();}else{scrollToMenu();}}break;}}else if(v.getId()==R.id.menu){}// TODO Auto-generated method stubreturn true;}/* * 将屏幕滚动到content界面,滚动速度设定为-30 */private void scrollToContent() {// TODO Auto-generated method stubnew ScrollTask().execute(-30);}/* * 将屏幕滚动到menu界面,滚动速度设定为30. */private void scrollToMenu() {// TODO Auto-generated method stubnew ScrollTask().execute(30);}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);                  // 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。                  sleep(20);              }              if (speed[0] > 0) {                  isMenuVisible = true;              } else {                  isMenuVisible = false;              }              return leftMargin;          }            @Override          protected void onProgressUpdate(Integer... leftMargin) {              menuParams.leftMargin = leftMargin[0];              menu.setLayoutParams(menuParams);          }            @Override          protected void onPostExecute(Integer leftMargin) {              menuParams.leftMargin = leftMargin;              menu.setLayoutParams(menuParams);          }      }        /**      * 使当前线程睡眠指定的毫秒数。      *       * @param millis      *            指定当前线程睡眠多久,以毫秒为单位      */      private void sleep(long millis) {          try {              Thread.sleep(millis);          } catch (InterruptedException e) {              e.printStackTrace();          }      }/* * 判断是否应该滚动将menu显示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于定义的SNAP_VELOCITY  * 就认为应该滚动将menu展示出来 *  * @return 如果应该滚动将menu显示出来就返回true,否则返回false */private boolean shouldScrollToMenu() {// TODO Auto-generated method stubreturn xUp-xDown>screenWidth/2 || getScrollVelocity()>SNAP_VELOCITY;}/* * 判断是否应该滚动将content显示出来。如果手指移动距离加上menuPadding大于屏幕的1/2,或者手指移动速度大于定义的SNAP_VELOCITY  * 就认为应该滚动将content展示出来 *  * @return 如果应该滚动将content显示出来就返回true,否则返回false */private boolean shouldScrollToContent() {// TODO Auto-generated method stubreturn xDown-xUp+menuPadding>screenWidth/2 || getScrollVelocity()>SNAP_VELOCITY;}/* * 判断当前手势的意图是不是想显示menu.如果手指移动的距离是正数(从左到右滑),并且当前menu是不可见的, * 则认为当前手势是想要显示menu *   *  @return 当前手势想显示menu返回true,否则返回false */private boolean wantToShowMenu(){return xUp - xDown >0 && !isMenuVisible;}/* * 判断当前手势的意图是不是想显示content.如果手指移动的距离是负数(从右到左滑),并且当前menu是可见的, * 则认为当前手势是想要显示content *   *  @return 当前手势想显示content返回true,否则返回false */private boolean wantToShowContent(){return xUp-xDown <0 && isMenuVisible;}/* * 创建VelocityTracker对象,并将触摸content界面的滑动事件加入到VelocityTracker当中 *  * @param event *             content界面的滑动事件 */private void createVelocityTracker(MotionEvent event) {// TODO Auto-generated method stubif(mVelocityTracker == null ){    mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(event);}/* * 获得手指在content界面滑动的速度 * @return 滑动速度,以每秒钟移动多少个像素值为单位  */private int getScrollVelocity(){if(mVelocityTracker != null){//设置单位mVelocityTracker.computeCurrentVelocity(1000);int velocity = (int) mVelocityTracker.getXVelocity();return Math.abs(velocity);}return 0;}/* * 回收VelocityTracker对象  */private void recycleVelocityTracker(){if(mVelocityTracker != null){mVelocityTracker.recycle();mVelocityTracker = null;}}}

     解释一下代码流程:

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

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

      这里可以看到我们仅仅是针对 content进行了监听,所以当我们划出menu之后,在menu上滑动的话是不会有任何反应的。所以如果想在menu上滑动也有反应的话,可以监听整个layout 

main.setOnTouchListener(this);//这样设置当显示menu的时候也可以在menu表面滑动
    关于这个到此结束,今天记录到这里。
     



0 0
原创粉丝点击