创建一个Navigation Drawer
实例代码地址
navigation drawer是一个位于屏幕左侧用于显示应用主要导航选项的面板。一般情况下它是隐藏着的,当用户从屏幕左侧用手势划一下或者点击action bar上的app icon,它就会显示出来。
通过 Support Library中的 DrawerLayout来实现navigation drawer
。
创建一个Drawer Layout
为了添加一个navigation drawer ,用DrawerLayout布局作为你的Layout的根view来申明你的用户接口。在DrawerLyaout中一般添加两个view:第一个view用来显示主要内容(当drawer被隐藏时你的初始布局),第二个view包含了你的navigation drawer要显示的内容。
例如下面实例就是使用一个DrawerLayout包含两个子view视图:一个FrameLayout包含主要内容(运行时用fragment进行填充),另外一个是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>
上述布局演示了几个很重要的布局特点:
在你的activity中,你要做的第一件事就是初始化导航抽屉中的列表项。你要怎么做完全取决于你的App的内容,但是一个导航抽屉往往包含了一个ListView,所以这个list应该通过一个Adapter来填充(比如 ArrayAdapter
或者 SimpleCursorAdapter
)。
下面是通过一个字符串数组来填充抽屉列表的:
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()); ... } }
在 onItemClick()中你做些什么取决于你是怎么实现你的应用结构的。在本文中的实例中,每选择一个list中的item就在主要内容视图中插入一个不同的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); }
setDrawerListener()给 DrawerLayout设置 DrawerLayout.DrawerListener监听器,实现 DrawerLayout.DrawerListener中的回调方法onDrawerOpened()
和 onDrawerClosed()。
但是,假如你的activity包含有actiion bar,你就应该使用ActionBarDrawerToggle(DrawerListener的子类),因为它可以促进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); } }
The next section describes the ActionBarDrawerToggle
constructor arguments and the other steps required to set it up to handle interaction with the action bar icon.
通过App Icon开启和关闭抽屉
当有action bar时,你就应该实现可以通过App icon来开启和关闭抽屉。而且这个app icon能够预示抽屉是否存在。
创建一个ActionBarDrawerToggle,它的构造函数需要如下参数:
Activity
hosting the drawer.DrawerLayout
.
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); } ... }
Activity中有两个菜单相关的回调方法:onCreateOptionsMenu(Menu menu)和onPrepareOptionsMenu(Menu menu)
onCreateOptionsMenu方法:从App创建到结束只会被调用一次,用来初始化Activity的标准选项菜单的内容;当我们需要更新菜单的显示时这个方法明显帮不上什么忙了。
onPrepareOptionsMenu方法:准备屏幕的标准选项菜单,在菜单显示前调用,而且是每次显示前都会被调用,所有这个方法常用来动态改变菜单项
listener = new ActionBarDrawerToggle( this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close){ @Override public void onDrawerClosed(View drawerView) { // title改为mTitle getActionBar().setTitle(mTitle); // 通知修改action bar中的菜单 invalidateOptionsMenu(); } @Override public void onDrawerOpened(View drawerView) { // title改为mDrawerTitle getActionBar().setTitle(mDrawerTitle); // 通知修改action bar中的菜单 invalidateOptionsMenu(); } };