人人客户端有一个特效还是挺吸引人的,在主界面手指向右滑动,就可以将菜单展示出来,而主界面会被隐藏大部分,但是仍有左侧的一小部分同菜单一起展示。

据说人人客户端的这个特效是从facebook客户端模仿来的,至于facebook是不是又从其它地方模仿来的就不得而知了。好,今天我们就一起来实现这个效果,总之我第一次看到这个特效是在人人客户端看到的,我也就主观性地认为我是在模仿人人客户端的特效了。

虽然现在网上类似这种效果的实现也非常多,可是我发现实现方案大都非常复杂,并不容易理解。但其实这种效果并不难实现,因此我今天给大家带来的也是史上最简单的滑动菜单实现方案。

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

将菜单布局的左偏移值改成0时,效果图如下:

好,我们开始用代码来实现。首先在Eclipse中新建一个Android项目,项目名就叫做RenRenSlideMenuDemo。然后写一下布局文件,创建或打开layout目录下的activity_main.xml文件,加入如下代码:

  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:background="@drawable/menu" >
  12. </LinearLayout>
  13. <LinearLayout
  14. android:id="@+id/content"
  15. android:layout_width="fill_parent"
  16. android:layout_height="fill_parent"
  17. android:background="@drawable/content" >
  18. </LinearLayout>
  19. </LinearLayout>

这个布局文件的最外层布局是一个LinearLayout,排列方向是水平方向排列。这个LinearLayout下面嵌套了两个子 LinearLayout,分别就是菜单的布局和内容的布局。这里为了要让布局尽量简单,菜单布局和内容布局里面没有加入任何控件,只是给这两个布局各添 加了一张背景图片,这两张背景图片是我从人人客户端上截下来的图。这样我们可以把注意力都集中在如何实现滑动菜单的效果上面,不用关心里面各种复杂的布局 了。

创建或打开MainActivity,这个类仍然是程序的主Activity,也是这次demo唯一的Activity,在里面加入如下代码:

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

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

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

最后还是给出AndroidManifest.xml的代码,都是自动生成的,非常简单:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.renrenslidemenudemo"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="8"
  8. android:targetSdkVersion="8" />
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@drawable/ic_launcher"
  12. android:label="@string/app_name"
  13. android:theme="@android:style/Theme.NoTitleBar" >
  14. <activity
  15. android:name="com.example.renrenslidemenudemo.MainActivity"
  16. android:label="@string/app_name" >
  17. <intent-filter>
  18. <action android:name="android.intent.action.MAIN" />
  19. <category android:name="android.intent.category.LAUNCHER" />
  20. </intent-filter>
  21. </activity>
  22. </application>
  23. </manifest>

好了,现在我们运行一下,看一下效果吧,首先是程序刚打开的时候,显示的是内容布局。用手指在界面向右滑动,可以看到菜单布局出现。

                             

而当菜单布局完全展示的时候,效果如下图:

