Android UI開發 ——Creating a Navigation Drawer


       Navigation Drawer是從屏幕的左側滑出,顯示應用導航的視圖。官方是這樣定義的:

  1. The navigation drawer is a panel that displays the app’s main   
  2.     navigation options on the left edge of the screen. It is hidden   
  3.     most of the time, but is revealed when the user swipes a finger   
  4.     from the left edge of the screen or, while at the top level of the   
  5.     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內容。

  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、顯示界面主要內容的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,這樣用戶可以在菜單打開的時候看到部分內容界面。

Initialize the Drawer List


       在您的Activity中需要先初始化Navigation Drawer內容,根據您的應用需要Navigation Drawer的內容可能不是ListView,可以使用其他View。


      在上面的示例中,我們需要給Navigation Drawer的ListView設置一個Adapter來提供數據。如下所示:


  1. public class MainActivity extends Activity {  
  2.     private String[] mPlanetTitles;  
  3.     private DrawerLayout mDrawerLayout;  
  4.     private ListView mDrawerList;  
  5.     ...  
  6.   
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.activity_main);  
  11.   
  12.         mPlanetTitles = getResources().getStringArray(R.array.planets_array);  
  13.         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);  
  14.         mDrawerList = (ListView) findViewById(R.id.left_drawer);  
  15.   
  16.         // Set the adapter for the list view  
  17.         mDrawerList.setAdapter(new ArrayAdapter<String>(this,  
  18.                 R.layout.drawer_list_item, mPlanetTitles));  
  19.         // Set the list's click listener  
  20.         mDrawerList.setOnItemClickListener(new DrawerItemClickListener());  
  21.   
  22.         ...  
  23.     }  
  24. }  

      上面的代碼調用了 setOnItemClickListener() 函數來接受Navigation Drawer點擊事件。下面會介紹如何通過點擊Navigation Drawer顯示主界面內容。

Handle Navigation Click Events


       當用戶選擇Navigation Drawer List中的條目時,系統會調用  OnItemClickListener的 onItemClick()函數


       根據您的應用需要,onItemClick函數的實現方式可能不同。下面的示例中,選擇Navigation Drawer條目會在程序主界面中插入不同的 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.     mDrawerList.setItemChecked(position, true);  
  24.     setTitle(mPlanetTitles[position]);  
  25.     mDrawerLayout.closeDrawer(mDrawerList);  
  26. }  
  27.   
  28. @Override  
  29. public void setTitle(CharSequence title) {  
  30.     mTitle = title;  
  31.     getActionBar().setTitle(mTitle);  
  32. }  

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 類的相關函數來實現該功能。


  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. }  

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的文本(用於支持可訪問性).

    無論你是否繼承 ActionBarDrawerToggle 來實現Navigation Drawer監聽器,您都需要在Activity的生命週期函數中調用ActionBarDrawerToggle 的一些函數。

如下所示:


  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. }  


代碼下載:demo

參考:

https://developer.android.com/reference/android/support/v4/widget/DrawerLayout.html

http://developer.android.com/training/implementing-navigation/nav-drawer.html

http://developer.android.com/design/patterns/navigation-drawer.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章