之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现,因为我们今天要实现的滑动菜单框架也是基于同样的原理的。
之前的文章中在最后也提到了,如果是你的应用程序中有很多个Activity都需要加入滑动菜单的功能,那么每个Activity都要写上百行的代码才能实现效果,再简单的滑动菜单实现方案也没用。因此我们今天要实现一个滑动菜单的框架,然后在任何Activity中都可以一分钟引入滑动菜单功能。
首先还是讲一下实现原理。说是滑动菜单的框架,其实说白了也很简单,就是我们自定义一个布局,在这个自定义布局中实现好滑动菜单的功能,然后只要在Activity的布局文件里面引入我们自定义的布局,这个Activity就拥有了滑动菜单的功能了。原理讲完了,是不是很简单?下面我们来动手实现吧。
在Eclipse中新建一个Android项目,项目名就叫做RenRenSlidingLayout。
新建一个类,名叫SlidingLayout,这个类是继承自LinearLayout的,并且实现了OnTouchListener接口,具体代码如下:
-
publicclassSlidingLayoutextendsLinearLayoutimplementsOnTouchListener{
-
-
-
-
-
publicstaticfinalintSNAP_VELOCITY=200;
-
-
-
-
-
privateintscreenWidth;
-
-
-
-
-
privateintleftEdge;
-
-
-
-
-
privateintrightEdge=0;
-
-
-
-
-
privateintleftLayoutPadding=80;
-
-
-
-
-
privatefloatxDown;
-
-
-
-
-
privatefloatxMove;
-
-
-
-
-
privatefloatxUp;
-
-
-
-
-
privatebooleanisLeftLayoutVisible;
-
-
-
-
-
privateViewleftLayout;
-
-
-
-
-
privateViewrightLayout;
-
-
-
-
-
privateViewmBindView;
-
-
-
-
-
privateMarginLayoutParamsleftLayoutParams;
-
-
-
-
-
privateMarginLayoutParamsrightLayoutParams;
-
-
-
-
-
privateVelocityTrackermVelocityTracker;
-
-
-
-
-
-
-
-
publicSlidingLayout(Contextcontext,AttributeSetattrs){
-
super(context,attrs);
-
WindowManagerwm=(WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
-
screenWidth=wm.getDefaultDisplay().getWidth();
-
}
-
-
-
-
-
-
-
-
publicvoidsetScrollEvent(ViewbindView){
-
mBindView=bindView;
-
mBindView.setOnTouchListener(this);
-
}
-
-
-
-
-
publicvoidscrollToLeftLayout(){
-
newScrollTask().execute(30);
-
}
-
-
-
-
-
publicvoidscrollToRightLayout(){
-
newScrollTask().execute(-30);
-
}
-
-
-
-
-
-
-
publicbooleanisLeftLayoutVisible(){
-
returnisLeftLayoutVisible;
-
}
-
-
-
-
-
@Override
-
protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
-
super.onLayout(changed,l,t,r,b);
-
if(changed){
-
-
leftLayout=getChildAt(0);
-
leftLayoutParams=(MarginLayoutParams)leftLayout.getLayoutParams();
-
-
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:
-
-
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();
-
}
-
-
-
-
-
-
-
privatebooleanwantToShowRightLayout(){
-
returnxUp-xDown<0&&isLeftLayoutVisible;
-
}
-
-
-
-
-
-
-
privatebooleanwantToShowLeftLayout(){
-
returnxUp-xDown>0&&!isLeftLayoutVisible;
-
}
-
-
-
-
-
-
-
-
privatebooleanshouldScrollToLeftLayout(){
-
returnxUp-xDown>screenWidth/2||getScrollVelocity()>SNAP_VELOCITY;
-
}
-
-
-
-
-
-
-
-
privatebooleanshouldScrollToContent(){
-
returnxDown-xUp+leftLayoutPadding>screenWidth/2
-
||getScrollVelocity()>SNAP_VELOCITY;
-
}
-
-
-
-
-
-
-
-
-
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());
-
}
-
-
-
-
-
-
-
-
privatevoidcreateVelocityTracker(MotionEventevent){
-
if(mVelocityTracker==null){
-
mVelocityTracker=VelocityTracker.obtain();
-
}
-
mVelocityTracker.addMovement(event);
-
}
-
-
-
-
-
-
-
privateintgetScrollVelocity(){
-
mVelocityTracker.computeCurrentVelocity(1000);
-
intvelocity=(int)mVelocityTracker.getXVelocity();
-
returnMath.abs(velocity);
-
}
-
-
-
-
-
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);
-
-
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);
-
}
-
}
-
-
-
-
-
-
-
-
privatevoidsleep(longmillis){
-
try{
-
Thread.sleep(millis);
-
}catch(InterruptedExceptione){
-
e.printStackTrace();
-
}
-
}
-
}
看到这里,我相信大家一定会觉得这些代码非常熟悉。没错,基本上这些代码和之前那篇文章的代码大同小异,只不过以前这些代码是写在Activity里的,而现在我们移动到了自定义的View当中。
接着我来说明一下和以前不同的部分。我们可以看到,这里将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">
-
-
-
-
<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>
我们可以看到,在根布局的下面,我们引入了自定义布局com.example.slide.SlidingLayout,然后在它里面加入了两个子元素,一个RelativeLayout和一个LinearLayout。RelativeLayout中比较简单,就加入了一个TextView。LinearLayout里面我们加入了一个按钮和一个ListView。
然后创建或打开MainActivity作为程序的主Activity,加入代码:
-
publicclassMainActivityextendsActivity{
-
-
-
-
-
privateSlidingLayoutslidingLayout;
-
-
-
-
-
privateButtonmenuButton;
-
-
-
-
-
privateListViewcontentListView;
-
-
-
-
-
privateArrayAdapter<String>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);
-
-
slidingLayout.setScrollEvent(contentListView);
-
menuButton.setOnClickListener(newOnClickListener(){
-
@Override
-
publicvoidonClick(Viewv){
-
-
if(slidingLayout.isLeftLayoutVisible()){
-
slidingLayout.scrollToRightLayout();
-
}else{
-
slidingLayout.scrollToLeftLayout();
-
}
-
}
-
});
-
}
-
-
}
上述代码重点是调用SlidingLayout的setScrollEvent方法,为ListView注册touch事件。同时给按钮添加了一个点击事件,实现了点击一下显示左边布局,再点击一下隐藏左边布局的功能。
最后还是老规矩,给出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完成的滑动菜单栏