Android侧滑的更多相关文章

  1. Android侧滑菜单代码实现

    前两天学习了hyman老师讲的Android侧滑菜单的实现,经过自己的整理分享出来给大家学习一下 现在很多APP都有菜单侧滑的功能,本篇文章主要讲解使用自定义的HorizontalScrollView ...

  2. Android 侧滑菜单的简单实现(SlidingMenu)二

    在上一篇博文中已经简单的实现了侧滑菜单,代码也很简单,就几行代码. 这篇文章依然讲侧滑菜单,与前一篇文章不同的是,这篇文章用不同的代码方式来实现侧滑菜单. 在前面的文章中已经用了在Activity中通 ...

  3. Android 侧滑菜单的简单实现(SlidingMenu)

    在我还没有学习Android的时候就用过侧滑菜单的APP,当时第一个感觉是:哇塞,这效果不错!当然,现在自己都已经学Android了,这效果当然也要做出来啊~ SlidingMenu是一种比较新的设置 ...

  4. android侧滑菜单笔记

    一.SlidingPaneLayout v4包下的控件,使用简单,功能简洁.官方文档明确说明该控件只能左侧滑动.使用如下: <android.support.v4.widget.SlidingP ...

  5. 转 android 侧滑实现

    本篇博客给大家分享一个效果比较好的侧滑菜单的Demo,实现点击左边菜单切换Fragment. 效果如下: 主Activity代码: package com.infzm.slidingmenu.demo ...

  6. android侧滑删除,模仿qq跟进item显示删除按钮

    今天所写的代码只是为了个人以后查询方便,如果你参考了并且在使用中遇到问题也可以在这里直接回复我 SwipeDelMenuLayout: 效果图: item布局: <?xml version=&q ...

  7. 教你用DrawLayout 实现Android 侧滑菜单

    现在的APP越来越注重用户体验,百度视频客户端有一个特效还是挺吸引人的,在主界面手指向右滑动,就可以将菜单展示出来,而主界面会被隐藏大部分,但是仍有左侧的一小部分同菜单一起展示.类似的还有天天动听,人 ...

  8. android侧滑效果,SlidingMenu配置

    最近开始做点东西,需要用到android activity侧滑的效果.感觉配置起来还是有点小麻烦,总结一下以便回顾. 需要的东西: 1.SlidingMenu项目:点击打开链接 2.ActionBar ...

  9. android 侧滑菜单

    就是用手一滑才出现,占手机半个多屏幕的菜单.为了美观和页面转跳,很多时候要用到. 实现的话就是使用官方的DrawerLayout,注意这个布局一定要是最顶层的布局. 在DrawerLayout里面直接 ...

  10. android侧滑效果(引用官方网站提供的API文件)

    原文地址:http://www.cnblogs.com/android100/p/android-SlidingMenu.html 在新浪微博和唱吧里面都有看到android的侧滑效果,于是想要学习一 ...

随机推荐

  1. JS之script标签

    1.script标签的位置 script标签可以在head标签中,也可以在body标签中 2.async属性 async的目的是不让页面等待js文件的下载和执行,从而异步加载页面中的其他内容.只支持外 ...

  2. DHT(Distributed Hash Table) Translator

    DHT(Distributed Hash Table) Translator What is DHT? DHT is the real core of how GlusterFS aggregates ...

  3. JQuery: JQuery效果(隐藏、显示、切换,滑动,淡入淡出,以及动画)

    JQuery:效果 JQuery效果有很多,包括隐藏.显示.切换,滑动,淡入淡出,以及动画等.隐藏:JQuery hide() 显示:JQuery show() 切换:JQuery toggle() ...

  4. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

  5. 优化系统资源ulimit《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》

    优化系统资源ulimit<高性能Linux服务器构建实战:运维监控.性能调优与集群应用> 假设有这样一种情况,一台Linux 主机上同时登录了10个用户,在没有限制系统资源的情况下,这10 ...

  6. 正确统计SQLServer的慢日志

    RDS的一个富有吸引力的服务是为用户提供慢日志的运行状况报告.报告从不同的维度(总执行时间,总执行次数,总逻辑读,总物理读)为用户提供TOP20的SQL.RDS希望在为用户提供稳定,快速服务的同时,用 ...

  7. API文档的阅读

    API ——Application Programming Interface(应用程序编程接口) API是应用程序接口的意思,API是Java提供的基本编程接口,当使用Java语言进行编程时,不可能 ...

  8. DLL项目报错:fatal error lnk1104: cannot open file "...\xxx.dll"

    在生成DLL的时候报这个错, 生成不了DLL 检查生成DLL的路径是否有其他程序在使用... 或者把杀毒软件关了试试...

  9. Function.prototype.bind接口浅析

    本文大部分内容翻译自 MDN内容, 翻译内容经过自己的理解. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Glo ...

  10. Hadoop学习(3)-- 安装1.x版本

    Hadoop有三种安装模式,分别为单机模式.伪分布式模式和完全分布式模式(集群模式).本文安装版本是hadoop-1.1.2,hadoop-2.x版本安装请参考:http://www.cnblogs. ...