Navigation Drawer是從屏幕的左側滑出,顯示應用導航的視圖。官方是這樣定義的:
- The navigation drawer is a panel that displays the app’s main
- navigation options on the left edge of the screen. It is hidden
- most of the time, but is revealed when the user swipes a finger
- from the left edge of the screen or, while at the top level of the
- app, the user touches the app icon in the action bar.
如下圖指示:
Navigation Drawer不同於SlidingDrawer,它不存在可以拖動的handle;它也不同於SlidingMenu,Navigation
Drawer滑出時主屏幕視圖不一定。Navigation Drawer是覆蓋在主視圖上的。
Navigation Drawer是Android團對在2013 google IO大會期間更新的Support庫(V13)中新加入的重要的功能。實現現Navigation Drawer需要使用最新支持庫(V13)的DrawerLayout。Navigation Drawer的設計指南請參考
Navigation
Drawer design guide
Navigation Drawer develop guide
Create a Drawer Layout
創建Navigation Drawer需要用
DrawerLayout
作爲界面根控件。在DrawerLayout裏面第一個View爲當前界面主內容;第二個和第三個View爲Navigation
Drawer內容。如果當前界面只需要一個Navigation
Drawer,則第三個View可以省略。
下面的例子中DrawerLayout裏面包含兩個View,第一個FrameLayout中是當前界面主要內容顯示區域;第二個ListView爲Navigation
Drawer內容。
- <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、顯示界面主要內容的View (上面的
FrameLayout
) 必須爲DrawerLayout的第一個子View, 原因在於 XML 佈局文件中的View順序爲Android系統中的 z-ordering順序,而Navigation Drawer必須出現在內容之上。 - 2、顯示界面內容的View寬度和高度設置爲和父View一樣,原因在於當Navigation Drawer不可見的時候,界面內容代表整個界面UI。
- 3、Navigation Drawer (上面的
ListView
) 必須使用android:layout_gravity屬性設置水平的 gravity值 .如果要支持 right-to-left (RTL,從右向左閱讀)語言 用"start"
代替"left"
(當在 RTL語言運行時候,菜單出現在右側)。 - 4、抽屜菜單的寬度爲
dp
單位而高度和父View一樣。抽屜菜單的寬度應該不超過320dp,這樣用戶可以在菜單打開的時候看到部分內容界面。
- 1、顯示界面主要內容的View (上面的
Initialize the Drawer List
在您的Activity中需要先初始化Navigation Drawer內容,根據您的應用需要Navigation Drawer的內容可能不是ListView,可以使用其他View。
在上面的示例中,我們需要給Navigation Drawer的ListView設置一個Adapter來提供數據。如下所示:
- public class MainActivity extends Activity {
- private String[] mPlanetTitles;
- private DrawerLayout mDrawerLayout;
- private ListView mDrawerList;
- ...
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mPlanetTitles = getResources().getStringArray(R.array.planets_array);
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- 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());
- ...
- }
- }
上面的代碼調用了
setOnItemClickListener()
函數來接受Navigation
Drawer點擊事件。下面會介紹如何通過點擊Navigation
Drawer顯示主界面內容。Handle Navigation Click Events
當用戶選擇Navigation
Drawer List中的條目時,系統會調用 OnItemClickListener的
onItemClick()函數。
根據您的應用需要,onItemClick函數的實現方式可能不同。下面的示例中,選擇Navigation
Drawer條目會在程序主界面中插入不同的 Fragment
。
- 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
- mDrawerList.setItemChecked(position, true);
- setTitle(mPlanetTitles[position]);
- mDrawerLayout.closeDrawer(mDrawerList);
- }
- @Override
- public void setTitle(CharSequence title) {
- mTitle = title;
- getActionBar().setTitle(mTitle);
- }
Listen for Open and Close Events
如果需要監聽菜單打開關閉事件,則需要調用 DrawerLayout類的
setDrawerListener()
函數,參數爲 DrawerLayout.DrawerListener接口的實現。
該接口提供了菜單打開關閉等事件的回調函數,例如 onDrawerOpened()
和onDrawerClosed()
.
如果您的Activity使用了 action bar,則您可以使用Support庫提供的 ActionBarDrawerToggle
類,該類實現了 DrawerLayout.DrawerListener接口,並且您還可以根據需要重寫相關的函數。該類實現了菜單和Action
bar相關的操作。
根據在 Navigation Drawer 設計指南中的介紹,當菜單顯示的時候您應該根據情況隱藏ActionBar上的功能菜單並且修改ActionBar的標題。下面的代碼演示瞭如何重寫 ActionBarDrawerToggle
類的相關函數來實現該功能。
- 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);
- }
- }
Open and Close with the App Icon
用戶可以從屏幕邊緣滑動來打開Navigation
Drawer,如果您使用了 action bar,應該讓用戶通過點擊應用圖標也可以打開抽屜菜單。並且應用圖標也應該使用一個特殊的圖標來指示抽屜菜單。您可以使用 ActionBarDrawerToggle
類來實現這些功能。
使用 ActionBarDrawerToggle
,先通過其構造函數來創建該對象,構造函數需要如下參數:
- 1)顯示Navigation
Drawer的
Activity
對象 - 2)
DrawerLayout 對象
- 3)一個用來指示Navigation Drawer的 drawable資源
- 4)一個用來描述打開Navigation Drawer的文本 (用於支持可訪問性)。
- 5)一個用來描述關閉Navigation Drawer的文本(用於支持可訪問性).
- 1)顯示Navigation
Drawer的
無論你是否繼承 ActionBarDrawerToggle
來實現Navigation
Drawer監聽器,您都需要在Activity的生命週期函數中調用ActionBarDrawerToggle
的一些函數。
如下所示:
- 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);
- }
- ...
- }
代碼下載:demo
參考:
https://developer.android.com/reference/android/support/v4/widget/DrawerLayout.html
http://developer.android.com/training/implementing-navigation/nav-drawer.html