#爱加密杯#Navigation Drawer详解-Google推出的用来取代Sliding Menu的控件(一)

相信 Sliding Menu 很多人都用过,在 Android iOS app 中,越来越多的开发者都会把自己的菜单界面放在一个列表里,然后让用户通过向右(或者向左)滑动的操作看到应用所有的功能。 Google 官方的应用也基本都选择了这种交互方式,不同的是, Google 使用的是 Navigation Drawer ,而我们大部分用的还是 Sliding Menu
大家对 Sliding Menu 这个开源项目可能已经很熟悉了,但是 Navigation Drawer 我们有些童鞋可能了解的还比较少,它是 Google I/O 2013 刚推出不久的一个在 support v4 包里面的一个控件,下面我就通过一个 demo 跟大家介绍一下 Navigation Drawer 的使用方法。


这个 demo google 官方的,大家可以到这里下载一下: http://developer.android.com/training/implementing-navigation/nav-drawer.html ,我下面写的代码说明也基本就是翻译了一下这个教程,英语比较好的童鞋建议还是直接看官方的吧。
创建一个抽屉
导航抽屉是一个位于屏幕左侧边缘用来显示应用程序导航项的一个面板。导航抽屉在大部分时间是不显示的,但两种情况下会进行显示:一是发生从屏幕左侧边缘向右滑的手势,二是点击了工具栏中应用图标。导航抽屉在 SupportLibrary   中提供支持,在使用导航抽屉时,需要符合导航抽屉设计原则( NavigationDrawer ),看看你是否有必要创建导航抽屉  



创建抽屉布局
如果你要添加一个导航抽屉,需要用 DrawerLayout 来作为用户界面的根视图, DrawerLayout 视图下需放置两个子视图,一个是用来显示显示屏幕的主体内容(导航抽屉隐藏的时候),一个是用来显示导航抽屉。



用来显示屏幕主体内容的视图一般是 FrameLayout (运行的时候,会被一个 Fragment 填充),用来显示导航抽屉的视图一般是一个 ListView ,如下所示
  1. <android.support.v4.widget.DrawerLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/drawer_layout"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent">
  6. <!-- The main content view -->
  7. <FrameLayout
  8. android:id="@+id/content_frame"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent" />
  11. <!-- The navigation drawer -->
  12. <ListView android:id="@+id/left_drawer"
  13. android:layout_width="240dp"
  14. android:layout_height="match_parent"
  15. android:layout_gravity="start"
  16. android:choiceMode="singleChoice"
  17. android:divider="@android:color/transparent"
  18. android:dividerHeight="0dp"
  19. android:background="#111"/>
  20. </android.support.v4.widget.DrawerLayout>
复制代码

上面的布局说明了导航抽屉的布局一些非常重要的特点:
1 、显示主体内容的视图必须是 DrawerLayout 下的第一个子视图,因为抽屉视图必须在主体内容视图的上方(意味着 DrawerLayout 是一个以 z 轴来布局的控件)
2 、显示主体内容的视图必须设置为匹配父视图的高和宽,因为当抽屉视图隐藏的时候显示主体内容的视图代表了整个用户界面
      3 、抽屉视图的 layout_gravity 属性值需为 “start” To support right-to-left (RTL) languages,specify the value with "start" insteadof "left" (so the drawer appears on the right when thelayout is RTL)
      4 、抽屉视图的宽度不宜匹配父视图,应当适当的窄一点,这样就能在抽屉显示的时候还能看到主体内容视图的一部分
初始化抽屉列表
抽屉视图一般包含一个 ListView (具体包含的 View 取决于你的应用),该 ListView 和平常没什么两样,都需要一个 Adapter 来填充,也可设置单项选中监听器
  1. public class MainActivity extends Activity {
  2. private String[] mPlanetTitles;
  3. private DrawerLayout mDrawerLayout;
  4. private ListView mDrawerList;
  5. ...

  6. @Override
  7. public void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);

  10. mPlanetTitles = getResources().getStringArray(R.array.planets_array);
  11. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
  12. mDrawerList = (ListView) findViewById(R.id.left_drawer);

  13. // Set the adapter for the list view
  14. mDrawerList.setAdapter(new ArrayAdapter<String>(this,
  15. R.layout.drawer_list_item, mPlanetTitles));
  16. // Set the list's click listener
  17. mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

  18. ...
  19. }
  20. }
复制代码

处理导航项选点击事件
导航项的点击事件其实就是包含的 ListView 项的点击事件,我们需要根据点击的项来相应的改变主体内容,记得上面说过显示主体内容的 View 运行时一般会是一个 Fragment ,所以只要把当前的 Fragment 替换成相应的 Fragment 就行了
  1. private class DrawerItemClickListener implements ListView.OnItemClickListener {
  2. @Override
  3. public void onItemClick(AdapterView parent, View view, int position, long id) {
  4. selectItem(position);
  5. }
  6. }

  7. /** Swaps fragments in the main content view */
  8. private void selectItem(int position) {
  9. // Create a new fragment and specify the planet to show based on position
  10. Fragment fragment = new PlanetFragment();
  11. Bundle args = new Bundle();
  12. args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
  13. fragment.setArguments(args);

  14. // Insert the fragment by replacing any existing fragment
  15. FragmentManager fragmentManager = getFragmentManager();
  16. fragmentManager.beginTransaction()
  17. .replace(R.id.content_frame, fragment)
  18. .commit();

  19. // Highlight the selected item, update the title, and close the drawer
  20. mDrawerList.setItemChecked(position, true);
  21. setTitle(mPlanetTitles[position]);
  22. mDrawerLayout.closeDrawer(mDrawerList);
  23. }

  24. @Override
  25. public void setTitle(CharSequence title) {
  26. mTitle = title;
  27. getActionBar().setTitle(mTitle);
  28. }
