侧滑菜单应用现在非常多,而且实现方式也多种多样。通过在网上的多方查找,我找到郭霖少侠的这篇文章:http://blog.csdn.net/guolin_blog/article/details/8744400,研究之后收获颇多。同时记得以前看过一篇讲Scroller实现滑屏的文章:http://www.cnblogs.com/wanqieddy/archive/2012/05/05/2484534.html

那为何不用scroller来实现以下侧滑菜单?闲的蛋疼,那就试试吧,在这里先感谢以上两篇博文给我的启发。

原理:通过scrollBy和scrollTo来移动右侧的content布局,实际上整个过程中,左侧的menu布局未发生滚动,这样出来的效果是右侧content布局覆盖住左侧menu布局。当然scroll的方式也可以实现menu和content同时平移的效果,这个只需要在布局文件上动动手脚就行了,在此先按下不表。

下面是本文的实现效果

activity_main.xml文件:

首先放入menu布局,因为是RelativeLayout布局因此先放入的会被覆盖,然后是滑动布局,在滑动布局中加入content布局,因为content布局会随滑动布局一起移动。

[html] view plaincopy

 
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. tools:context=".MainActivity" >
  6. <include android:id="@+id/menu" layout="@layout/menu" />
  7. <com.noter.layout.SlideLayout
  8. android:id="@+id/slide_layout"
  9. android:layout_width="fill_parent"
  10. android:layout_height="fill_parent" >
  11. <include android:id="@+id/content" layout="@layout/content_diary" />
  12. </com.noter.layout.SlideLayout>
  13. </RelativeLayout>

menu.xml和conent_diary.xml这两个布局文件就不用讲了,大家跟着感觉走,想放什么放什么吧。

SlideLayout.java文件:大部分代码都很简单,看注释就能懂。这里只说几个要点,我也是调试过之后才明白的:

1. getScrollX()得到的是当前View的最左边所在的X坐标。程序初始化后此值为0,View向右滑动后,相当于屏幕坐标系向负方向移动了一段,因此此时此值为负数;反之则相反。

2. Scroller实际上只是保存和提供自动滑动时所需的数值,真正完成滑动的还是scrollTo和scrollBy两个函数。

3. 我写的SlideLayout继承自RelativeLayout,其实也可以继承自ViewGroup,但是就需要自己重写onMeasure和onLayout函数,来布局子控件。这个实现中没有特殊的布局要求,所以用RelatiLayout就可以啦。

