抽屉导航非常火,非常好用,但是怎么实现呢?
创建一个DrawerLayout的布局
首先将 DrawerLayout 作为 根view,然后在其中添加两个子view:
这两个子view一上一下(固定的),分别对应的 主体内容 和 隐藏内容(也就是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">
<!-- 一个可能会动态添加的Fragmentlayout -->
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 一个用作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>
要注意的几点是:
- 主体内容一定是第一个子view,而且高宽必须match_parent
- drawer的内容一定要指明 在水平方向的 gravity,用 start 代替 left 表示这个 drawer是在左边的
- drawer的宽度不能超过320dp
实现一个DrawerList
一般来说 Drawer 都是带有一个 ListView 的,一个Listview也就对应一歌adapter,一个List。下面来看一组简单的实现:
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);
// 给Listview添加一个适配器
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, mPlanetTitles));
// 添加点击事件,添加点击事件
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
...
}
}
处理点击事件
当Drawer的item被点击之后,我们需要将对应的Fragment放入当前的fragmentLayout之中去,代码如下:
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
// 衔接上面的代码,点击之后,根据点击的按钮的位置,选择相应的Fragment
selectItem(position);
}
}
/** 替换主内容中的Fragment */
private void selectItem(int position) {
// 通过position来创建一个fragment,
Fragment fragment = new PlanetFragment();
Bundle args = new Bundle();
args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
// 用这个新创建的fragment替换之前的fragment
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
// 将Listview中选中的那个选项 高亮显示
mDrawerList.setItemChecked(position, true);
// 根据postion设置当前的bar显示的标题
setTitle(mPlanetTitles[position]);
// 将drawer关闭,也就是缩回抽屉
mDrawerLayout.closeDrawer(mDrawerList);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
监听打开和关闭drawer的事件
创建menu
为了实现对drawer的打开和关闭事件的监听,我们要在 DrawerLayout 上添加一个 setDrawerListener 的方法,传入参数为 DrawerLayout.DrawerListener 的一个 实现。
而且,如果你的activity里有actionbar,那么比起一个DrawerLayout.DrawerListener的实现,不如直接继承类 ActionBarDrawerToggle 。这个类也实现的需要的接口。同时 它也确定一些 actionbar的图标 和 导航drawer 之间的行为
比如 : 通过点击bar上的按钮来打开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) {
/** 当drawer处于完全关闭的情况下调用 */
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // 这是个静态方法吗?
}
/** 当drawer处于完全打开的情况下调用 */
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu();
}
};
// 把这个toggle设置为这个drawer的listener
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
// 以下两个方法可以创建一个menu在actionbar中
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
/* Called whenever we call invalidateOptionsMenu() */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 获取当前的Drawer是打开的还是关闭的
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
// 如果是开的,那就隐藏对应的view,如果是关的那么就要显示对应的view
menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
}
打开和关闭 actionbar icon
衔接上面对drawer的打开和关闭的监听。
一般来说,用户打开drawer都是通过,手势或者点击边缘。但是如果你使用了action bar 那么你也得允许用户通过 action bar 来打开和关闭 drawer.
而且这个 icon 应该是 表明是可以打开和关闭drawer的特殊 icon
还有,不管你是否要使用ActionbarToggle的子类,你都必须在程序的一些地方调用你都ActionbarToggle
代码如下:
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);
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
// ?
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
}
// 这个方法是在activity初始化完成之后调用的,一般不实现这个方法。这个是让一些系统类做最后的初始化的
// 注意,自定义的代码一定要写在 super方法之后
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// 同步开关状态
mDrawerToggle.syncState();
}
// 这个方法是在设备参数改变的时候调用的,比如:屏幕方向倒置等
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
// 当有menu种的item被选中的时候调用
@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);
}
...
}
在这个案例中,官方给的控件有些已经显示为不支持了,如果感兴趣可以去github搜索第三方的drawer来使用