抽屉导航是一个在应用程序主界面左侧显示的面板,它多数时候处于隐藏状态,用户可以通过用手指从屏幕左侧拖动或者点击Acion Bar的应用程序图标来呼出抽屉导航。
本课介绍如何使用android-support-v4.jar中的DrawerLayout来实现抽屉导航功能。
创建一个抽屉布局
为了在你的程序中实现抽屉导航功能,需要在你的窗口布局中定义一个DrawerLayout对象作为根视图,并且需要添加一个包含界面显示内容的视图(当抽屉导航处于隐藏状态下窗口所显示的视图)和包含抽屉导航显示内容的视图作为DrawerLayout的子视图。
比如,如下所示的布局就是将DrawerLayout作为布局的跟标签,包含一个FrameLayout帧布局作为内容显示视图和一个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">
<!—窗口主视图 -->
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!—抽屉导航视图 -->
<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>
上面这个布局展示了抽屉导航布局的一些重要布局特点:
界面的内容显示视图(FrameLayout)必须是DrawerLayout的第一个子视图。
因为在抽屉导航处于隐藏状态时内容显示视图作为窗口的主界面,所以内容显示视图应设置为填充父布局。
抽屉显示视图(ListView)必须通过android:layout_gravity指定其水平Gravity属性。为了支持从右到左的语言,应该将android:layout_gravity属性指定为”start”而不是”left”,所以当布局模式为从右到左时抽屉显示在窗口右边。
初始化抽屉列表
在Acitivity里面,应该首先初始化抽屉视图。你如何处理取决于你程序的显示情况。但多数时候抽屉导航都是有ListView组成的,所以应该为抽屉列表指定一个Adapter(比如ArrayAdapter或者SimpleCursonAdapter)。
比如,如下所示的代码将教会你通过字符串数组初始化抽屉导航列表: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);
// 为抽屉导航列表设置适配器
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, mPlanetTitles));
// 为列表点击设置监听器
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
...
}
}
上面的代码通过调用setOnItemClickListener()方法监听抽屉导航列表项的点击事件,下面将介绍如何实现这个接口并在选择抽屉列表项时更新内容显示视图。
处理抽屉导航点击事件
当用户选择抽屉列表的某一项时,系统将调用OnItemClickListener接口的onItemClick()方法。
在onItemClick()方法中你做什么取决于如何实现你应用程序的结构,在下面的示例中,每选中列表的一项将在内容显示视图插入一个不同的Fragment(FrameLayout标签通过R.id.content_frame定义):
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
selectItem(position);
}
}
/** 在主视图中切换Fragment */
private void selectItem(int position) {
// 创建一个fragment并根据列表项的位置指定fragment的显示内容
Fragment fragment = new PlanetFragment();
Bundle args = new Bundle();
args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
// 将该fragement替换掉原fragment
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
// 高亮选中项,更新标题并且关闭抽屉
mDrawerList.setItemChecked(position, true);
setTitle(mPlanetTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
监听抽屉打开和关闭事件
通过实现DrawerLayout.DrawerListener接口来监听抽屉的打开和关闭事件。这个接口提供了如onDrawerOpened()和onDrawerClosed()。
除了通过实现DrawerLayout.DrawerListener接口来监听抽屉的打开和关闭事件外,如果你的窗口中包含action bar,你可以通过几成ActionBarDrawerToggle类来实现同样的功能,ActionBarDrawerToggle同样实现了DrawerLayout.DrawerListener接口,但它更利于action和抽屉导航栏的交互(将在下一部分讨论)。
正如在Navagation Drawer设计范式中讨论的一样,你可以在抽屉导航处于可见状态时修改action bar的显示内容,比如改变标题等。下例中的代码展示了怎样通过ActionBarDrawerToggle实例复写DrawerLayout.DrawerListener回调方法。
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) {
/** 在抽屉导航完全处于关闭状态时调用. */
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
/** 在抽屉导航完全处于打开状态时调用. */
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);
}
}
通过应用程序图标打开和关闭抽屉
用户可以通过从屏幕左边缘滑动的方式来开启和关闭抽屉导航,如果你同时也使用了action bar,你也需要在当用户点击应用程序图标时实现打开和关闭抽屉导航的功能。也要指定一个特殊的图标来指示抽屉导航的存在,你可以通过使用ActionBarDrawerToggle方法来显示之前的选择。
为了使ActionBarDrawerToggle能够工作,通过其带参构造方法实例化该类,必须包含如下参数:
抽屉导航附属的窗口实例
DrawerLayout实例
指示抽屉的图标资源
描述“打开抽屉”动作的字符串资源
描述“关闭抽屉”动作的字符串资源
然后,你是否创建了一个ActionBarDrawerToggle的子类作为你对抽屉的监听,你需要在你窗口的生命周期回调中调用ActionBarDrawerToggle的相关方法。
publicclassMainActivityextendsActivity{
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 thedrawer toggle as the DrawerListener
mDrawerLayout.setDrawerListener(mDrawerToggle);
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
}
@Override
protected void onPostCreate(Bundle savedInstanceState){
super.onPostCreate(savedInstanceState);
// Syncthe 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){
// Passthe event to ActionBarDrawerToggle, if it returns
// true,then it has handled the app icon touch event
if (mDrawerToggle.onOptionsItemSelected(item)){
return true;
}
// Handleyour other action bar items...
return super.onOptionsItemSelected(item);
}
...
}
详细地示例请在本页面的顶部下载。
原文:Creating a Navigation Drawer
关于抽屉导航的设计,极客公园的这篇文章不错:Android Design 趋势——Navigation Drawer第一次翻译文档,难免会有很多错误,呵呵。