[java] view plaincopy

 
  1. package com.noter.layout;
  2. import android.content.Context;
  3. import android.graphics.Rect;
  4. import android.util.AttributeSet;
  5. import android.util.Log;
  6. import android.view.MotionEvent;
  7. import android.view.VelocityTracker;
  8. import android.view.View;
  9. import android.view.ViewConfiguration;
  10. import android.widget.RelativeLayout;
  11. import android.widget.Scroller;
  12. public class SlideLayout extends RelativeLayout {
  13. private static String TAG = "SlideMenuLayout";
  14. private Context mContext;
  15. private Scroller mScroller;    //Android 提供的滑动辅助类
  16. private int mTouchSlop = 0 ;    //在被判定为滚动之前用户手指可以移动的最大值
  17. private VelocityTracker mVelocityTracker;    //用于计算手指滑动的速度
  18. public static final int SNAP_VELOCITY = 200;    //滚动显示和隐藏左侧布局时,手指滑动需要达到的速度:每秒200个像素点
  19. private int mMaxScrollX = 0;    //最大滚动距离,等于menu的宽度
  20. public void setMaxScrollX(int maxScrollX) {
  21. this.mMaxScrollX = maxScrollX;
  22. }
  23. private float mDownX;    //一次按下抬起的动作中,按下时的X坐标,用于和抬起时的X比较,判断移动距离。少于mTouchSlop则判定为原地点击
  24. private float mLastX;    //记录滑动过程中的X坐标
  25. private boolean isMenuOpen = false;    //菜单界面是否被打开,只有完全打开才为true
  26. public boolean isMenuOpen() {
  27. return isMenuOpen;
  28. }
  29. private View mContent;
  30. public SlideLayout(Context context, AttributeSet attrs) {
  31. super(context, attrs);
  32. mContext = context;
  33. init();
  34. }
  35. private void init() {
  36. Log.v(TAG, "init start");
  37. mScroller = new Scroller(mContext);
  38. mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  39. }
  40. @Override
  41. public void computeScroll() {
  42. if (mScroller.computeScrollOffset()) {
  43. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  44. postInvalidate();
  45. }
  46. }
  47. @Override
  48. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  49. super.onLayout(changed, l, t, r, b);
  50. if(changed){
  51. mContent = getChildAt(0);
  52. }
  53. }
  54. @Override
  55. public boolean onTouchEvent(MotionEvent event) {
  56. createVelocityTracker(event);
  57. int curScrollX = getScrollX();
  58. // 检查触摸点是否在滑动布局(内容content)中,如果不是则返回false,即本View不处理该事件
  59. if (mContent != null) {
  60. Rect rect = new Rect();
  61. mContent.getHitRect(rect);
  62. if (!rect.contains((int)event.getX() + curScrollX, (int)event.getY())) {
  63. return false;
  64. }
  65. }
  66. float x = event.getX();    //取得本次event的X坐标
  67. switch (event.getAction()) {
  68. case MotionEvent.ACTION_DOWN:
  69. mDownX = x;
  70. mLastX = x;
  71. break;
  72. case MotionEvent.ACTION_MOVE:
  73. int deltaX = (int)(mLastX - x);
  74. if((curScrollX + deltaX) < -mMaxScrollX) {
  75. deltaX = -mMaxScrollX - curScrollX;
  76. }
  77. if((curScrollX + deltaX) > 0){
  78. deltaX = -curScrollX;
  79. }
  80. if (deltaX != 0) {
  81. scrollBy(deltaX, 0);
  82. }
  83. mLastX = x;
  84. break;
  85. case MotionEvent.ACTION_UP:
  86. int velocityX = getScrollVelocity();
  87. int offsetX = (int) (x - mDownX);
  88. //成立表明移动距离已经达到被判断为滑动的最低标准
  89. //不成立表明不被判断为滑动,则认为是单一的点击,则关闭menu
  90. if(Math.abs(offsetX) >= mTouchSlop) {
  91. //成立表明手指移动速度达标,则进行自动滑动;
  92. //不成立表明速度不达标,但仍然需要判断当前SlideLayout的位置
  93. //如果已经超过一半,则继续自动完成剩下的滑动,如果没有超过一半,则反向滑动
  94. if(Math.abs(velocityX) >= SNAP_VELOCITY) {
  95. if(velocityX > 0){
  96. openMenu();
  97. } else if(velocityX < 0) {
  98. closeMenu();
  99. }
  100. } else {
  101. if (curScrollX >= -mMaxScrollX / 2) {
  102. closeMenu();
  103. } else {
  104. openMenu();
  105. }
  106. }
  107. } else {
  108. closeMenu();
  109. }
  110. recycleVelocityTracker();
  111. break;
  112. }
  113. return true;
  114. }
  115. private void createVelocityTracker(MotionEvent event) {
  116. if (mVelocityTracker == null) {
  117. mVelocityTracker = VelocityTracker.obtain();
  118. }
  119. mVelocityTracker.addMovement(event);
  120. }
  121. //获取手指在View上的滑动速度,以每秒钟移动了多少像素值为单位
  122. private int getScrollVelocity() {
  123. mVelocityTracker.computeCurrentVelocity(1000);
  124. return (int) mVelocityTracker.getXVelocity();
  125. }
  126. private void recycleVelocityTracker() {
  127. mVelocityTracker.recycle();
  128. mVelocityTracker = null;
  129. }
  130. //打开Menu布局
  131. public void openMenu() {
  132. int curScrollX = getScrollX();
  133. scrollToDestination(-mMaxScrollX - curScrollX);
  134. isMenuOpen = true;
  135. }
  136. //关闭Menu布局
  137. public void closeMenu() {
  138. int curScrollX = getScrollX();
  139. scrollToDestination(-curScrollX);
  140. isMenuOpen = false;
  141. }
  142. private void scrollToDestination(int x) {
  143. if (x == 0)
  144. return;
  145. mScroller.startScroll(getScrollX(), 0, x, 0, Math.abs(x));
  146. invalidate();
  147. }
  148. }

最后是代码下载,Enjoy it!

侧滑菜单实例

侧滑菜单(修正版)  修改了1楼提到的bug

侧滑菜单(平移版)

