【转】Navigation Drawer(导航抽屉)

  1. 云栖社区>
  2. 博客>
  3. 正文

【转】Navigation Drawer(导航抽屉)

cxycappuccino 2013-12-12 20:49:00 浏览839
展开阅读全文
  1. 创建一个导航抽屉
    1. 创建抽屉布局
    2. 初始化抽屉列表
    3. 处理导航项选点击事件
    4. 监听导航抽屉打开和关闭事件
    5. 点击应用图标来打开和关闭导航抽屉 
 

创建一个导航抽屉

导航抽屉是一个位于屏幕左侧边缘用来显示应用程序导航项的一个面板。导航抽屉在大部分时间是不显示的,但两种情况下会进行显示:一是发生从屏幕左侧边缘向右滑的手势,二是点击了工具栏中应用图标。导航抽屉在Support Library  中提供支持,在使用导航抽屉时,需要符合导航抽屉设计原则(Navigation Drawer),看看你是否有必要创建导航抽屉 。
 

创建抽屉布局

如果你要添加一个导航抽屉,需要用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>  
<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">
    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!-- The navigation drawer -->
    <ListView android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
</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" instead of "left" (so the drawer appears on the right when the layout is RTL)
      4、抽屉视图的宽度不宜匹配父视图,应当适当的窄一点,这样就能在抽屉显示的时候还能看到主体内容视图的一部分
 

初始化抽屉列表

抽屉视图一般包含一个ListView(具体包含的View取决于你的应用),该ListView和平常没什么两样,都需要一个Adapter来填充,也可设置单项选中监听器
  1. public class MainActivity extends Activity {  
  2.     private String[] mPlanetTitles;  
  3.     private ListView mDrawerList;  
  4.     ...  
  5.   
  6.     @Override  
  7.     public void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.activity_main);  
  10.   
  11.         mPlanetTitles = getResources().getStringArray(R.array.planets_array);  
  12.         mDrawerList = (ListView) findViewById(R.id.left_drawer);  
  13.   
  14.         // Set the adapter for the list view  
  15.         mDrawerList.setAdapter(new ArrayAdapter<String>(this,  
  16.                 R.layout.drawer_list_item, mPlanetTitles));  
  17.         // Set the list's click listener  
  18.         mDrawerList.setOnItemClickListener(new DrawerItemClickListener());  
  19.   
  20.         ...  
  21.     }  
  22. }  
public class MainActivity extends Activity {
    private String[] mPlanetTitles;
    private ListView mDrawerList;
    ...

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

        mPlanetTitles = getResources().getStringArray(R.array.planets_array);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

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

        ...
    }
}

处理导航项选点击事件

导航项的点击事件其实就是包含的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.   
  8. /** Swaps fragments in the main content view */  
  9. private void selectItem(int position) {  
  10.     // Create a new fragment and specify the planet to show based on position  
  11.     Fragment fragment = new PlanetFragment();  
  12.     Bundle args = new Bundle();  
  13.     args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);  
  14.     fragment.setArguments(args);  
  15.   
  16.     // Insert the fragment by replacing any existing fragment  
  17.     FragmentManager fragmentManager = getFragmentManager();  
  18.     fragmentManager.beginTransaction()  
  19.                    .replace(R.id.content_frame, fragment)  
  20.                    .commit();  
  21.   
  22.     // Highlight the selected item, update the title, and close the drawer  
  23.     mDrawer.setItemChecked(position, true);  
  24.     setTitle(mPlanetTitles[position]);  
  25.     mDrawerLayout.closeDrawer(mDrawer);  
  26. }  
  27.   
  28. @Override  
  29. public void setTitle(CharSequence title) {  
  30.     mTitle = title;  
  31.     getActionBar().setTitle(mTitle);  
  32. }  
private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id) {
        selectItem(position);
    }
}

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

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

    // Highlight the selected item, update the title, and close the drawer
    mDrawer.setItemChecked(position, true);
    setTitle(mPlanetTitles[position]);
    mDrawerLayout.closeDrawer(mDrawer);
}

@Override
public void setTitle(CharSequence title) {
    mTitle = title;
    getActionBar().setTitle(mTitle);
}

