一.导航抽屉Navigation Drawer
想必用过Android的人都熟悉导航抽屉,这算是一个比较经典的导航方式了,个人也非常喜欢这种导航方式。下面介绍这种导航方式的开发方法。
创建一个抽屉布局DrawerLayout
这里有一些需要注意的地方:
- 要添加一个导航抽屉,首先应该用support库提供的DrawerLayout布局来作为你的主布局的根布局,在根布局中,应该包含显示主要内容的布局和drawer的布局。
- 第一个布局必须是你的主要内容的布局(因为XML文件是根据z轴的顺序来显示,而抽屉要在主要内容的上面)
- 主要内容布局需要将长宽都设置为铺满父容器,因为当抽屉隐藏时,它是主要显示的容器。
- 抽屉view(the ListView)必须指定horizontal_gravity,通过设置
android:layout_gravity
。为了支持从右向左的语言,最好指定值为"start"
而不是left
。 - 为了让用户能看见主页面的一部分,抽屉view的宽度应该用
dp
来设置,并且尽量不要超过320dp。
初始化Drawer List
在你的activity中,第一件事就应该去初始化导航抽屉列表的每一项。不管你怎么去组织你的app内容,一个导航抽屉通常都是由一个列表组成的,所以我们应该从一个适配器Adapter
开始入手(比如ArrayAdapter
或者SimpleCursorAdapter
)。
例如,这段代码演示了如何通过一个string array来初始化你的列表。
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); // 为list设置一个适配器
mDrawerList.setAdapter(new ArrayAdapter(this, R.layout.drawer_list_item, mPlanetTitles)); // 设置一个监听器
mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); ...
}
}
通过setOnItemClickListener()
接收导航抽屉的列表点击事件。接下来展示如何实现这个接口和当选择一项时改变主页面。
处理导航点击事件
当用户点击抽屉的列表时,系统会在你通过setOnItemClickListener()
设置的监听器OnItemClickListener()
上调用onItemClick()
你的onItemClick()
的功能取决于你的app的功能。在下面的例子中,选择每一项都会动态的插入一个不同的Fragment
到主页面(FrameLayout
)中。
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) {
// 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);
// 通过替换已存在的fragment来插入
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()// 开始事务
.replace(R.id.content_frame, fragment)// 替换
.commit();// 提交
// 高亮选中项,设置标题,关闭抽屉
mDrawerList.setItemChecked(position, true);
setTitle(mPlanetTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
@Overridepublic void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
监听打开和关闭抽屉事件
要监听打开和关闭事件,首先应该在你的DrawerLayout
上设置监听器setDrawerListener()
,还要实现DrawerLayout.DrawerListener
,这个接口提供回调函数,比如 onDrawerOpened()
和onDrawerClosed()
。
回调函数解析
然而,与其实现这个接口,如果你的activity包含了actionbar
,你可以替换为使用ActionBarDrawerToggle
类,这个类实现了DrawerLayout.DrawerListener
这个接口,你仍可以去重载那些回调函数,但是使用ActionBarDrawerToggle可以促进action bar的图标和导航抽屉的正确的交互。
当抽屉可见时,你应该改变action bar的内容,像是改变标题,移除action items来适应主页面的内容,下面的代码通过一个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) { super.onDrawerClosed(view);
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // 创建调用onPrepareOptionsMenu()
}
/** 当抽屉完全打开时调用 */
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // 创建调用onPrepareOptionsMenu()
}
};
// 设置toggle为抽屉监听器
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
/* 当调用invalidateOptionsMenu()时调用 */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 如果导航抽屉式打开的,根据内容隐藏action items
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
}
下个部分介绍ActionBarDrawerToggle
的构造函数参数和与action icon交互的其他要求。
通过App Icon来打开和关闭抽屉
用户能够通过右滑来打开抽屉,但是如果你使用action bar,那么你也应该允许用户通过点击App Icon来打开和关闭抽屉。同时也应该通过一个特殊的icon来显示隐藏抽屉的存在。你能够通过之前介绍的ActionBarDrawerToggle
来实现。
为了使ActionBarDrawerToggle
工作,在这里创建一个例子和他的构造函数,他需要以下的元素:
- 托管抽屉的
Activity
DrawerLayout
- 一个图片资源用作抽屉开关的指示器
图片资源 - 一个描述抽屉打开的string(为了用户友好)
- 一个描述抽屉关闭的string(为了用户友好)
不管你是否创建了ActionBarDrawerToggle
的子类作为你抽屉监听器,你需要在你的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) {
super.onDrawerClosed(view);
getActionBar().setTitle(mTitle);
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(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);
}
...
}
最后给一个例子的完整版
导航抽屉完整版
未完待续……