1、概述

关于自定义控件侧滑已经写了两篇了~~今天决定把之前的单向改成双向,当然了,单纯的改动之前的代码也没意思,今天不仅会把之前的单向改为双向,还会多添加一种侧滑效果,给大家带来若干种形态各异的双向侧滑菜单,不过请放心,代码会很简单~~然后根据这若干种,只要你喜欢,相信你可以打造任何绚(bian)丽(tai)效果的双向侧滑菜单~~

首先回顾一下,之前写过的各种侧滑菜单,为了不占据篇幅,就不贴图片了:

1、最普通的侧滑效果,请参考:Android 自定义控件打造史上最简单的侧滑菜单

2、仿QQ5.0侧滑效果,请参考:Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭

3、菜单在内容之后的侧滑效果,请参考:Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭

2、目标效果


1、最普通的双向侧滑


是不是很模糊,嗯,没办法,电脑显卡弱。。。。

2、抽屉式双向侧滑


3、菜单在内容之下的双向侧滑


凑合看下,文章最后会提供源码下载,大家可以安装体验一下~

所有的代码的内容区域都是一个ListView,两侧菜单都包含按钮,基本的冲突都检测过~~~当然如果有bug在所难免,请直接留言;如果你解决了某些未知bug,希望你也可以留言,或许可以帮助到其他人~~

下面就开始我们的代码了。


3、代码是最好的老师

1、布局文件

既然是双向菜单,那么我们的布局文件是这样的:

[html] view plain copy
  1. <com.zhy.view.BinarySlidingMenuxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu02"
  4. android:id="@+id/id_menu"
  5. android:layout_width="wrap_content"
  6. android:layout_height="fill_parent"
  7. android:scrollbars="none"
  8. zhy:rightPadding="100dp">
  9. <LinearLayout
  10. android:layout_width="wrap_content"
  11. android:layout_height="fill_parent"
  12. android:orientation="horizontal">
  13. <includelayout="@layout/layout_menu"/>
  14. <LinearLayout
  15. android:layout_width="fill_parent"
  16. android:layout_height="fill_parent"
  17. android:background="@drawable/eee"
  18. android:gravity="center"
  19. android:orientation="horizontal">
  20. <ListView
  21. android:id="@android:id/list"
  22. android:layout_width="fill_parent"
  23. android:layout_height="fill_parent">
  24. </ListView>
  25. </LinearLayout>
  26. <includelayout="@layout/layout_menu2"/>
  27. </LinearLayout>
  28. </com.zhy.view.BinarySlidingMenu>


最外层是我们的自定义的BinarySlidingMenu,内部一个水平方向的LinearLayout,然后是左边的菜单,内容区域,右边的菜单布局~~

关键就是我们的BinarySlidingMenu

2、BinarySlidingMenu的构造方法

[java] view plain copy
  1. /**
  2. *屏幕宽度
  3. */
  4. privateintmScreenWidth;
  5. /**
  6. *dp菜单距离屏幕的右边距
  7. */
  8. privateintmMenuRightPadding;
  9. publicBinarySlidingMenu(Contextcontext,AttributeSetattrs,intdefStyle)
  10. {
  11. super(context,attrs,defStyle);
  12. mScreenWidth=ScreenUtils.getScreenWidth(context);
  13. TypedArraya=context.getTheme().obtainStyledAttributes(attrs,
  14. R.styleable.BinarySlidingMenu,defStyle,0);
  15. intn=a.getIndexCount();
  16. for(inti=0;i<n;i++)
  17. {
  18. intattr=a.getIndex(i);
  19. switch(attr)
  20. {
  21. caseR.styleable.BinarySlidingMenu_rightPadding:
  22. //默认50
  23. mMenuRightPadding=a.getDimensionPixelSize(attr,
  24. (int)TypedValue.applyDimension(
  25. TypedValue.COMPLEX_UNIT_DIP,50f,
  26. getResources().getDisplayMetrics()));//默认为10DP
  27. break;
  28. }
  29. }
  30. a.recycle();
  31. }
我们在构造方法中,获取我们自定义的一个属性rightPadding,然后赋值给我们的成员变量mMenuRightPadding;关于如何自定义属性参考侧滑菜单的第一篇博文,这里就不赘述了。

