創建一個導航抽屜
導航抽屜是一個位於屏幕左側邊緣用來顯示應用程序導航項的一個面板。導航抽屜在大部分時間是不顯示的,但兩種情況下會進行顯示:一是發生從屏幕左側邊緣向右滑的手勢,二是點擊了工具欄中應用圖標。導航抽屜在Support Library 中提供支持,在使用導航抽屜時,需要符合導航抽屜設計原則(Navigation Drawer),看看你是否有必要創建導航抽屜 。
創建抽屜布局
如果你要添加一個導航抽屜,需要用DrawerLayout來作為用戶界面的根視圖,DrawerLayout視圖下需放置兩個子視圖,一個是用來顯示顯示屏幕的主體內容(導航抽屜隱藏的時候),一個是用來顯示導航抽屜。
用來顯示屏幕主體內容的視圖一般是FrameLayout(運行的時候,會被一個Fragment填充),用來顯示導航抽屜的視圖一般是一個ListView,如下所示
- <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>
<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來填充,也可設置單項選中監聽器
- 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());
- ...
- }
- }
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就行了
- 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);
- }
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 中有介紹)
- 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);
- }
- }
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方法中進行同步,具體的可以看如下代碼
- 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);
- }
- ...
- }
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); } ... }

