概述
今天这篇博客将记录一些关于DrawerLayout的基本用法,我想关于DrawerLayout的用法也许有不少不够了解,这也是比较正常的事情,因为DrawerLayout作为AndroID组件是Google后来在androID中添加的,在androID.support.v4包下。那么,DrawerLayout是一个怎么的组件呢?我们知道,当我们使用AndroID上各类App的时候,是不是注意过App主页上通常有一个“侧滑菜单”?关于侧滑菜单的实现,我在前面博客里有一些介绍,想多些了解的朋友请移步:
Android自定义控件――侧滑菜单
Android自定义控件――开源组件SlidingMenu的项目集成
这里用“网易新闻”客户端v4.4的截图来说明一下,这个DrawerLayout抽屉式布局是什么样子的。
好,大家已经看到了,网易新闻客户端效果很明显,当我们手指在屏幕左侧向右滑动时候,就会有一个抽屉式的菜单从左边d出,并且是“悬浮”在主界面之上的,合理的利用了设备上有限的空间,同样手指在屏幕右侧向左滑动也会出现一个向左d出的抽屉式菜单,用户体验效果还是不错的,在DrawerLayout出现之前,我们需要做侧滑菜单时,不得不自己实现一个或者使用Github上的开源的项目SlIDingMenu,也许是Google也看到了SlIDingMenu的强大之处,于是在AndroID的后期版本中添加了DrawerLayout来实现SlIDingMenu同样功能的组件,而且为了兼容早期版本,将其添加在androID,support.v4包下。
关于DrawerLayout的Training:http://developer.android.com/training/implementing-navigation/nav-drawer.html
关于DrawerLayout的API:http://developer.android.com/reference/android/support/v4/widget/DrawerLayout.html
另外,我已经翻译过了Google的Training课程,地址是:https://www.oudahe.com/p/26920/
效果预览
创建抽屉布局
下面这个抽屉布局引用的是androID.support.v4.DrawerLayout,类似于lineaLayout、relativeLayout等布局一样定义,在DrawerLayout内部再定义3个布局,分别是管理主界面的FrameLayout,此布局用来展示界面切换的Fragment,下面是ListVIEw,用来展示菜单列表,最后是一个relativeLayout,用来展示右边的布局,布局代码如下:
<androID.support.v4.Widget.DrawerLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:ID="@+ID/drawer_layout" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" > <FrameLayout androID:ID="@+ID/content_frame" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" /> <ListVIEw androID:ID="@+ID/left_drawer" androID:layout_wIDth="200dp" androID:layout_height="match_parent" androID:layout_gravity="start" androID:background="#111" androID:choiceMode="singleChoice" androID:divIDer="@androID:color/transparent" androID:divIDerHeight="0dp" /> <relativeLayout androID:ID="@+ID/right_drawer" androID:layout_wIDth="220dp" androID:layout_height="match_parent" androID:layout_gravity="end" androID:background="#111" androID:gravity="center_horizontal" > <TextVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="这是右边栏" androID:textcolor="@androID:color/white" androID:textSize="24sp" /> </relativeLayout> </androID.support.v4.Widget.DrawerLayout>
这个布局文件示范了一些重要的布局特征.
初始化抽屉列表
正如上述所讲,因为DrawerLayout里包含一个ListVIEw作为左边栏侧滑菜单,所以我们需要首先初始化这个抽屉列表,并且为这个列表适配上数据,数据适配器使用的是最简单的ArrayAdapter,模拟数据被简单的定义在res/values/strings.xml里,如下:
<string-array name="menu_array"> <item>Menu 1</item> <item>Menu 2</item> <item>Menu 3</item> <item>Menu 4</item> </string-array>
在Java代码中,首先创建一个MainActivity继承了androID.support.v4.app.FragmentActivity,因为后续中需要进行Fragment之间的切换。
protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); ...... // 初始化菜单列表 mMenuTitles = getResources().getStringArray(R.array.menu_array); mMenuListVIEw.setAdapter(new ArrayAdapter<String>(this,R.layout.drawer_List_item,mMenuTitles)); mMenuListVIEw.setonItemClickListener(new DrawerItemClickListener()); ...... }
处理导航点击事件
当用户选择了抽屉列表里面的一个Item时,系统调用onItemClickListener上的onItemClick(),给setonItemClickListener()你在onItemClick()方法里面做什么,在下面的例子中,选择每一个Item都会在主要内容的布局中插入一个不同的Fragment.并且将导航列表的内容传递给Fragment中显示出来,下面是部分代码:
/** * ListVIEw上的Item点击事件 * */ private class DrawerItemClickListener implements ListVIEw.OnItemClickListener { @OverrIDe public voID onItemClick(AdapterVIEw<?> parent,VIEw vIEw,int position,long ID) { selectItem(position); } } /** * 切换主视图区域的Fragment * * @param position */ private voID selectItem(int position) { // Todo auto-generated method stub Fragment fragment = new ContentFragment(); Bundle args = new Bundle(); switch (position) { case 0: args.putString("key",mMenuTitles[position]); break; case 1: args.putString("key",mMenuTitles[position]); break; case 2: args.putString("key",mMenuTitles[position]); break; case 3: args.putString("key",mMenuTitles[position]); break; default: break; } fragment.setArguments(args); // FragmentActivity将点击的菜单列表标题传递给Fragment FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction().replace(R.ID.content_frame,fragment).commit(); // 更新选择后的item和Title,然后关闭菜单 mMenuListVIEw.setItemChecked(position,true); setTitle(mMenuTitles[position]); mDrawerLayout.closeDrawer(mMenuListVIEw); }
开源material-menu的集成
细心的朋友也许会发现“网易新闻”v4.4客户端主页左上角上有个菜单“动态”的菜单按钮,显示流程是这样的,当菜单没有打开时,显示“三”这样的三条横线,当菜单打开(无论左右菜单)时,会显示“<-”这样的按钮,不停的变化,这样的效果是不是有点绚丽啊?!了解过AndroID5.0的朋友,应该会知道这种效果是使用了AndroID5.0新推出的Material Design设计语言做出来的效果,那么该怎么模仿这个效果呢?不好意思,由于偷懒,我已经在牛牛的Github中找到了这样的效果――material-menu组件,该组件模拟出了AndroID5.0下的Material Design效果,注意的是该组件中使用了JackWharton的NineoldAndroIDs动画效果。
material-menu主页:https://github.com/balysv/material-menu
NineoldAndroIDs主页:https://github.com/JakeWharton/NineOldAndroids
关于material-menu的使用可以参考其主页上的Demo和说明,集成时需要下载NineoldAndroIDs导出jar集成到项目中。下面是我使用的部分代码:
protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); ...... // 设置抽屉打开时,主要内容区被自定义阴影覆盖 mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,GravityCompat.START); // 设置Actionbar可见,并且切换菜单和内容视图 getActionbar().setdisplayHomeAsUpEnabled(true); getActionbar().setHomebuttonEnabled(true); mMaterialMenuIcon = new MaterialMenuIcon(this,color.WHITE,stroke.THIN); mDrawerLayout.setDrawerListener(new DrawerLayout.SimpleDrawerListener() { @OverrIDe public voID onDrawerSlIDe(VIEw drawerVIEw,@R_419_5987@ slIDeOffset) { showVIEw = drawerVIEw; if (drawerVIEw == mMenuListVIEw) { mMaterialMenuIcon.settransformationOffset(MaterialMenuDrawable.AnimationState.BURGER_ARROW,isDirection_left ? 2 - slIDeOffset : slIDeOffset); } else if (drawerVIEw == right_drawer) { mMaterialMenuIcon.settransformationOffset(MaterialMenuDrawable.AnimationState.BURGER_ARROW,isDirection_right ? 2 - slIDeOffset : slIDeOffset); } } @OverrIDe public voID onDrawerOpened(androID.vIEw.VIEw drawerVIEw) { if (drawerVIEw == mMenuListVIEw) { isDirection_left = true; } else if (drawerVIEw == right_drawer) { isDirection_right = true; } } @OverrIDe public voID onDrawerClosed(androID.vIEw.VIEw drawerVIEw) { if (drawerVIEw == mMenuListVIEw) { isDirection_left = false; } else if (drawerVIEw == right_drawer) { isDirection_right = false; showVIEw = mMenuListVIEw; } } }); ...... }
此外,还需要关联一下meterial-menu的状态,需要覆盖Activity下的onPostCreate和onSaveInstanceState方法:
/** * 根据onPostCreate回调的状态,还原对应的icon state */ @OverrIDe protected voID onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mMaterialMenuIcon.syncState(savedInstanceState); } /** * 根据onSaveInstanceState回调的状态,保存当前icon state */ @OverrIDe protected voID onSaveInstanceState(Bundle outState) { mMaterialMenuIcon.onSaveInstanceState(outState); super.onSaveInstanceState(outState); }
添加Actionbar上的菜单按钮
为了尽量模拟出“网易新闻”v4.4客户端主页,我也在标题栏右上角添加一个小图标,为了能在点击这个小图标的时候d出右边栏菜单,实现方式很简单,关于Actionbar上添加导航的知识可以在csdn上搜到一些解释或者上AndroID开发者官网查看源文档,我这里首先简单的在res/menu下main.xml中这样定义一个:
<menu xmlns:androID="http://schemas.androID.com/apk/res/androID" > <item androID:ID="@+ID/action_personal" androID:icon="@drawable/action_personal" androID:orderIncategory="100" androID:showAsAction="always" androID:title="@string/action_personal"/> </menu>
完成定义 *** 作后,需要加载菜单布局:
/** * 加载菜单 */ @OverrIDe public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main,menu); return true; }
标题栏导航点击事件处理
/** * 点击Actionbar上菜单 */ @OverrIDe public boolean onoptionsItemSelected(MenuItem item) { int ID = item.getItemID(); switch (ID) { case androID.R.ID.home: if (showVIEw == mMenuListVIEw) { if (!isDirection_left) { // 左边栏菜单关闭时,打开 mDrawerLayout.openDrawer(mMenuListVIEw); } else {// 左边栏菜单打开时,关闭 mDrawerLayout.closeDrawer(mMenuListVIEw); } } else if (showVIEw == right_drawer) { if (!isDirection_right) {// 右边栏关闭时,打开 mDrawerLayout.openDrawer(right_drawer); } else {// 右边栏打开时,关闭 mDrawerLayout.closeDrawer(right_drawer); } } break; case R.ID.action_personal: if (!isDirection_right) {// 右边栏关闭时,打开 if (showVIEw == mMenuListVIEw) { mDrawerLayout.closeDrawer(mMenuListVIEw); } mDrawerLayout.openDrawer(right_drawer); } else {// 右边栏打开时,关闭 mDrawerLayout.closeDrawer(right_drawer); } break; default: break; } return super.onoptionsItemSelected(item); }
这段的逻辑有点绕,事实上我做的是这样的,需要保证主界面上只能最多显示一个菜单布局,当左边的菜单布局展示时,此时打开右边菜单布局时,需要隐藏左边菜单布局;同样,如果右边的菜单布局已经在展示的时候,这时需要打开左边菜单布局,必须首先隐藏掉右边的菜单布局。为了判断当前即将显示或者关闭的是哪个布局,我在全局变量中定义了showVIEw用来标记当前即将显示或者关闭的视图,如果showVIEw==mMenuListVIEw,说明左边菜单布局是即将被显示或隐藏的,这时进一步判断菜单是视图mMenuListVIEw的是否已经显示的标记isDirection_left,来打开或者关闭左边视图菜单。
同样的道理,如果当前即将显示或者隐藏的是右边导航菜单的话,我们需要进一步判断右边导航是否已经显示,从而进行相关打开或隐藏的决定。
这里的逻辑似乎解释的有点乱,而且代码是分片段贴出来的,不利于理解,需要进一步理解的话,不妨继续看下面的部分,我已经贴出了所以的Java代码,注释也很详尽,可以方便理解,实在不行,还可以点击博客下方的下载链接,直接下载源码运行一下。
全部源码
public class MainActivity extends FragmentActivity { /** DrawerLayout */ private DrawerLayout mDrawerLayout; /** 左边栏菜单 */ private ListVIEw mMenuListVIEw; /** 右边栏 */ private relativeLayout right_drawer; /** 菜单列表 */ private String[] mMenuTitles; /** Material Design风格 */ private MaterialMenuIcon mMaterialMenuIcon; /** 菜单打开/关闭状态 */ private boolean isDirection_left = false; /** 右边栏打开/关闭状态 */ private boolean isDirection_right = false; private VIEw showVIEw; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); mDrawerLayout = (DrawerLayout) findVIEwByID(R.ID.drawer_layout); mMenuListVIEw = (ListVIEw) findVIEwByID(R.ID.left_drawer); right_drawer = (relativeLayout) findVIEwByID(R.ID.right_drawer); this.showVIEw = mMenuListVIEw; // 初始化菜单列表 mMenuTitles = getResources().getStringArray(R.array.menu_array); mMenuListVIEw.setAdapter(new ArrayAdapter<String>(this,mMenuTitles)); mMenuListVIEw.setonItemClickListener(new DrawerItemClickListener()); // 设置抽屉打开时,主要内容区被自定义阴影覆盖 mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,stroke.THIN); mDrawerLayout.setDrawerListener(new DrawerLayoutStateListener()); if (savedInstanceState == null) { selectItem(0); } } /** * ListVIEw上的Item点击事件 * */ private class DrawerItemClickListener implements ListVIEw.OnItemClickListener { @OverrIDe public voID onItemClick(AdapterVIEw<?> parent,long ID) { selectItem(position); } } /** * DrawerLayout状态变化监听 */ private class DrawerLayoutStateListener extends DrawerLayout.SimpleDrawerListener { /** * 当导航菜单滑动的时候被执行 */ @OverrIDe public voID onDrawerSlIDe(VIEw drawerVIEw,@R_419_5987@ slIDeOffset) { showVIEw = drawerVIEw; if (drawerVIEw == mMenuListVIEw) {// 根据isDirection_left决定执行动画 mMaterialMenuIcon.settransformationOffset( MaterialMenuDrawable.AnimationState.BURGER_ARROW,isDirection_left ? 2 - slIDeOffset : slIDeOffset); } else if (drawerVIEw == right_drawer) {// 根据isDirection_right决定执行动画 mMaterialMenuIcon.settransformationOffset( MaterialMenuDrawable.AnimationState.BURGER_ARROW,isDirection_right ? 2 - slIDeOffset : slIDeOffset); } } /** * 当导航菜单打开时执行 */ @OverrIDe public voID onDrawerOpened(androID.vIEw.VIEw drawerVIEw) { if (drawerVIEw == mMenuListVIEw) { isDirection_left = true; } else if (drawerVIEw == right_drawer) { isDirection_right = true; } } /** * 当导航菜单关闭时执行 */ @OverrIDe public voID onDrawerClosed(androID.vIEw.VIEw drawerVIEw) { if (drawerVIEw == mMenuListVIEw) { isDirection_left = false; } else if (drawerVIEw == right_drawer) { isDirection_right = false; showVIEw = mMenuListVIEw; } } } /** * 切换主视图区域的Fragment * * @param position */ private voID selectItem(int position) { Fragment fragment = new ContentFragment(); Bundle args = new Bundle(); switch (position) { case 0: args.putString("key",mMenuTitles[position]); break; default: break; } fragment.setArguments(args); // FragmentActivity将点击的菜单列表标题传递给Fragment FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.ID.content_frame,true); setTitle(mMenuTitles[position]); mDrawerLayout.closeDrawer(mMenuListVIEw); } /** * 点击Actionbar上菜单 */ @OverrIDe public boolean onoptionsItemSelected(MenuItem item) { int ID = item.getItemID(); switch (ID) { case androID.R.ID.home: if (showVIEw == mMenuListVIEw) { if (!isDirection_left) { // 左边栏菜单关闭时,打开 mDrawerLayout.openDrawer(mMenuListVIEw); } else {// 左边栏菜单打开时,关闭 mDrawerLayout.closeDrawer(mMenuListVIEw); } } else if (showVIEw == right_drawer) { if (!isDirection_right) {// 右边栏关闭时,打开 mDrawerLayout.openDrawer(right_drawer); } else {// 右边栏打开时,关闭 mDrawerLayout.closeDrawer(right_drawer); } } break; case R.ID.action_personal: if (!isDirection_right) {// 右边栏关闭时,打开 if (showVIEw == mMenuListVIEw) { mDrawerLayout.closeDrawer(mMenuListVIEw); } mDrawerLayout.openDrawer(right_drawer); } else {// 右边栏打开时,关闭 mDrawerLayout.closeDrawer(right_drawer); } break; default: break; } return super.onoptionsItemSelected(item); } /** * 根据onPostCreate回调的状态,还原对应的icon state */ @OverrIDe protected voID onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mMaterialMenuIcon.syncState(savedInstanceState); } /** * 根据onSaveInstanceState回调的状态,保存当前icon state */ @OverrIDe protected voID onSaveInstanceState(Bundle outState) { mMaterialMenuIcon.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } /** * 加载菜单 */ @OverrIDe public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main,menu); return true; } }
源码请在这里下载:http://xiazai.jb51.net/201701/yuanma/AndroidDrawerLayout(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的Android组件DrawerLayout仿网易新闻v4.4侧滑菜单全部内容,希望文章能够帮你解决Android组件DrawerLayout仿网易新闻v4.4侧滑菜单所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)