3、onMeasure

onMeasure中肯定是对侧滑菜单的宽度、高度等进行设置:

[java] view plain copy
  1. @Override
  2. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)
  3. {
  4. /**
  5. *显示的设置一个宽度
  6. */
  7. if(!once)
  8. {
  9. mWrapper=(LinearLayout)getChildAt(0);
  10. mLeftMenu=(ViewGroup)mWrapper.getChildAt(0);
  11. mContent=(ViewGroup)mWrapper.getChildAt(1);
  12. mRightMenu=(ViewGroup)mWrapper.getChildAt(2);
  13. mMenuWidth=mScreenWidth-mMenuRightPadding;
  14. mHalfMenuWidth=mMenuWidth/2;
  15. mLeftMenu.getLayoutParams().width=mMenuWidth;
  16. mContent.getLayoutParams().width=mScreenWidth;
  17. mRightMenu.getLayoutParams().width=mMenuWidth;
  18. }
  19. super.onMeasure(widthMeasureSpec,heightMeasureSpec);
  20. }

可以看到我们分别给左侧、右侧的菜单设置了宽度(mScreenWidth - mMenuRightPadding);

宽度设置完成以后,肯定就是定位了,把左边的菜单弄到左边去,右边的菜单放置到右边,中间依然是我们的内容区域,那么请看onLayout方法~

4、onLayout

[java] view plain copy
  1. @Override
  2. protectedvoidonLayout(booleanchanged,intl,intt,intr,intb)
  3. {
  4. super.onLayout(changed,l,t,r,b);
  5. if(changed)
  6. {
  7. //将菜单隐藏
  8. this.scrollTo(mMenuWidth,0);
  9. once=true;
  10. }
  11. }

哈,出奇的简单,因为我们的内部是个横向的线性布局,所以直接把左侧滑出去即可~~定位也完成了,那么此时应该到了我们的处理触摸了。

5、onTouchEvent

[java] view plain copy
  1. @Override
  2. publicbooleanonTouchEvent(MotionEventev)
  3. {
  4. intaction=ev.getAction();
  5. switch(action)
  6. {
  7. //Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏
  8. caseMotionEvent.ACTION_UP:
  9. intscrollX=getScrollX();
  10. //如果是操作左侧菜单
  11. if(isOperateLeft)
  12. {
  13. //如果影藏的区域大于菜单一半,则影藏菜单
  14. if(scrollX>mHalfMenuWidth)
  15. {
  16. this.smoothScrollTo(mMenuWidth,0);
  17. //如果当前左侧菜单是开启状态,且mOnMenuOpenListener不为空,则回调关闭菜单
  18. if(isLeftMenuOpen&&mOnMenuOpenListener!=null)
  19. {
  20. //第一个参数true:打开菜单,false:关闭菜单;第二个参数0代表左侧;1代表右侧
  21. mOnMenuOpenListener.onMenuOpen(false,0);
  22. }
  23. isLeftMenuOpen=false;
  24. }else
  25. //关闭左侧菜单
  26. {
  27. this.smoothScrollTo(0,0);
  28. //如果当前左侧菜单是关闭状态,且mOnMenuOpenListener不为空,则回调打开菜单
  29. if(!isLeftMenuOpen&&mOnMenuOpenListener!=null)
  30. {
  31. mOnMenuOpenListener.onMenuOpen(true,0);
  32. }
  33. isLeftMenuOpen=true;
  34. }
  35. }
  36. //操作右侧
  37. if(isOperateRight)
  38. {
  39. //打开右侧侧滑菜单
  40. if(scrollX>mHalfMenuWidth+mMenuWidth)
  41. {
  42. this.smoothScrollTo(mMenuWidth+mMenuWidth,0);
  43. }else
  44. //关闭右侧侧滑菜单
  45. {
  46. this.smoothScrollTo(mMenuWidth,0);
  47. }
  48. }
  49. returntrue;
  50. }
  51. returnsuper.onTouchEvent(ev);
  52. }


依然是简单~~~我们只需要关注ACTION_UP,然后得到手指抬起后的scrollX,然后我们通过一个布尔值,判断用户现在操作是针对左侧菜单,还是右侧菜单?

