侧滑、listView中折叠效果的简单实现方法

   

activity_main.xml:

 

[html][/html] view plaincopyprint?

  1. <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  2.     xmlns:tools=”http://schemas.android.com/tools”
  3.     android:layout_width=”fill_parent”
  4.     android:layout_height=”fill_parent”
  5.     android:orientation=”horizontal”
  6.     tools:context=”.MainActivity” >
  7.     <LinearLayout
  8.         android:id=”@+id/menu”
  9.         android:layout_width=”fill_parent”
  10.         android:layout_height=”fill_parent”
  11.         android:orientation=”vertical”
  12.         android:background=”#dddddddd”
  13.         >
  14.         <ListView
  15.             android:id=”@+id/list_menu”
  16.             android:layout_width=”fill_parent”
  17.             android:layout_height=”fill_parent”
  18.             android:cacheColorHint=”#00000000″
  19.             android:orientation=”vertical” >
  20.         </ListView>
  21.     </LinearLayout>
  22.     <LinearLayout
  23.         android:id=”@+id/content”
  24.         android:layout_width=”fill_parent”
  25.         android:layout_height=”fill_parent”
  26.         android:background=”#ffff0000″
  27.         android:orientation=”vertical” >
  28.         <ListView
  29.             android:id=”@+id/list_content”
  30.             android:layout_width=”fill_parent”
  31.             android:layout_height=”wrap_content”
  32.             android:cacheColorHint=”#00000000″
  33.             android:orientation=”vertical” >
  34.         </ListView>
  35.     </LinearLayout>
  36. </LinearLayout>

 

 

text.xml:

[html][/html] view plaincopyprint?

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  3.     android:layout_width=”fill_parent”
  4.     android:layout_height=”fill_parent”
  5.     android:orientation=”vertical” >
  6.     <TextView
  7.         android:id=”@+id/text1″
  8.         android:layout_width=”fill_parent”
  9.         android:layout_height=”30dp”
  10.         android:textColor=”#ff00ff00″
  11.         />
  12.      <TextView
  13.         android:id=”@+id/text2″
  14.         android:layout_width=”fill_parent”
  15.         android:layout_height=”wrap_content”
  16.         android:textColor=”#ffffffff”
  17.         android:visibility=”gone”
  18.         />
  19. </LinearLayout>

MainActivity.java:

 