【Android 界面效果31】Android--侧滑菜单应用的实现的更多相关文章

  1. Android 实现形态各异的双向侧滑菜单 自定义控件来袭

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935,本文出自:[张鸿洋的博客] 1.概述 关于自定义控件侧滑已经写了两 ...

  2. Android 实现形态各异的双向侧滑菜单 自定义控件来袭(转载)

    1.概述 关于自定义控件侧滑已经写了两篇了~~今天决定把之前的单向改成双向,当然了,单纯的改动之前的代码也没意思,今天不仅会把之前的单向改为双向,还会多添加一种侧滑效果,给大家带来若干种形态各异的双向 ...

  3. Xamarin.Android中使用ResideMenu实现侧滑菜单

    上次使用Xamarin.Android实现了一个比较常用的功能PullToRefresh,详情见:Xamarin. Android实现下拉刷新功能 这次将实现另外一个手机App中比较常用的功能:侧滑菜 ...

  4. android L 新控件侧滑菜单DrawerLayout 使用教程

    介绍 drawerLayout是Support Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产 ...

  5. 【Android 界面效果21】Android ViewPager使用详解

    这是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包囊了只有在安卓3.0以上可以使用的api.而viewpager就是其中之一利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等.那如 ...

  6. 【Android 界面效果18】Android软件开发之常用系统控件界面整理

    [java] view plaincopyprint?   <span style="font-size:18px">1.文本框TextView TextView的作用 ...

  7. 【Android 界面效果17】Android手机平板两不误,使用Fragment实现兼容手机和平板的程序

    记得我之前参与开发过一个华为的项目,要求程序可以支持好几种终端设备,其中就包括Android手机和Android Pad.然后为了节省人力,公司无节操地让Android手机和Android Pad都由 ...

  8. Android自定义顶部栏及侧滑菜单和fragment+viewpag滑动切换的实现

    嘿嘿嘿,关于android滑动的操作,是不是经常都会用到呢. 我肯定也要学习一下啦. https://blog.csdn.net/u013184970/article/details/82882107 ...

  9. 【Android 界面效果25】android中include标签的使用

    在一个项目中我们可能会需要用到相同的布局设计,如果都写在一个xml文件中,代码显得很冗余,并且可读性也很差,所以我们可以把相同布局的代码单独写成一个模块,然后用到的时候可以通过<include ...

随机推荐

  1. 1005. 继续(3n+1)猜想 (25)

    卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数.例如对n=3进行验证的时候, ...

  2. Linux coredump

    前面写过通过注册信号处理函数定位SEGV问题.其实Linux coredump机制也是比较好的debug手段. 进程由于某种异常或者bug导致在运行过程中异常退出或者中止,有时会产生一个叫做core的 ...

  3. iOS7 各种问题解决

    1 UITableView 行分割线不到头,短线问题 if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) { ...

  4. C:内存分配、内存中五大区

     1.内存的划分  (从高到低依次是: 栈区 . 堆区 .全局静态区 . 常量区 . 代码区 )栈区是系统自动回收,堆区是我们手动回收  2. 栈区   在函数内部定义的局部变量和数组.都存放在栈区, ...

  5. C:数组

    数组.排序 关于排序 :参考 关于数组: 参考 求a[i][j]行与列的和然后求平均值 参考 二维数组使用指针的表示方法  参考 字符串数组:char  name [5][20] ={ {} , {} ...

  6. 最基本的Unix系统操作命令

    基本知识点: OSX 采用的Unix文件系统,所有文件都挂在跟目录 / 下面,所以不在要有Windows 下的盘符概念. 你在桌面上看到的硬盘都挂在 /Volumes 下. 比如接上个叫做 USBHD ...

  7. 《高性能MySQL》

    <高性能MySQL>(第3版)讲解MySQL如何工作,为什么如此工作? MySQL系统架构.设计应用技巧.SQL语句优化.服务器性能调优.系统配置管理和安全设置.监控分析,以及复制.扩展和 ...

  8. EasyUI ComboBox默认值

    combobox数据加载完后设置默认值 $('#ck').combobox({ url: '/External/GetAllCk', valueField: 'Ddbh', textField: 'D ...

  9. (剑指Offer)面试题34:丑数

    题目: 把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 思路: 1. ...

  10. Ehcache(04)——设置缓存的大小

    http://haohaoxuexi.iteye.com/blog/2116749 设置缓存的大小 目录 1     CacheManager级别 2     Cache级别 3     大小衡量 4 ...