如果是操作左侧,那么判断scorllX是否超过了菜单宽度的一半,然后做相应的操作。

如果是操作右侧,那么判断scrollX与mHalfMenuWidth + mMenuWidth ( 注意下,右侧菜单完全影藏的时候,scrollX 就等于mMenuWidth ),然后做对应的操作。

我们还给左侧的菜单加上了一个回调:

if (isLeftMenuOpen && mOnMenuOpenListener != null)
{
//第一个参数true:打开菜单,false:关闭菜单;第二个参数 0 代表左侧;1代表右侧
mOnMenuOpenListener.onMenuOpen(false, 0);
}

扫一眼我们的回调接口:

[java] view plain copy
  1. /**
  2. *回调的接口
  3. *@authorzhy
  4. *
  5. */
  6. publicinterfaceOnMenuOpenListener
  7. {
  8. /**
  9. *
  10. *@paramisOpentrue打开菜单,false关闭菜单
  11. *@paramflag0左侧,1右侧
  12. */
  13. voidonMenuOpen(booleanisOpen,intflag);
  14. }

右侧菜单我没有添加回调,大家按照左侧的形式自己添加下就ok ;

好了,接下来,看下我们判断用户操作是左侧还是右侧的代码写在哪。

6、onScrollChanged

[java] view plain copy
  1. @Override
  2. protectedvoidonScrollChanged(intl,intt,intoldl,intoldt)
  3. {
  4. super.onScrollChanged(l,t,oldl,oldt);
  5. if(l>mMenuWidth)
  6. {
  7. isOperateRight=true;
  8. isOperateLeft=false;
  9. }else
  10. {
  11. isOperateRight=false;
  12. isOperateLeft=true;
  13. }
  14. }

如果看过前两篇,对这个方法应该很眼熟了吧。我们直接通过 l 和 菜单宽度进行比较, 如果大于菜单宽度,那么肯定是想操作右侧菜单,否则那么就是想操作左侧菜单;

到此,我们的双向侧滑菜单已经大功告成了,至于你信不信,反正我有效果图。看效果图前,贴一下MainActivity的代码:

7、MainActivity

[java] view plain copy
  1. packagecom.zhy.zhy_bin_slidingmenu02;
  2. importjava.util.ArrayList;
  3. importjava.util.List;
  4. importandroid.app.ListActivity;
  5. importandroid.os.Bundle;
  6. importandroid.view.Window;
  7. importandroid.widget.ArrayAdapter;
  8. importandroid.widget.Toast;
  9. importcom.zhy.view.BinarySlidingMenu;
  10. importcom.zhy.view.BinarySlidingMenu.OnMenuOpenListener;
  11. publicclassMainActivityextendsListActivity
  12. {
  13. privateBinarySlidingMenumMenu;
  14. privateList<String>mDatas=newArrayList<String>();
  15. @Override
  16. protectedvoidonCreate(BundlesavedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. mMenu=(BinarySlidingMenu)findViewById(R.id.id_menu);
  22. mMenu.setOnMenuOpenListener(newOnMenuOpenListener()
  23. {
  24. @Override
  25. publicvoidonMenuOpen(booleanisOpen,intflag)
  26. {
  27. if(isOpen)
  28. {
  29. Toast.makeText(getApplicationContext(),
  30. flag==0?"LeftMenuOpen":"RightMenuOpen",
  31. Toast.LENGTH_SHORT).show();
  32. }else
  33. {
  34. Toast.makeText(getApplicationContext(),
  35. flag==0?"LeftMenuClose":"RightMenuClose",
  36. Toast.LENGTH_SHORT).show();
  37. }
  38. }
  39. });
  40. //初始化数据
  41. for(inti='A';i<='Z';i++)
  42. {
  43. mDatas.add((char)i+"");
  44. }
  45. //设置适配器
  46. setListAdapter(newArrayAdapter<String>(this,R.layout.item,mDatas));
  47. }
  48. }

没撒好说的,为了方便直接继承了ListActivity,然后设置了一下回调,布局文件一定要有ListView,为了测试我们是否有冲突~~不过有咱们也不怕~

效果图:


当然了,最简单的双向侧滑怎么能满足大家的好(Zhao)奇(Nue)心呢,所以我们准备玩点神奇的花样~~