[java][/java] view plaincopyprint?

  1. package com.example.renrenslidemenudemo;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import android.app.Activity;
  6. import android.content.Context;
  7. import android.os.AsyncTask;
  8. import android.os.Bundle;
  9. import android.util.Log;
  10. import android.view.MotionEvent;
  11. import android.view.VelocityTracker;
  12. import android.view.View;
  13. import android.view.View.OnTouchListener;
  14. import android.view.WindowManager;
  15. import android.widget.AdapterView;
  16. import android.widget.AdapterView.OnItemClickListener;
  17. import android.widget.ArrayAdapter;
  18. import android.widget.LinearLayout;
  19. import android.widget.ListView;
  20. import android.widget.SimpleAdapter;
  21. import android.widget.TextView;
  22. public class MainActivity extends Activity implements OnTouchListener {
  23.     /**
  24.      * 滚动显示和隐藏menu时,手指滑动需要达到的速度。
  25.      */
  26.     public static final int SNAP_VELOCITY = 200;
  27.     /**
  28.      * 屏幕宽度值。
  29.      */
  30.     private int screenWidth;
  31.     /**
  32.      * menu最多可以滑动到的左边缘。值由menu布局的宽度来定,marginLeft到达此值之后,不能再减少。
  33.      */
  34.     private int leftEdge;
  35.     /**
  36.      * menu最多可以滑动到的右边缘。值恒为0,即marginLeft到达0之后,不能增加。
  37.      */
  38.     private int rightEdge = 0;
  39.     /**
  40.      * menu完全显示时,留给content的宽度值。
  41.      */
  42.     private int menuPadding = 80;
  43.     /**
  44.      * 主内容的布局。
  45.      */
  46.     private View content;
  47.     private ListView listContent;
  48.     /**
  49.      * menu的布局。
  50.      */
  51.     private View menu;
  52.     private ListView listMenu;
  53.     /**
  54.      * menu布局的参数,通过此参数来更改leftMargin的值。
  55.      */
  56.     private LinearLayout.LayoutParams menuParams;
  57.     /**
  58.      * 记录手指按下时的横坐标。
  59.      */
  60.     private float xDown;
  61.     /**
  62.      * 记录手指移动时的横坐标。
  63.      */
  64.     private float xMove;
  65.     /**
  66.      * 记录手机抬起时的横坐标。
  67.      */
  68.     private float xUp;
  69.     /**
  70.      * menu当前是显示还是隐藏。只有完全显示或隐藏menu时才会更改此值,滑动过程中此值无效。
  71.      */
  72.     private boolean isMenuVisible;
  73.     /**
  74.      * 用于计算手指滑动的速度。
  75.      */
  76.     private VelocityTracker mVelocityTracker;
  77.     @Override
  78.     protected void onCreate(Bundle savedInstanceState) {
  79.         super.onCreate(savedInstanceState);
  80.         setContentView(R.layout.activity_main);
  81.         initValues();
  82.         content.setOnTouchListener(this);
  83.         boolean flag = false;
  84.         listContent.setOnItemClickListener(new OnItemClickListener() {
  85.             @Override
  86.             public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
  87.                     long arg3) {
  88.                 // TODO Auto-generated method stub
  89.                 TextView text2 = (TextView) arg1.findViewById(R.id.text2);
  90.                 if (text2.getVisibility() == View.GONE)
  91.                     text2.setVisibility(View.VISIBLE);
  92.                 else
  93.                     text2.setVisibility(View.GONE);
  94.             }
  95.         });
  96.     }
  97.     /**
  98.      * 初始化一些关键性数据。包括获取屏幕的宽度,给content布局重新设置宽度,给menu布局重新设置宽度和偏移距离等。
  99.      */
  100.     private void initValues() {
  101.         WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
  102.         screenWidth = window.getDefaultDisplay().getWidth();
  103.         content = findViewById(R.id.content);
  104.         listContent = (ListView) findViewById(R.id.list_content);
  105.         listMenu = (ListView) findViewById(R.id.list_menu);
  106.         List<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
  107.         for (int i = 0; i < 15; i++) {
  108.             HashMap<String, String> map = new HashMap<String, String>();
  109.             map.put(“title”, “侧滑、折叠效果 0” + i);
  110.             map.put(“title1”,
  111.                     “初始化一些关键性数据。包括获取屏幕的宽度,给content布局重新设置宽度,给menu布局重新设置宽度和偏移距离,初始化一些关键性数据。包括获取屏幕的宽度,给content布局重新设置宽度,给menu布局重新设置宽度和偏移距离,初始化一些关键性数据。包括获取屏幕的宽度,给content布局重新设置宽度,给menu布局重新设置宽度和偏移距离”);
  112.             list.add(map);
  113.         }
  114.         SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.text,
  115.                 new String[] { “title”, “title1” }, new int[] { R.id.text1,
  116.                         R.id.text2 });
  117.         listContent.setAdapter(adapter);
  118.         menu = findViewById(R.id.menu);
  119.         List<String> list1 = new ArrayList<String>();
  120.         list1.add(“内容1”);
  121.         list1.add(“内容2”);
  122.         list1.add(“内容3”);
  123.         list1.add(“内容4”);
  124.         ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(this,
  125.                 android.R.layout.simple_list_item_1, list1);
  126.         listMenu.setAdapter(adapter1);
  127.         menuParams = (LinearLayout.LayoutParams) menu.getLayoutParams();
  128.         // 将menu的宽度设置为屏幕宽度减去menuPadding
  129.         menuParams.width = screenWidth – menuPadding;
  130.         // 左边缘的值赋值为menu宽度的负数
  131.         leftEdge = -menuParams.width;
  132.         // menu的leftMargin设置为左边缘的值,这样初始化时menu就变为不可见
  133.         menuParams.leftMargin = leftEdge;
  134.         // 将content的宽度设置为屏幕宽度
  135.         content.getLayoutParams().width = screenWidth;
  136.     }
  137.     @Override
  138.     public boolean onTouch(View v, MotionEvent event) {
  139.         createVelocityTracker(event);
  140.         switch (event.getAction()) {
  141.         case MotionEvent.ACTION_DOWN:
  142.             // 手指按下时,记录按下时的横坐标
  143.             xDown = event.getRawX();
  144.             break;
  145.         case MotionEvent.ACTION_MOVE:
  146.             // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整menu的leftMargin值,从而显示和隐藏menu
  147.             xMove = event.getRawX();
  148.             int distanceX = (int) (xMove – xDown);
  149.             if (isMenuVisible) {
  150.                 menuParams.leftMargin = distanceX;
  151.             } else {
  152.                 menuParams.leftMargin = leftEdge + distanceX;
  153.             }
  154.             if (menuParams.leftMargin < leftEdge) {
  155.                 menuParams.leftMargin = leftEdge;
  156.             } else if (menuParams.leftMargin > rightEdge) {
  157.                 menuParams.leftMargin = rightEdge;
  158.             }
  159.             Log.i(“Left Margin”, menuParams.leftMargin + “”);
  160.             menu.setLayoutParams(menuParams);
  161.             break;
  162.         case MotionEvent.ACTION_UP:
  163.             // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到menu界面,还是滚动到content界面
  164.             xUp = event.getRawX();
  165.             if (wantToShowMenu()) {
  166.                 if (shouldScrollToMenu()) {
  167.                     scrollToMenu();
  168.                 } else {
  169.                     scrollToContent();
  170.                 }
  171.             } else if (wantToShowContent()) {
  172.                 if (shouldScrollToContent()) {
  173.                     scrollToContent();
  174.                 } else {
  175.                     scrollToMenu();
  176.                 }
  177.             }
  178.             recycleVelocityTracker();
  179.             break;
  180.         }
  181.         return true;
  182.     }
  183.     /**
  184.      * 判断当前手势的意图是不是想显示content。如果手指移动的距离是负数,且当前menu是可见的,则认为当前手势是想要显示content。
  185.      *
  186.      * @return 当前手势想显示content返回true,否则返回false。
  187.      */
  188.     private boolean wantToShowContent() {
  189.         return xUp – xDown < 0 && isMenuVisible;
  190.     }
  191.     /**
  192.      * 判断当前手势的意图是不是想显示menu。如果手指移动的距离是正数,且当前menu是不可见的,则认为当前手势是想要显示menu。
  193.      *
  194.      * @return 当前手势想显示menu返回true,否则返回false。
  195.      */
  196.     private boolean wantToShowMenu() {
  197.         return xUp – xDown > 0 && !isMenuVisible;
  198.     }
  199.     /**
  200.      * 判断是否应该滚动将menu展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
  201.      * 就认为应该滚动将menu展示出来。
  202.      *
  203.      * @return 如果应该滚动将menu展示出来返回true,否则返回false。
  204.      */
  205.     private boolean shouldScrollToMenu() {
  206.         return xUp – xDown > screenWidth / 2
  207.                 || getScrollVelocity() > SNAP_VELOCITY;
  208.     }
  209.     /**
  210.      * 判断是否应该滚动将content展示出来。如果手指移动距离加上menuPadding大于屏幕的1/2,
  211.      * 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将content展示出来。
  212.      *
  213.      * @return 如果应该滚动将content展示出来返回true,否则返回false。
  214.      */
  215.     private boolean shouldScrollToContent() {
  216.         return xDown – xUp + menuPadding > screenWidth / 2
  217.                 || getScrollVelocity() > SNAP_VELOCITY;
  218.     }
  219.     /**
  220.      * 将屏幕滚动到menu界面,滚动速度设定为30.
  221.      */
  222.     private void scrollToMenu() {
  223.         new ScrollTask().execute(30);
  224.     }
  225.     /**
  226.      * 将屏幕滚动到content界面,滚动速度设定为-30.
  227.      */
  228.     private void scrollToContent() {
  229.         new ScrollTask().execute(-30);
  230.     }
  231.     /**
  232.      * 创建VelocityTracker对象,并将触摸content界面的滑动事件加入到VelocityTracker当中。
  233.      *
  234.      * @param event
  235.      *            content界面的滑动事件
  236.      */
  237.     private void createVelocityTracker(MotionEvent event) {
  238.         if (mVelocityTracker == null) {
  239.             mVelocityTracker = VelocityTracker.obtain();
  240.         }
  241.         mVelocityTracker.addMovement(event);
  242.     }
  243.     /**
  244.      * 获取手指在content界面滑动的速度。
  245.      *
  246.      * @return 滑动速度,以每秒钟移动了多少像素值为单位。
  247.      */
  248.     private int getScrollVelocity() {
  249.         mVelocityTracker.computeCurrentVelocity(1000);
  250.         int velocity = (int) mVelocityTracker.getXVelocity();
  251.         return Math.abs(velocity);
  252.     }
  253.     /**
  254.      * 回收VelocityTracker对象。
  255.      */
  256.     private void recycleVelocityTracker() {
  257.         mVelocityTracker.recycle();
  258.         mVelocityTracker = null;
  259.     }
  260.     class ScrollTask extends AsyncTask<Integer, Integer, Integer> {
  261.         @Override
  262.         protected Integer doInBackground(Integer… speed) {
  263.             int leftMargin = menuParams.leftMargin;
  264.             // 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。
  265.             while (true) {
  266.                 leftMargin = leftMargin + speed[0];
  267.                 if (leftMargin > rightEdge) {
  268.                     leftMargin = rightEdge;
  269.                     break;
  270.                 }
  271.                 if (leftMargin < leftEdge) {
  272.                     leftMargin = leftEdge;
  273.                     break;
  274.                 }
  275.                 publishProgress(leftMargin);
  276.                 // 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。
  277.                 sleep(20);
  278.             }
  279.             if (speed[0] > 0) {
  280.                 isMenuVisible = true;
  281.             } else {
  282.                 isMenuVisible = false;
  283.             }
  284.             return leftMargin;
  285.         }
  286.         @Override
  287.         protected void onProgressUpdate(Integer… leftMargin) {
  288.             menuParams.leftMargin = leftMargin[0];
  289.             menu.setLayoutParams(menuParams);
  290.         }
  291.         @Override
  292.         protected void onPostExecute(Integer leftMargin) {
  293.             menuParams.leftMargin = leftMargin;
  294.             menu.setLayoutParams(menuParams);
  295.         }
  296.     }
  297.     /**
  298.      * 使当前线程睡眠指定的毫秒数。
  299.      *
  300.      * @param millis
  301.      *            指定当前线程睡眠多久,以毫秒为单位
  302.      */
  303.     private void sleep(long millis) {
  304.         try {
  305.             Thread.sleep(millis);
  306.         } catch (InterruptedException e) {
  307.             e.printStackTrace();
  308.         }
  309.     }
  310. }

标签