- 浏览: 65251 次
- 性别:
- 来自: 西安
文章分类
最新评论
Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效
之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现,因为我们今天要实现的滑动菜单框架也是基于同样的原理的。
之前的文章中在最后也提到了,如果是你的应用程序中有很多个Activity都需要加入滑动菜单的功能,那么每个Activity都要写上百行的代码才能实现效果,再简单的滑动菜单实现方案也没用。因此我们今天要实现一个滑动菜单的框架,然后在任何Activity中都可以一分钟引入滑动菜单功能。
首先还是讲一下实现原理。说是滑动菜单的框架,其实说白了也很简单,就是我们自定义一个布局,在这个自定义布局中实现好滑动菜单的功能,然后只要在Activity的布局文件里面引入我们自定义的布局,这个Activity就拥有了滑动菜单的功能了。原理讲完了,是不是很简单?下面我们来动手实现吧。
在Eclipse中新建一个Android项目,项目名就叫做RenRenSlidingLayout。
新建一个类,名叫SlidingLayout,这个类是继承自LinearLayout的,并且实现了OnTouchListener接口,具体代码如下:
- publicclassSlidingLayoutextendsLinearLayoutimplementsOnTouchListener{
- /**
- *滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。
- */
- publicstaticfinalintSNAP_VELOCITY=200;
- /**
- *屏幕宽度值。
- */
- privateintscreenWidth;
- /**
- *左侧布局最多可以滑动到的左边缘。值由左侧布局的宽度来定,marginLeft到达此值之后,不能再减少。
- */
- privateintleftEdge;
- /**
- *左侧布局最多可以滑动到的右边缘。值恒为0,即marginLeft到达0之后,不能增加。
- */
- privateintrightEdge=0;
- /**
- *左侧布局完全显示时,留给右侧布局的宽度值。
- */
- privateintleftLayoutPadding=80;
- /**
- *记录手指按下时的横坐标。
- */
- privatefloatxDown;
- /**
- *记录手指移动时的横坐标。
- */
- privatefloatxMove;
- /**
- *记录手机抬起时的横坐标。
- */
- privatefloatxUp;
- /**
- *左侧布局当前是显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。
- */
- privatebooleanisLeftLayoutVisible;
- /**
- *左侧布局对象。
- */
- privateViewleftLayout;
- /**
- *右侧布局对象。
- */
- privateViewrightLayout;
- /**
- *用于监听侧滑事件的View。
- */
- privateViewmBindView;
- /**
- *左侧布局的参数,通过此参数来重新确定左侧布局的宽度,以及更改leftMargin的值。
- */
- privateMarginLayoutParamsleftLayoutParams;
- /**
- *右侧布局的参数,通过此参数来重新确定右侧布局的宽度。
- */
- privateMarginLayoutParamsrightLayoutParams;
- /**
- *用于计算手指滑动的速度。
- */
- privateVelocityTrackermVelocityTracker;
- /**
- *重写SlidingLayout的构造函数,其中获取了屏幕的宽度。
- *
- *@paramcontext
- *@paramattrs
- */
- publicSlidingLayout(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- WindowManagerwm=(WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
- screenWidth=wm.getDefaultDisplay().getWidth();
- }
- /**
- *绑定监听侧滑事件的View,即在绑定的View进行滑动才可以显示和隐藏左侧布局。
- *
- *@parambindView
- *需要绑定的View对象。
- */
- publicvoidsetScrollEvent(ViewbindView){
- mBindView=bindView;
- mBindView.setOnTouchListener(this);
- }
- /**
- *将屏幕滚动到左侧布局界面,滚动速度设定为30.
- */
- publicvoidscrollToLeftLayout(){
- newScrollTask().execute(30);
- }
- /**
- *将屏幕滚动到右侧布局界面,滚动速度设定为-30.
- */
- publicvoidscrollToRightLayout(){
- newScrollTask().execute(-30);
- }
- /**
- *左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效。
- *
- *@return左侧布局完全显示返回true,完全隐藏返回false。
- */
- publicbooleanisLeftLayoutVisible(){
- returnisLeftLayoutVisible;
- }
- /**
- *在onLayout中重新设定左侧布局和右侧布局的参数。
- */
- @Override
- protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
- super.onLayout(changed,l,t,r,b);
- if(changed){
- //获取左侧布局对象
- leftLayout=getChildAt(0);
- leftLayoutParams=(MarginLayoutParams)leftLayout.getLayoutParams();
- //重置左侧布局对象的宽度为屏幕宽度减去leftLayoutPadding
- leftLayoutParams.width=screenWidth-leftLayoutPadding;
- //设置最左边距为负的左侧布局的宽度
- leftEdge=-leftLayoutParams.width;
- leftLayoutParams.leftMargin=leftEdge;
- leftLayout.setLayoutParams(leftLayoutParams);
- //获取右侧布局对象
- rightLayout=getChildAt(1);
- rightLayoutParams=(MarginLayoutParams)rightLayout.getLayoutParams();
- rightLayoutParams.width=screenWidth;
- rightLayout.setLayoutParams(rightLayoutParams);
- }
- }
- @Override
- publicbooleanonTouch(Viewv,MotionEventevent){
- createVelocityTracker(event);
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- //手指按下时,记录按下时的横坐标
- xDown=event.getRawX();
- break;
- caseMotionEvent.ACTION_MOVE:
- //手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局
- xMove=event.getRawX();
- intdistanceX=(int)(xMove-xDown);
- if(isLeftLayoutVisible){
- leftLayoutParams.leftMargin=distanceX;
- }else{
- leftLayoutParams.leftMargin=leftEdge+distanceX;
- }
- if(leftLayoutParams.leftMargin<leftEdge){
- leftLayoutParams.leftMargin=leftEdge;
- }elseif(leftLayoutParams.leftMargin>rightEdge){
- leftLayoutParams.leftMargin=rightEdge;
- }
- leftLayout.setLayoutParams(leftLayoutParams);
- break;
- caseMotionEvent.ACTION_UP:
- //手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局
- xUp=event.getRawX();
- if(wantToShowLeftLayout()){
- if(shouldScrollToLeftLayout()){
- scrollToLeftLayout();
- }else{
- scrollToRightLayout();
- }
- }elseif(wantToShowRightLayout()){
- if(shouldScrollToContent()){
- scrollToRightLayout();
- }else{
- scrollToLeftLayout();
- }
- }
- recycleVelocityTracker();
- break;
- }
- returnisBindBasicLayout();
- }
- /**
- *判断当前手势的意图是不是想显示右侧布局。如果手指移动的距离是负数,且当前左侧布局是可见的,则认为当前手势是想要显示右侧布局。
- *
- *@return当前手势想显示右侧布局返回true,否则返回false。
- */
- privatebooleanwantToShowRightLayout(){
- returnxUp-xDown<0&&isLeftLayoutVisible;
- }
- /**
- *判断当前手势的意图是不是想显示左侧布局。如果手指移动的距离是正数,且当前左侧布局是不可见的,则认为当前手势是想要显示左侧布局。
- *
- *@return当前手势想显示左侧布局返回true,否则返回false。
- */
- privatebooleanwantToShowLeftLayout(){
- returnxUp-xDown>0&&!isLeftLayoutVisible;
- }
- /**
- *判断是否应该滚动将左侧布局展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
- *就认为应该滚动将左侧布局展示出来。
- *
- *@return如果应该滚动将左侧布局展示出来返回true,否则返回false。
- */
- privatebooleanshouldScrollToLeftLayout(){
- returnxUp-xDown>screenWidth/2||getScrollVelocity()>SNAP_VELOCITY;
- }
- /**
- *判断是否应该滚动将右侧布局展示出来。如果手指移动距离加上leftLayoutPadding大于屏幕的1/2,
- *或者手指移动速度大于SNAP_VELOCITY,就认为应该滚动将右侧布局展示出来。
- *
- *@return如果应该滚动将右侧布局展示出来返回true,否则返回false。
- */
- privatebooleanshouldScrollToContent(){
- returnxDown-xUp+leftLayoutPadding>screenWidth/2
- ||getScrollVelocity()>SNAP_VELOCITY;
- }
- /**
- *判断绑定滑动事件的View是不是一个基础layout,不支持自定义layout,只支持四种基本layout,
- *AbsoluteLayout已被弃用。
- *
- *@return如果绑定滑动事件的View是LinearLayout,RelativeLayout,FrameLayout,
- *TableLayout之一就返回true,否则返回false。
- */
- privatebooleanisBindBasicLayout(){
- if(mBindView==null){
- returnfalse;
- }
- StringviewName=mBindView.getClass().getName();
- returnviewName.equals(LinearLayout.class.getName())
- ||viewName.equals(RelativeLayout.class.getName())
- ||viewName.equals(FrameLayout.class.getName())
- ||viewName.equals(TableLayout.class.getName());
- }
- /**
- *创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。
- *
- *@paramevent
- *右侧布局监听控件的滑动事件
- */
- privatevoidcreateVelocityTracker(MotionEventevent){
- if(mVelocityTracker==null){
- mVelocityTracker=VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- }
- /**
- *获取手指在右侧布局的监听View上的滑动速度。
- *
- *@return滑动速度,以每秒钟移动了多少像素值为单位。
- */
- privateintgetScrollVelocity(){
- mVelocityTracker.computeCurrentVelocity(1000);
- intvelocity=(int)mVelocityTracker.getXVelocity();
- returnMath.abs(velocity);
- }
- /**
- *回收VelocityTracker对象。
- */
- privatevoidrecycleVelocityTracker(){
- mVelocityTracker.recycle();
- mVelocityTracker=null;
- }
- classScrollTaskextendsAsyncTask<Integer,Integer,Integer>{
- @Override
- protectedIntegerdoInBackground(Integer...speed){
- intleftMargin=leftLayoutParams.leftMargin;
- //根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。
- while(true){
- leftMargin=leftMargin+speed[0];
- if(leftMargin>rightEdge){
- leftMargin=rightEdge;
- break;
- }
- if(leftMargin<leftEdge){
- leftMargin=leftEdge;
- break;
- }
- publishProgress(leftMargin);
- //为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。
- sleep(20);
- }
- if(speed[0]>0){
- isLeftLayoutVisible=true;
- }else{
- isLeftLayoutVisible=false;
- }
- returnleftMargin;
- }
- @Override
- protectedvoidonProgressUpdate(Integer...leftMargin){
- leftLayoutParams.leftMargin=leftMargin[0];
- leftLayout.setLayoutParams(leftLayoutParams);
- }
- @Override
- protectedvoidonPostExecute(IntegerleftMargin){
- leftLayoutParams.leftMargin=leftMargin;
- leftLayout.setLayoutParams(leftLayoutParams);
- }
- }
- /**
- *使当前线程睡眠指定的毫秒数。
- *
- *@parammillis
- *指定当前线程睡眠多久,以毫秒为单位
- */
- privatevoidsleep(longmillis){
- try{
- Thread.sleep(millis);
- }catch(InterruptedExceptione){
- e.printStackTrace();
- }
- }
- }
接着我来说明一下和以前不同的部分。我们可以看到,这里将onLayout方法进行了重写,使用getChildAt(0)获取到的布局作为左边布局,使用getChildAt(1)获取到的布局作为右边布局。并将左边布局的宽度重定义为屏幕宽度减去leftLayoutPadding,将右侧布局的宽度重定义为屏幕宽度。然后让左边布局偏移出屏幕,这样能看到的就只有右边布局了。因此在这里我们也可以看出,使用SlidingLayout这个布局的前提条件,必须为这个布局提供两个子元素,第一个元素会作为左边布局偏移出屏幕,第二个元素会作为右边布局显示在屏幕上。
然后我们看一下setScrollEvent方法,这个方法接收一个View作为参数,然后为这个View绑定了一个touch事件。这是什么意思呢?让我们来想象一个场景,如果右侧布局是一个LinearLayout,我可以通过监听LinearLayout上的touch事件来控制左侧布局的显示和隐藏。但是如果右侧布局的LinearLayout里面加入了一个ListView,而这个ListView又充满了整个LinearLayout,这个时候LinearLayout将不可能再被touch到了,这个时候我们就需要将touch事件注册到ListView上。setScrollEvent方法也就是提供了一个注册接口,touch事件将会注册到传入的View上。
最后还有一个陌生的方法,isBindBasicLayout。这个方法就是判断了一下注册touch事件的View是不是四个基本布局之一,如果是就返回true,否则返回false。这个方法在整个SlidingLayout中起着非常重要的作用,主要用于控制onTouch事件是返回true还是false,这将影响到布局当中的View的功能是否可用。由于里面牵扯到了Android的事件转发机制,内容比较多,就不在这里详细解释了,我会考虑以后专门写一篇文章来介绍Android的事件机制。这里就先简单记住如果是基本布局就返回true,否则就返回false。
好了,我们的SlidingLayout写完了,接下来就是见证奇迹的时刻,让我们一起看看如何一分钟在Activity中引入滑动菜单功能。
创建或打开layout目录下的activity_main.xml文件,加入如下代码:
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="horizontal"
- tools:context=".MainActivity">
- <!--使用自定义的侧滑布局,orientation必须为水平方向-->
- <com.example.slide.SlidingLayout
- android:id="@+id/slidingLayout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="horizontal">
- <!--
- 侧滑布局的根节点下,有且只能有两个子元素,这两个子元素必须是四种基本布局之一,
- 即LinearLayout,RelativeLayout,FrameLayout或TableLayout。
- 第一个子元素将做为左侧布局,初始化后被隐藏。第二个子元素将做为右侧布局,
- 也就是当前Activity的主布局,将主要的数据放在里面。
- -->
- <RelativeLayout
- android:id="@+id/menu"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="#00ccff">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:text="Thisismenu"
- android:textColor="#000000"
- android:textSize="28sp"/>
- </RelativeLayout>
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
- <Button
- android:id="@+id/menuButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Menu"/>
- <ListView
- android:id="@+id/contentList"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- </ListView>
- </LinearLayout>
- </com.example.slide.SlidingLayout>
- </LinearLayout>
然后创建或打开MainActivity作为程序的主Activity,加入代码:
- publicclassMainActivityextendsActivity{
- /**
- *侧滑布局对象,用于通过手指滑动将左侧的菜单布局进行显示或隐藏。
- */
- privateSlidingLayoutslidingLayout;
- /**
- *menu按钮,点击按钮展示左侧布局,再点击一次隐藏左侧布局。
- */
- privateButtonmenuButton;
- /**
- *放在content布局中的ListView。
- */
- privateListViewcontentListView;
- /**
- *作用于contentListView的适配器。
- */
- privateArrayAdapter<String>contentListAdapter;
- /**
- *用于填充contentListAdapter的数据源。
- */
- privateString[]contentItems={"ContentItem1","ContentItem2","ContentItem3",
- "ContentItem4","ContentItem5","ContentItem6","ContentItem7",
- "ContentItem8","ContentItem9","ContentItem10","ContentItem11",
- "ContentItem12","ContentItem13","ContentItem14","ContentItem15",
- "ContentItem16"};
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- slidingLayout=(SlidingLayout)findViewById(R.id.slidingLayout);
- menuButton=(Button)findViewById(R.id.menuButton);
- contentListView=(ListView)findViewById(R.id.contentList);
- contentListAdapter=newArrayAdapter<String>(this,android.R.layout.simple_list_item_1,
- contentItems);
- contentListView.setAdapter(contentListAdapter);
- //将监听滑动事件绑定在contentListView上
- slidingLayout.setScrollEvent(contentListView);
- menuButton.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- //实现点击一下menu展示左侧布局,再点击一下隐藏左侧布局的功能
- if(slidingLayout.isLeftLayoutVisible()){
- slidingLayout.scrollToRightLayout();
- }else{
- slidingLayout.scrollToLeftLayout();
- }
- }
- });
- }
- }
最后还是老规矩,给出AndroidManifest.xml的代码:
- <?xmlversion="1.0"encoding="utf-8"?>
- <manifestxmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.slide"
- android:versionCode="1"
- android:versionName="1.0">
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="8"/>
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.NoTitleBar">
- <activity
- android:name="com.example.slide.MainActivity"
- android:label="@string/app_name">
- <intent-filter>
- <actionandroid:name="android.intent.action.MAIN"/>
- <categoryandroid:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
- </manifest>
而当左边布局完全显示的时候,效果图如下:
除此之外,点击Menu按钮也可以控制左边布局的显示和隐藏,大家可以自己试一下。
使用自定义布局的话,就可以用简单的方式在任意Activity中加入滑动菜单功能,即使你有再多的Activity也不用怕了,一分钟引入滑动菜单妥妥的。
再总结一下吧,向Activity中加入滑动菜单功能只需要两步:
1. 在Acitivty的layout中引入我们自定义的布局,并且给这个布局要加入两个直接子元素。
2. 在Activity中通过setScrollEvent方法,给一个View注册touch事件。
好了,今天的讲解到此结束,有疑问的朋友请在下面留言。
相关推荐
此为示例代码,详细讲解请参考 http://blog.csdn.net/sinyu890807/article/details/8744400
此代码为之前 Android滑动菜单框架完全解析,教你如何一分钟实现滑动菜单特效demo 的扩展版,主要加入了在菜单界面点击未隐藏部分,可以回到主界面的功能。
Android滑动菜单框架完全解析。。。。。。。。。。。。。。。。。
此为示例代码,详细讲解请参考: http://blog.csdn.net/guolin_blog/article/details/9671609
Android滑动菜单框架 SliderMenu 一分钟集成
此为示例代码,详解讲解请参考博文: http://blog.csdn.net/guolin_blog/article/details/10471245 如果对资源内容有疑问,可以到博客中留言。
此版本修正了以下内容: 1.将滑动方式改成了覆盖型。 2.ListView上下滚动时不会轻易滑出菜单。 3.正在滑动时屏蔽掉内容布局上的事件。 4.当菜单布局展示时,点击一下右侧的内容布局,可以将菜单隐藏。
主要介绍了Android实现滑动菜单特效之滑动菜单框架完全解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
android圆形滑动菜单源码是一款仿圆形滑动菜单的android源码
Android中实现导航菜单左右滑动效果,实现原理是使用android-support-v4.jar包中ViewPager控件,在ViewPager控件中设置流布局。
android滑动菜单,在开发中经常遇到像91手机助手和豌豆夹的滑动菜单。现在滑动菜单也成移动开发在主流。
利用开源库slidingmenu自己作了一个实例。
android滑动菜单
很好的demo,希望能帮助到那些需要学习滑动菜单的人
android实现仿qq中listview滑动菜单 实现滑动推出删除和打开按钮
主要为大家详细介绍了Android 3D滑动菜单,Android实现推拉门式的立体特效,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
android滑动菜单SlidingMenu实例
android滑动菜单源码,包含制作左右滑动的菜单
滑动菜单,Android 仿QQ滑动菜单栏仿照QQ完成的滑动菜单栏,Android 仿QQ滑动菜单栏仿照QQ完成的滑动菜单栏