4、打造抽屉式双向侧滑

我们在onScrollChanged添加两行代码~~为mContent设置一个属性动画

[java] view plain copy
  1. @Override
  2. protectedvoidonScrollChanged(intl,intt,intoldl,intoldt)
  3. {
  4. super.onScrollChanged(l,t,oldl,oldt);
  5. if(l>mMenuWidth)
  6. {
  7. isOperateRight=true;
  8. isOperateLeft=false;
  9. }else
  10. {
  11. isOperateRight=false;
  12. isOperateLeft=true;
  13. }
  14. floatscale=l*1.0f/mMenuWidth;
  15. ViewHelper.setTranslationX(mContent,mMenuWidth*(scale-1));
  16. }
简单分析一下哈:

1、scale,在滑动左侧菜单时:值为1.0~0.0;mMenuWidth * (scale - 1) 的值就是从 0.0~ -mMenuWidth(注意:负的) ; 那么mContent的向偏移量,就是0到mMenuWidth ;也就是说,整个滑动的过程,我们强制让内容区域固定了。

2、scale,在滑动右侧菜单时:值为:1.0~2.0;mMenuWidth * (scale - 1) 的值就是从 0.0~ mMenuWidth(注意:正数) ;那么mContent的向偏移量,就是0到mMenuWidth ;也就是说,整个滑动的过程,我们强制让内容区域固定了。


好了,内容固定了,那么我们此刻的两边菜单应该是在内容之上显示出来~~这不就是我们的抽屉效果么~

嗯,这次木有效果图了,因为测试结果发现,左侧的菜单会被内容区域遮盖住,看不到;右侧菜单符合预期效果;因为,左侧菜单滑动出来以后,被内容区域遮盖住了,这个也很容易理解,毕竟我们的布局,内容在左侧菜单后面,肯定会挡住它的。那么,怎么办呢?

起初,我准备使用bringToFont方法,在拖动的时候,让菜单在上面~~~不过呢,问题大大的,有兴趣可以试试~~

于是乎,我换了个方法,我将BinarySlidingMenu内部的Linearlayout进行了自定义,现在布局文件是这样的:

[html] view plain copy
  1. <com.zhy.view.BinarySlidingMenuxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu03"
  4. android:id="@+id/id_menu"
  5. android:layout_width="wrap_content"
  6. android:layout_height="fill_parent"
  7. android:scrollbars="none"
  8. zhy:rightPadding="100dp">
  9. <com.zhy.view.MyLinearLayout
  10. android:layout_width="wrap_content"
  11. android:layout_height="fill_parent"
  12. android:orientation="horizontal">
  13. <includelayout="@layout/layout_menu"/>
  14. <LinearLayout
  15. android:layout_width="fill_parent"
  16. android:layout_height="fill_parent"
  17. android:background="@drawable/eee"
  18. android:gravity="center"
  19. android:orientation="horizontal">
  20. <ListView
  21. android:id="@android:id/list"
  22. android:layout_width="fill_parent"
  23. android:layout_height="fill_parent">
  24. </ListView>
  25. </LinearLayout>
  26. <includelayout="@layout/layout_menu2"/>
  27. </com.zhy.view.MyLinearLayout>
  28. </com.zhy.view.BinarySlidingMenu>

MyLinearlayout的代码:

[java] view plain copy
  1. packagecom.zhy.view;
  2. importandroid.content.Context;
  3. importandroid.util.AttributeSet;
  4. importandroid.util.Log;
  5. importandroid.widget.LinearLayout;
  6. publicclassMyLinearLayoutextendsLinearLayout
  7. {
  8. publicMyLinearLayout(Contextcontext,AttributeSetattrs)
  9. {
  10. super(context,attrs);
  11. //Log.e("TAG","MyLinearLayout");
  12. setChildrenDrawingOrderEnabled(true);
  13. }
  14. @Override
  15. protectedintgetChildDrawingOrder(intchildCount,inti)
  16. {
  17. //Log.e("tag","getChildDrawingOrder"+i+","+childCount);
  18. if(i==0)
  19. return1;
  20. if(i==2)
  21. return2;
  22. if(i==1)
  23. return0;
  24. returnsuper.getChildDrawingOrder(childCount,i);
  25. }
  26. }