复制代码

监听导航抽屉打开和关闭事件
可以为 DrawerLayout 设置  DrawerLayout.DrawerListener 监听器来监听打开和关闭事件,当导航抽屉打开和关闭时分别会回调 onDrawerOpened()  onDrawerClosed()  方法
但是如果你的 Activity 包含 Action Bar 话,你可以选择  ActionBarDrawerToggle  来替代  DrawerListener ,ActionBarDrawerToggle  实现了 DrawerListener 接口,所以抽屉的打开和关闭事件照样能监听的到,并且使用 ActionBarDrawerToggle 能促进 Action Bar Icon 和导航抽屉之间的交互(通过点击 icon 来打开和关闭导航抽屉),当导航抽屉打开或关闭时 Action Bar 的状态也应该做相应的改变(  NavigationDrawer  中有介绍)
  1. public class MainActivity extends Activity {
  2. private DrawerLayout mDrawerLayout;
  3. private ActionBarDrawerToggle mDrawerToggle;
  4. private CharSequence mDrawerTitle;
  5. private CharSequence mTitle;
  6. ...

  7. @Override
  8. public void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. ...

  12. mTitle = mDrawerTitle = getTitle();
  13. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
  14. mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
  15. R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {

  16. /** Called when a drawer has settled in a completely closed state. */
  17. public void onDrawerClosed(View view) {
  18. getActionBar().setTitle(mTitle);
  19. invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
  20. }

  21. /** Called when a drawer has settled in a completely open state. */
  22. public void onDrawerOpened(View drawerView) {
  23. getActionBar().setTitle(mDrawerTitle);
  24. invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
  25. }
  26. };

  27. // Set the drawer toggle as the DrawerListener
  28. mDrawerLayout.setDrawerListener(mDrawerToggle);
  29. }

  30. /* Called whenever we call invalidateOptionsMenu() */
  31. @Override
  32. public boolean onPrepareOptionsMenu(Menu menu) {
  33. // If the nav drawer is open, hide action items related to the content view
  34. boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
  35. menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
  36. return super.onPrepareOptionsMenu(menu);
  37. }
  38. }
复制代码
点击应用图标来打开和关闭导航抽屉
前面我们是介绍过通过手势来打开和关闭导航抽屉,但是如果 Activity 包含 Action Bar 的话,当我们点击应用图标时也能打开和关闭导航抽屉,而且我们也需要通过图标来指示导航抽屉当前的状态,如果我们是使用 ActionBarDrawerToggle 类的话,可以通过设置构造方法的参数来做到这一点,它的构造方法参数有五个,分别代表:宿主 Activity DrawerLayout 、导航抽屉打开时应用图标、导航抽屉打开时描述文本、导航抽屉关闭时描述文本
还有一点要注意的是,当手机屏幕的配置环境发生变化时(横竖屏切换),导航抽屉的配置也需改变,当宿主 Activity onRestoreInstanceState 方法调用的时候,导航抽屉的状态也需进行同步,可在 onPostCreate 方法中进行同步,具体的可以看如下代码
  1. public class MainActivity extends Activity {
  2. private DrawerLayout mDrawerLayout;
  3. private ActionBarDrawerToggle mDrawerToggle;
  4. ...

  5. public void onCreate(Bundle savedInstanceState) {
  6. ...

  7. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
  8. mDrawerToggle = new ActionBarDrawerToggle(
  9. this, /* host Activity */
  10. mDrawerLayout, /* DrawerLayout object */
  11. R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
  12. R.string.drawer_open, /* "open drawer" description */
  13. R.string.drawer_close /* "close drawer" description */
  14. ) {

  15. /** Called when a drawer has settled in a completely closed state. */
  16. public void onDrawerClosed(View view) {
  17. getActionBar().setTitle(mTitle);
  18. }

  19. /** Called when a drawer has settled in a completely open state. */
  20. public void onDrawerOpened(View drawerView) {
  21. getActionBar().setTitle(mDrawerTitle);
  22. }
  23. };

  24. // Set the drawer toggle as the DrawerListener
  25. mDrawerLayout.setDrawerListener(mDrawerToggle);

  26. getActionBar().setDisplayHomeAsUpEnabled(true);
  27. getActionBar().setHomeButtonEnabled(true);
  28. }

  29. @Override
  30. protected void onPostCreate(Bundle savedInstanceState) {
  31. super.onPostCreate(savedInstanceState);
  32. // Sync the toggle state after onRestoreInstanceState has occurred.
  33. mDrawerToggle.syncState();
  34. }

  35. @Override
  36. public void onConfigurationChanged(Configuration newConfig) {
  37. super.onConfigurationChanged(newConfig);
  38. mDrawerToggle.onConfigurationChanged(newConfig);
  39. }

  40. @Override
  41. public boolean onOptionsItemSelected(MenuItem item) {
  42. // Pass the event to ActionBarDrawerToggle, if it returns
  43. // true, then it has handled the app icon touch event
  44. if (mDrawerToggle.onOptionsItemSelected(item)) {
  45. return true;
  46. }
  47. // Handle your other action bar items...

  48. return super.onOptionsItemSelected(item);
  49. }

  50. ...
  51. }
复制代码

几张运行的截图:

 
源码下载:
  drawer.rar (2.29 MB, 下载次数: 32) 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值