这次练习的重点是DrawerLayout,这个的效果和SlidingMenu很类似,对于SlidingMenu这里不是重点,可以参考github上的开源代码
DrawerLayout是Support Library包中实现了侧滑菜单效果的控件,这里我们首先来讲讲布局文件有什么讲究吧!
我们先来看看布局文件的代码
<span style="font-size:18px;"><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" >
</FrameLayout>
<!-- The navigation view
android:layout_gravity="start"表示从左向右滑出菜单,"end"表示从右向左滑出菜单
不推荐left和right,虽然这两个值也可以使用
抽屉试图宽度不要超过320dp
-->
<ListView
android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#ffffcc"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp" >
</ListView>
</android.support.v4.widget.DrawerLayout></span>
从上面的布局可以很明了的看到,这个布局文件中只有三个标签,如下
<span style="font-size:18px;"><android.support.v4.widget.DrawerLayout >
<!-- The main content view -->
<FrameLayout>
<span style="white-space:pre"> </span>……
</FrameLayout>
<!-- The navigation view -->
<ListView>
<span style="white-space:pre"> </span>……
</ListView>
</android.support.v4.widget.DrawerLayout></span>
一个DrawerLayout标签包含了两个子标签FrameLayout和ListView,注意:FrameLayout先,ListView后
这是有讲究的,一定要注意,我们可以看看API文档中对这个先后顺序的解释
The main content view (the FrameLayout above) must be the first child in the DrawerLayout because the XML order implies z-ordering and the drawer must be on top of the content.
The main content view is set to match the parent view's width and height, because it represents the entire UI when the navigation drawer is hidden.
The drawer view (the ListView) must specify its horizontal gravity with the android:layout_gravity attribute. 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).
The drawer view specifies its width in dp units and the height matches the parent view. The drawer width should be no more than 320dp so the user can always see a portion of the main content.
这里的第一句话就解释了为什么FrameLayout必须在上面,因为XML是遵循Z坐标的,而Drawer必须在内容的上面(z方向上)。同时在最后一句中页讲到了为什么ListView的Width=240dp。剩下的就是一些属性的定义了,就不多讲了。
讲完了布局我们接着就要来Initialize the Drawer List——初始化DrawerList
我先把一些定义的参数变量给出了
<span style="font-size:18px;"> private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ArrayList<String> menuList;
private ArrayAdapter<String> adapter;
private ActionBarDrawerToggle mDrawToggle;
private String mTitle;</span>
以下代码的主要目的就是给ListView添加数据源,即一个适配器Adapter,这里是用ArrayAdapter
<span style="font-size:18px;">mTitle = (String) getTitle();
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
menuList = new ArrayList<String>();
for (int i = 0; i < 5; i++) {
menuList.add("Felix"+i);
}
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, menuList);
mDrawerList.setAdapter(adapter);
mDrawerList.setOnItemClickListener(this);//让MainActivity实现OnItemClickListener这个接口</span>
同时还要重写这个接口的点击事件
<span style="font-size:18px;"> @Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
//动态插入一个Fragment到FrameLayout中
ContentFragment contentFragment = new ContentFragment();
Bundle args = new Bundle();
args.putString("text", menuList.get(position));
contentFragment.setArguments(args);
FragmentManager fm = getFragmentManager();
//开启一个事务
fm.beginTransaction().replace(R.id.content_frame, contentFragment).commit();
mDrawerLayout.closeDrawer(mDrawerList);
}</span>
这里面的代码我一句句来讲解
第一句,ContentFragment ContentFragment = new ContentFragment();
这是我们自己创建的类,用于实现点击以后呈现的页面,在这里面我们需要在定义一个布局文件fragment_content.xml
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="25sp" />
</LinearLayout></span>
这个布局文件很简单就只有一个TextView,接着就需要在ContentFragment这个类中调用这个布局
<span style="font-size:18px;"> private TextView tv;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment_content, container, false);
tv = (TextView) view.findViewById(R.id.textView);
String txt = getArguments().getString("text");
tv.setText(txt);
return view;
}</span>
接着我们来讲讲Bundle args = new Bundle();
可能大家对于这个Bundle有点陌生,不知道这个是用来干嘛的(此处解释)
在这里我们的Bundle是用来传递参数的,传递点击ListView后的文字信息,接下来的两句就是不参数传递过去。
<span style="font-size:18px;">FragmentManager fm = getFragmentManager();
//开启一个事务
fm.beginTransaction().replace(R.id.content_frame, contentFragment).commit();</span>
Fragment是有FragmentManager管理的,这里我们就需要用到FragmentManager来管理我们点击后的Fragment,这里就实现里我们点击ListView上的某一条后的效果,本质的效果就是点击一条,就会创建一个Fragment,并且把之前创建的Fragment替代掉。
最后我们来监听Drawer的拉开和关闭
<span style="font-size:18px;">mDrawToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close){
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
getActionBar().setTitle("请选择");
invalidateOptionsMenu();
}
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
getActionBar().setTitle(mTitle);
invalidateOptionsMenu();//Call onPrepareOptionsMenu
}
};
mDrawerLayout.setDrawerListener(mDrawToggle);
//开启ActionBar上APP ICOn的功能
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);</span>
ActionBarDrawerToggle是一个开关,用于打开/关闭DrawerLayout抽屉
ActionBarDrawerToggle 提供了一个方便的方式来配合DrawerLayout和ActionBar,以实现推荐的抽屉功能。
即点击ActionBar的home按钮,即可弹出DrawerLayout抽屉。
在Activity中的两个回调函数中使用它:
onConfigurationChanged
onOptionsItemSelected
调用ActionBarDrawerToggle.syncState() 在Activity的onPostCreate()中;指示,ActionBarDrawerToggle与DrawerLayout的状态同步,并将ActionBarDrawerToggle中的drawer图标,设置为ActionBar的Home-Button的icon
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean isDrawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_search).setVisible(!isDrawerOpen);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//将ActionBar上的图标和Drawer结合起来
if (mDrawToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case R.id.action_search:
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
Uri uri = Uri.parse("http://www.baidu.com");
intent.setData(uri);
startActivity(intent);
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onPostCreate(savedInstanceState);
//需要将ActionDrawerToggle与DrawerLayout的状态同步
//需要将ActionBarDrawerToggle中的Drawer图标设置为ActionBar中的Home-Button的Icon
mDrawToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
mDrawToggle.onConfigurationChanged(newConfig);
}
上面的代码已经给了注释,这里就不多解释了