在构造方法设置setChildrenDrawingOrderEnabled(true);然后getChildDrawingOrder复写一下绘制子View的顺序,让内容(i==0)始终是最先绘制。

现在再运行,效果图:


效果是不是很赞,请允许我把图挪过来了~~~

现在,还有最后一个效果,如果让,菜单在内容之下呢?

5、打造菜单在内容之下的双向侧滑

不用说,大家都能想到,无非就是在onScrollChanged改改属性动画呗,说得对!

1、改写onScrollChanged方法

[java] view plain copy
  1. @Override
  2. protectedvoidonScrollChanged(intl,intt,intoldl,intoldt)
  3. {
  4. super.onScrollChanged(l,t,oldl,oldt);
  5. if(l>mMenuWidth)
  6. {
  7. //1.0~2.01.0~0.0
  8. //(2-scale)
  9. floatscale=l*1.0f/mMenuWidth;
  10. isOperateRight=true;
  11. isOperateLeft=false;
  12. ViewHelper.setTranslationX(mRightMenu,-mMenuWidth*(2-scale));
  13. }else
  14. {
  15. floatscale=l*1.0f/mMenuWidth;
  16. isOperateRight=false;
  17. isOperateLeft=true;
  18. ViewHelper.setTranslationX(mLeftMenu,mMenuWidth*scale);
  19. }
  20. }

也就是拉的时候,尽量让菜单保证在内容之下~~~代码自己琢磨下

2、改写MyLinearLayout

当然了,仅仅这些是不够的,既然我们的样式变化了,那么改写View的绘制顺序肯定也是必须的。

看下我们的MyLinearLayout

[java] view plain copy
  1. packagecom.zhy.view;
  2. importandroid.content.Context;
  3. importandroid.util.AttributeSet;
  4. importandroid.util.Log;
  5. importandroid.widget.LinearLayout;
  6. publicclassMyLinearLayoutextendsLinearLayout
  7. {
  8. publicMyLinearLayout(Contextcontext,AttributeSetattrs)
  9. {
  10. super(context,attrs);
  11. Log.e("TAG","MyLinearLayout");
  12. setChildrenDrawingOrderEnabled(true);
  13. }
  14. @Override
  15. protectedintgetChildDrawingOrder(intchildCount,inti)
  16. {
  17. if(i==0)
  18. return0;
  19. if(i==2)
  20. return1;
  21. if(i==1)
  22. return2;
  23. returnsuper.getChildDrawingOrder(childCount,i);
  24. }
  25. }
效果图:



到此,我们的形态各异的双向侧滑就结束了~~~

从最普通的双向,到抽屉式,再到我们的菜单在内容之下的侧滑都已经搞定;希望大家通过这三个侧滑,可以举一反三,打造各种变态的侧滑效果~~~~

最后我把3个侧滑的源码都会共享出来,大家自行下载:


Android普通双向侧滑


Android抽屉式双向侧滑


Android菜单在内容之下的双向侧滑


转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935,本文出自:【张鸿洋的博客】

更多相关文章

  1. 关于android滑屏菜单和tab菜单实现
  2. 基于Android的小巫新闻客户端开发---显示新闻详细内容业务逻辑实
  3. Android(安卓)自定义侧滑菜单
  4. Android(安卓)内容提供器---创建内容提供器(设计内容资源标识(URI))
  5. 基于Android小巫新闻客户端开发---显示新闻详细内容UI设计
  6. 从零开始--系统深入学习android(实践-让我们开始写代码-Android框
  7. android下拉五级菜单联动
  8. Android(安卓)TextView 判断文字内容是否超出显示省略号
  9. android使用Fragment实现底部菜单使用show()和hide()来切换以保

随机推荐

  1. 【Android(安卓)Basic Training - 02】基
  2. Android - 获取控件(包括状态栏与标题栏)宽
  3. http://www.jianshu.com/p/7e46fe7485bb
  4. Android 关于build.gradle的一些配置问题
  5. Android中隐藏标题栏和状态栏
  6. flutter 混合开发
  7. protected-broadcast 规范使用(ERROR: Se
  8. Android 手机获取时区
  9. Android EventBus二三事
  10. Android编译打包-gradle task 的依赖