首先我们看下面视图:
这种效果大家都不陌生,网上好多都说是仿人人网的,估计人家牛逼出来的早吧,我也参考了一一些例子,实现起来有三种方法,我下面简单介绍下:
方法一:其实就是对GestureDetector手势的应用及布局文件的设计.
布局文件main.xml 采用RelativeLayout布局.
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout 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 7 8 9 <LinearLayout 10 android:id="@+id/layout_right" 11 android:layout_width="fill_parent" 12 android:layout_height="fill_parent" 13 android:layout_marginLeft="50dp" 14 android:orientation="vertical" > 15 16 17 18 <AbsoluteLayout 19 android:layout_width="fill_parent" 20 android:layout_height="wrap_content" 21 android:background="@color/grey21" 22 android:padding="10dp" > 23 24 25 26 <TextView 27 android:layout_width="wrap_content" 28 android:layout_height="wrap_content" 29 android:text="设置" 30 android:textColor="@android:color/background_light" 31 android:textSize="20sp" /> 32 </AbsoluteLayout> 33 34 35 36 <ListView 37 android:id="@+id/lv_set" 38 android:layout_width="fill_parent" 39 android:layout_height="fill_parent" 40 android:layout_weight="1" > 41 </ListView> 42 </LinearLayout> 43 44 45 46 <LinearLayout 47 android:id="@+id/layout_left" 48 android:layout_width="fill_parent" 49 android:layout_height="fill_parent" 50 android:background="@color/white" 51 android:orientation="vertical" > 52 53 54 55 <RelativeLayout 56 android:layout_width="fill_parent" 57 android:layout_height="wrap_content" 58 android:background="@drawable/nav_bg" > 59 60 61 62 <ImageView 63 android:id="@+id/iv_set" 64 android:layout_width="wrap_content" 65 android:layout_height="wrap_content" 66 android:layout_alignParentRight="true" 67 android:layout_alignParentTop="true" 68 android:src="@drawable/nav_setting" /> 69 70 71 72 <TextView 73 android:layout_width="wrap_content" 74 android:layout_height="wrap_content" 75 android:layout_centerInParent="true" 76 android:text="我" 77 android:textColor="@android:color/background_light" 78 android:textSize="20sp" /> 79 </RelativeLayout> 80 81 82 83 <ImageView 84 android:id="@+id/iv_set" 85 android:layout_width="fill_parent" 86 android:layout_height="fill_parent" 87 android:scaleType="fitXY" 88 android:src="@drawable/bg_guide_5" /> 89 </LinearLayout> 90 91 92 93 </RelativeLayout>
layout_right:这个大布局文件,layout_left:距离左边50dp像素.(我们要移动的是layout_left).
看到这个图我想大家都很清晰了吧,其实:我们就是把layout_left这个布局控件整理向左移动,至于移动多少,就要看layout_right有多宽了。layout_left移动到距离左边的边距就是layout_right的宽及-MAX_WIDTH.相信大家都理解.
布局文件就介绍到这里,下面看代码.
1 /*** 2 * 初始化view 3 */ 4 void InitView() { 5 layout_left = (LinearLayout) findViewById(R.id.layout_left); 6 layout_right = (LinearLayout) findViewById(R.id.layout_right); 7 iv_set = (ImageView) findViewById(R.id.iv_set); 8 lv_set = (ListView) findViewById(R.id.lv_set); 9 lv_set.setAdapter(new ArrayAdapter<String>(this, R.layout.item, 10 R.id.tv_item, title)); 11 lv_set.setOnItemClickListener(new OnItemClickListener() { 12 13 14 15 @Override 16 public void onItemClick(AdapterView<?> parent, View view, 17 int position, long id) { 18 Toast.makeText(MainActivity.this, title[position], 1).show(); 19 } 20 }); 21 layout_left.setOnTouchListener(this); 22 iv_set.setOnTouchListener(this); 23 mGestureDetector = new GestureDetector(this); 24 // 禁用长按监听 25 mGestureDetector.setIsLongpressEnabled(false); 26 getMAX_WIDTH(); 27 } 28 29 这里要对手势进行监听,我想大家都知道怎么做,在这里我要说明一个方法: 30 31 /*** 32 * 获取移动距离 移动的距离其实就是layout_left的宽度 33 */ 34 void getMAX_WIDTH() { 35 ViewTreeObserver viewTreeObserver = layout_left.getViewTreeObserver(); 36 // 获取控件宽度 37 viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() { 38 @Override 39 public boolean onPreDraw() { 40 if (!hasMeasured) { 41 window_width = getWindowManager().getDefaultDisplay() 42 .getWidth(); 43 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 44 .getLayoutParams(); 45 layoutParams.width = window_width; 46 layout_left.setLayoutParams(layoutParams); 47 MAX_WIDTH = layout_right.getWidth(); 48 Log.v(TAG, "MAX_WIDTH=" + MAX_WIDTH + "width=" 49 + window_width); 50 hasMeasured = true; 51 } 52 return true; 53 } 54 }); 55 56 }
在这里我们要获取屏幕的宽度,并将屏幕宽度设置给layout_left这个控件,为什么要这么做呢,因为如果不把该控件宽度写死的话,那么系统将认为layout_left会根据不同环境宽度自动适应,也就是说我们通过layout_left.getLayoutParams动态移动该控件的时候,该控件会伸缩而不是移动。描述的有点模糊,大家请看下面示意图就明白了.
getLayoutParams可以很清楚看到,layout_left被向左拉伸了,并不是我们要的效果.
还有一种解决办法就是我们在配置文件中直接把layout_left宽度写死,不过这样不利于开发,因为分辨率的问题.因此就用ViewTreeObserver进行对layout_left设置宽度.
ViewTreeObserver,这个类主要用于对布局文件的监听.强烈建议同学们参考这篇文章 android ViewTreeObserver详细讲解,相信让你对ViewTreeObserver有更一步的了解.
其他的就是对GestureDetector手势的应用,下面我把代码贴出来:
1 package com.jj.slidingmenu; 2 3 4 5 import android.app.Activity; 6 import android.os.AsyncTask; 7 import android.os.Bundle; 8 import android.util.Log; 9 import android.view.GestureDetector; 10 import android.view.KeyEvent; 11 import android.view.MotionEvent; 12 import android.view.View; 13 import android.view.ViewTreeObserver; 14 import android.view.ViewTreeObserver.OnPreDrawListener; 15 import android.view.Window; 16 import android.view.View.OnTouchListener; 17 import android.widget.AdapterView; 18 import android.widget.AdapterView.OnItemClickListener; 19 import android.widget.ArrayAdapter; 20 import android.widget.ImageView; 21 import android.widget.LinearLayout; 22 import android.widget.ListView; 23 import android.widget.RelativeLayout; 24 import android.widget.Toast; 25 import android.widget.LinearLayout.LayoutParams; 26 27 28 29 /*** 30 * 滑动菜单 31 * 32 * @author jjhappyforever... 33 * 34 */ 35 public class MainActivity extends Activity implements OnTouchListener, 36 GestureDetector.OnGestureListener { 37 private boolean hasMeasured = false;// 是否Measured. 38 private LinearLayout layout_left; 39 private LinearLayout layout_right; 40 private ImageView iv_set; 41 private ListView lv_set; 42 43 44 45 /** 每次自动展开/收缩的范围 */ 46 private int MAX_WIDTH = 0; 47 /** 每次自动展开/收缩的速度 */ 48 private final static int SPEED = 30; 49 50 51 52 private GestureDetector mGestureDetector;// 手势 53 private boolean isScrolling = false; 54 private float mScrollX; // 滑块滑动距离 55 private int window_width;// 屏幕的宽度 56 57 58 59 private String TAG = "jj"; 60 61 62 63 private String title[] = { "待发送队列", "同步分享设置", "编辑我的资料", "找朋友", "告诉朋友", 64 "节省流量", "推送设置", "版本更新", "意见反馈", "积分兑换", "精品应用", "常见问题", "退出当前帐号" }; 65 66 67 68 /*** 69 * 初始化view 70 */ 71 void InitView() { 72 layout_left = (LinearLayout) findViewById(R.id.layout_left); 73 layout_right = (LinearLayout) findViewById(R.id.layout_right); 74 iv_set = (ImageView) findViewById(R.id.iv_set); 75 lv_set = (ListView) findViewById(R.id.lv_set); 76 lv_set.setAdapter(new ArrayAdapter<String>(this, R.layout.item, 77 R.id.tv_item, title)); 78 lv_set.setOnItemClickListener(new OnItemClickListener() { 79 80 81 82 @Override 83 public void onItemClick(AdapterView<?> parent, View view, 84 int position, long id) { 85 Toast.makeText(MainActivity.this, title[position], 1).show(); 86 } 87 }); 88 layout_left.setOnTouchListener(this); 89 iv_set.setOnTouchListener(this); 90 mGestureDetector = new GestureDetector(this); 91 // 禁用长按监听 92 mGestureDetector.setIsLongpressEnabled(false); 93 getMAX_WIDTH(); 94 } 95 96 97 98 /*** 99 * 获取移动距离 移动的距离其实就是layout_left的宽度 100 */ 101 void getMAX_WIDTH() { 102 ViewTreeObserver viewTreeObserver = layout_left.getViewTreeObserver(); 103 // 获取控件宽度 104 viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() { 105 @Override 106 public boolean onPreDraw() { 107 if (!hasMeasured) { 108 window_width = getWindowManager().getDefaultDisplay() 109 .getWidth(); 110 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 111 .getLayoutParams(); 112 // layoutParams.width = window_width; 113 layout_left.setLayoutParams(layoutParams); 114 MAX_WIDTH = layout_right.getWidth(); 115 Log.v(TAG, "MAX_WIDTH=" + MAX_WIDTH + "width=" 116 + window_width); 117 hasMeasured = true; 118 } 119 return true; 120 } 121 }); 122 123 124 125 } 126 127 128 129 @Override 130 public void onCreate(Bundle savedInstanceState) { 131 super.onCreate(savedInstanceState); 132 requestWindowFeature(Window.FEATURE_NO_TITLE); 133 setContentView(R.layout.main); 134 InitView(); 135 136 137 138 } 139 140 141 142 // 返回键 143 @Override 144 public boolean onKeyDown(int keyCode, KeyEvent event) { 145 if (KeyEvent.KEYCODE_BACK == keyCode && event.getRepeatCount() == 0) { 146 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 147 .getLayoutParams(); 148 if (layoutParams.leftMargin < 0) { 149 new AsynMove().execute(SPEED); 150 return false; 151 } 152 } 153 154 155 156 return super.onKeyDown(keyCode, event); 157 } 158 159 160 161 @Override 162 public boolean onTouch(View v, MotionEvent event) { 163 // 松开的时候要判断,如果不到半屏幕位子则缩回去, 164 if (MotionEvent.ACTION_UP == event.getAction() && isScrolling == true) { 165 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 166 .getLayoutParams(); 167 // 缩回去 168 if (layoutParams.leftMargin < -window_width / 2) { 169 new AsynMove().execute(-SPEED); 170 } else { 171 new AsynMove().execute(SPEED); 172 } 173 } 174 175 176 177 return mGestureDetector.onTouchEvent(event); 178 } 179 180 181 182 @Override 183 public boolean onDown(MotionEvent e) { 184 mScrollX = 0; 185 isScrolling = false; 186 // 将之改为true,不然事件不会向下传递. 187 return true; 188 } 189 190 191 192 @Override 193 public void onShowPress(MotionEvent e) { 194 195 196 197 } 198 199 200 201 /*** 202 * 点击松开执行 203 */ 204 @Override 205 public boolean onSingleTapUp(MotionEvent e) { 206 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 207 .getLayoutParams(); 208 // 左移动 209 if (layoutParams.leftMargin >= 0) { 210 new AsynMove().execute(-SPEED); 211 } else { 212 // 右移动 213 new AsynMove().execute(SPEED); 214 } 215 216 217 218 return true; 219 } 220 221 222 223 /*** 224 * e1 是起点,e2是终点,如果distanceX=e1.x-e2.x>0说明向左滑动。反之亦如此. 225 */ 226 @Override 227 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 228 float distanceY) { 229 isScrolling = true; 230 mScrollX += distanceX;// distanceX:向左为正,右为负 231 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 232 .getLayoutParams(); 233 layoutParams.leftMargin -= mScrollX; 234 if (layoutParams.leftMargin >= 0) { 235 isScrolling = false;// 拖过头了不需要再执行AsynMove了 236 layoutParams.leftMargin = 0; 237 238 239 240 } else if (layoutParams.leftMargin <= -MAX_WIDTH) { 241 // 拖过头了不需要再执行AsynMove了 242 isScrolling = false; 243 layoutParams.leftMargin = -MAX_WIDTH; 244 } 245 layout_left.setLayoutParams(layoutParams); 246 return false; 247 } 248 249 250 251 @Override 252 public void onLongPress(MotionEvent e) { 253 254 255 256 } 257 258 259 260 @Override 261 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 262 float velocityY) { 263 return false; 264 } 265 266 267 268 class AsynMove extends AsyncTask<Integer, Integer, Void> { 269 270 271 272 @Override 273 protected Void doInBackground(Integer... params) { 274 int times = 0; 275 if (MAX_WIDTH % Math.abs(params[0]) == 0)// 整除 276 times = MAX_WIDTH / Math.abs(params[0]); 277 else 278 times = MAX_WIDTH / Math.abs(params[0]) + 1;// 有余数 279 280 281 282 for (int i = 0; i < times; i++) { 283 publishProgress(params[0]); 284 try { 285 Thread.sleep(Math.abs(params[0])); 286 } catch (InterruptedException e) { 287 e.printStackTrace(); 288 } 289 } 290 291 292 293 return null; 294 } 295 296 297 298 /** 299 * update UI 300 */ 301 @Override 302 protected void onProgressUpdate(Integer... values) { 303 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left 304 .getLayoutParams(); 305 // 右移动 306 if (values[0] > 0) { 307 layoutParams.leftMargin = Math.min(layoutParams.leftMargin 308 + values[0], 0); 309 Log.v(TAG, "移动右" + layoutParams.rightMargin); 310 } else { 311 // 左移动 312 layoutParams.leftMargin = Math.max(layoutParams.leftMargin 313 + values[0], -MAX_WIDTH); 314 Log.v(TAG, "移动左" + layoutParams.rightMargin); 315 } 316 layout_left.setLayoutParams(layoutParams); 317 318 319 320 } 321 322 323 324 } 325 326 327 328 }
上面代码注释已经很明确,相信大家都看的明白,我就不过多解释了。
效果图:截屏出来有点卡,不过在手机虚拟机上是不卡的.
源码地址 仿人人网滑动菜单
http://www.eoeandroid.com/forum.php?mod=viewthread&tid=247442&fromuid=533640
怎么样,看着还行吧,我们在看下面一个示例:
简单说明一下,当你滑动的时候左边会跟着右边一起滑动,这个效果比上面那个酷吧,上面那个有点死板,其实实现起来也比较容易,只需要把我们上面那个稍微修改下,对layout_right也进行时时更新,这样就实现了这个效果了,如果上面那个理解了,这个很轻松就解决了,在这里我又遇到一个问题:此时的listview的item监听不到手势,意思就是我左右滑动listview他没有进行滑动。
本人对touch众多事件监听拦截等熟悉度不够,因此这里我用到自己写的方法,也许比较麻烦,如果有更好的解决办法,请大家一定要分享哦,再次 thanks for you 了.
具体解决办法:我们重写listview,对此listview进行手势监听,我们自定义一个接口来实现,具体代码如下:
1 package com.jj.slidingmenu; 2 3 4 5 import android.content.Context; 6 import android.util.AttributeSet; 7 import android.util.Log; 8 import android.view.GestureDetector; 9 import android.view.MotionEvent; 10 import android.view.GestureDetector.OnGestureListener; 11 import android.view.View; 12 import android.widget.ListView; 13 import android.widget.Toast; 14 15 16 17 public class MyListView extends ListView implements OnGestureListener { 18 19 20 21 private GestureDetector gd; 22 // 事件状态 23 public static final char FLING_CLICK = 0; 24 public static final char FLING_LEFT = 1; 25 public static final char FLING_RIGHT = 2; 26 public static char flingState = FLING_CLICK; 27 28 29 30 private float distanceX;// 水平滑动的距离 31 32 33 34 private MyListViewFling myListViewFling; 35 36 37 38 public static boolean isClick = false;// 是否可以点击 39 40 41 42 public void setMyListViewFling(MyListViewFling myListViewFling) { 43 this.myListViewFling = myListViewFling; 44 } 45 46 47 48 public float getDistanceX() { 49 return distanceX; 50 } 51 52 53 54 public char getFlingState() { 55 return flingState; 56 } 57 58 59 60 private Context context; 61 62 63 64 public MyListView(Context context) { 65 super(context); 66 67 68 69 } 70 71 72 73 public MyListView(Context context, AttributeSet attrs) { 74 super(context, attrs); 75 this.context = context; 76 gd = new GestureDetector(this); 77 } 78 79 80 81 /** 82 * 覆写此方法,以解决ListView滑动被屏蔽问题 83 */ 84 @Override 85 public boolean dispatchTouchEvent(MotionEvent event) { 86 myListViewFling.doFlingOver(event);// 回调执行完毕. 87 this.gd.onTouchEvent(event); 88 89 90 91 return super.dispatchTouchEvent(event); 92 } 93 94 95 96 @Override 97 public boolean onTouchEvent(MotionEvent ev) { 98 /*** 99 * 当移动的时候 100 */ 101 if (ev.getAction() == MotionEvent.ACTION_DOWN) 102 isClick = true; 103 if (ev.getAction() == MotionEvent.ACTION_MOVE) 104 isClick = false; 105 return super.onTouchEvent(ev); 106 } 107 108 109 110 @Override 111 public boolean onDown(MotionEvent e) { 112 int position = pointToPosition((int) e.getX(), (int) e.getY()); 113 if (position != ListView.INVALID_POSITION) { 114 View child = getChildAt(position - getFirstVisiblePosition()); 115 } 116 return true; 117 } 118 119 120 121 @Override 122 public void onShowPress(MotionEvent e) { 123 124 125 126 } 127 128 129 130 @Override 131 public boolean onSingleTapUp(MotionEvent e) { 132 133 134 135 return false; 136 } 137 138 139 140 @Override 141 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 142 float distanceY) { 143 // 左滑动 144 if (distanceX > 0) { 145 flingState = FLING_RIGHT; 146 Log.v("jj", "左distanceX=" + distanceX); 147 myListViewFling.doFlingLeft(distanceX);// 回调 148 // 右滑动. 149 } else if (distanceX < 0) { 150 flingState = FLING_LEFT; 151 Log.v("jj", "右distanceX=" + distanceX); 152 myListViewFling.doFlingRight(distanceX);// 回调 153 } 154 155 156 157 return false; 158 } 159 160 161 162 /*** 163 * 上下文菜单 164 */ 165 @Override 166 public void onLongPress(MotionEvent e) { 167 // System.out.println("Listview long press"); 168 // int position = pointToPosition((int) e.getX(), (int) e.getY()); 169 // if (position != ListView.INVALID_POSITION) { 170 // View child = getChildAt(position - getFirstVisiblePosition()); 171 // if (child != null) { 172 // showContextMenuForChild(child); 173 // this.requestFocusFromTouch(); 174 // } 175 // 176 // } 177 } 178 179 180 181 @Override 182 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 183 float velocityY) { 184 185 186 187 return false; 188 } 189 190 191 192 /*** 193 * 回调接口 194 * 195 * @author jjhappyforever... 196 * 197 */ 198 interface MyListViewFling { 199 void doFlingLeft(float distanceX);// 左滑动执行 200 201 202 203 void doFlingRight(float distanceX);// 右滑动执行 204 205 206 207 void doFlingOver(MotionEvent event);// 拖拽松开时执行 208 209 210 211 } 212 213 214 215 }
而在MainActivity.java里面实现该接口,我这么一说,我想有的同学们都明白了,具体实现起来代码有点多,我把代码上传到网上,大家可以下载后用心看,我想大家都能够明白的.(在这里我鄙视一下自己,肯定通过对手势监听拦截实现对listview的左右滑动,但是自己学业不经,再次再说一下,如有好的解决方案,请一定要分享我一下哦.)
另外有一个问题:当listivew超出一屏的时候,此时的listview滑动的时候可以上下左右一起滑动,在此没有解决这个问题,如有解决请分享我哦.
源码 仿人人网滑动菜单
http://www.eoeandroid.com/forum.php?mod=viewthread&tid=247442&fromuid=533640
地址:http://blog.csdn.net/jj120522/article/details/8075249
由于篇符较长,先说到这里,其实android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu也可以实现.具体参考下一篇文章:android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
先写到这里,有不足的地方请指出,
如果对您有帮助的话请记得赞一个哦. thanks for you。
贴2个老外的
http://stackoverflow.com/questions/8657894/android-facebook-style-slide