监听导航抽屉打开和关闭事件

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

  1. public class MainActivity extends Activity {  
  2.     private DrawerLayout mDrawerLayout;  
  3.     private ActionBarDrawerToggle mDrawerToggle;  
  4.     private CharSequence mDrawerTitle;  
  5.     private CharSequence mTitle;  
  6.     ...  
  7.   
  8.     @Override  
  9.     public void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.activity_main);  
  12.         ...  
  13.   
  14.         mTitle = mDrawerTitle = getTitle();  
  15.         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);  
  16.         mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,  
  17.                 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {  
  18.   
  19.             /** Called when a drawer has settled in a completely closed state. */  
  20.             public void onDrawerClosed(View view) {  
  21.                 getActionBar().setTitle(mTitle);  
  22.                 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()  
  23.             }  
  24.   
  25.             /** Called when a drawer has settled in a completely open state. */  
  26.             public void onDrawerOpened(View drawerView) {  
  27.                 getActionBar().setTitle(mDrawerTitle);  
  28.                 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()  
  29.             }  
  30.         };  
  31.   
  32.         // Set the drawer toggle as the DrawerListener  
  33.         mDrawerLayout.setDrawerListener(mDrawerToggle);  
  34.     }  
  35.   
  36.     /* Called whenever we call invalidateOptionsMenu() */  
  37.     @Override  
  38.     public boolean onPrepareOptionsMenu(Menu menu) {  
  39.         // If the nav drawer is open, hide action items related to the content view  
  40.         boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);  
  41.         menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);  
  42.         return super.onPrepareOptionsMenu(menu);  
  43.     }  
  44. }  
public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    ...

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

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

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

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

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

    /* Called whenever we call invalidateOptionsMenu() */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the nav drawer is open, hide action items related to the content view
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }
}
 

点击应用图标来打开和关闭导航抽屉 

前面我们是介绍过通过手势来打开和关闭导航抽屉,但是如果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.   
  6.     public void onCreate(Bundle savedInstanceState) {  
  7.         ...  
  8.   
  9.         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);  
  10.         mDrawerToggle = new ActionBarDrawerToggle(  
  11.                 this,                  /* host Activity */  
  12.                 mDrawerLayout,         /* DrawerLayout object */  
  13.                 R.drawable.ic_drawer,  /* nav drawer icon to replace 'Up' caret */  
  14.                 R.string.drawer_open,  /* "open drawer" description */  
  15.                 R.string.drawer_close  /* "close drawer" description */  
  16.                 ) {  
  17.   
  18.             /** Called when a drawer has settled in a completely closed state. */  
  19.             public void onDrawerClosed(View view) {  
  20.                 getActionBar().setTitle(mTitle);  
  21.             }  
  22.   
  23.             /** Called when a drawer has settled in a completely open state. */  
  24.             public void onDrawerOpened(View drawerView) {  
  25.                 getActionBar().setTitle(mDrawerTitle);  
  26.             }  
  27.         };  
  28.   
  29.         // Set the drawer toggle as the DrawerListener  
  30.         mDrawerLayout.setDrawerListener(mDrawerToggle);  
  31.   
  32.         getActionBar().setDisplayHomeAsUpEnabled(true);  
  33.         getActionBar().setHomeButtonEnabled(true);  
  34.     }  
  35.   
  36.     @Override  
  37.     protected void onPostCreate(Bundle savedInstanceState) {  
  38.         super.onPostCreate(savedInstanceState);  
  39.         // Sync the toggle state after onRestoreInstanceState has occurred.  
  40.         mDrawerToggle.syncState();  
  41.     }  
  42.   
  43.     @Override  
  44.     public void onConfigurationChanged(Configuration newConfig) {  
  45.         super.onConfigurationChanged(newConfig);  
  46.         mDrawerToggle.onConfigurationChanged(newConfig);  
  47.     }  
  48.   
  49.     @Override  
  50.     public boolean onOptionsItemSelected(MenuItem item) {  
  51.         // Pass the event to ActionBarDrawerToggle, if it returns  
  52.         // true, then it has handled the app icon touch event  
  53.         if (mDrawerToggle.onOptionsItemSelected(item)) {  
  54.           return true;  
  55.         }  
  56.         // Handle your other action bar items...  
  57.   
  58.         return super.onOptionsItemSelected(item);  
  59.     }  
  60.   
  61.     ...  
  62. }  
public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    ...

    public void onCreate(Bundle savedInstanceState) {
        ...

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

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

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

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

        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);
    }

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

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

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

        return super.onOptionsItemSelected(item);
    }

    ...
}
 
程序运行结果如下

 
 
 

网友评论

登录后评论
0/500
评论
cxycappuccino
+ 关注