侧滑菜单的实现方式:
- SlidingMenu开源库:https://github.com/jfeinstein10/SlidingMenu
- DrawerLayout:是2013年谷歌IO大会上由谷歌官方发布的,包含在support v4包中。
官方定义: http://developer.android.com/reference/android/support/v4/widget/DrawerLayout.html
使用说明: http://developer.android.com/training/implementing-navigation/nav-drawer.html
接下来按照谷歌官方给定的创建步骤来实现一个侧滑菜单。
Create a Drawer Layout
To add a navigation drawer, declare your user interface with a
DrawerLayout
object as the root view of your layout.
Inside the
DrawerLayout
, add one view that contains the main content for the screen (your primary layout when the drawer is hidden) and another view that contains the contents of the navigation drawer.
按照谷歌官方给出的示例创建一个带有Drawer Layout的项目:
activity_main.xml配置如下:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/draw_layout"
android:layout_height="match_parent"
android:layout_width="match_parent">
<!--The main content view-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/content_frame">
</FrameLayout>
<!--The navigation view-->
<ListView
android:layout_width="240dp"
android:layout_height="match_parent"
android:id="@+id/left_drawer"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:background="#ffffcc"
android:divider="@android:color/transparent"
android:dividerHeight="0dp">
</ListView>
</android.support.v4.widget.DrawerLayout>
启动项目,通过从屏幕左端滑动来滑出菜单。
注:
android
:layout_gravity=
"start"是从屏幕左边滑动,"end"则为从屏幕右端滑出。
android
:choiceMode=
"singleChoice"模式为单选模式
android
:divider=
"@android:color/transparent"分割线采用原始的透明分割线
android
:dividerHeight=
"0dp"分割线高度为0,不可见。
注意事项:
- 主内容视图一定要是DrawerLayout的第一个子视图
- 主内容视图宽度高度要匹配父视图,即"match_parent",也就是说:当抽屉隐藏时,要让用户看到主视图的全部内容。
- 必须显示指定抽屉视图(如LiistView)的android:layout_gravity属性。
- android:layout_gravity="start"从左向右滑出菜单
- android:layout_gravity="end"从右向左滑出菜单
- 不推荐使用"left" 和"right"
4.抽屉视图的宽度以dp为单位,请不要超过320dp(为了总能看到一些主内容视图)
Initialize the Drawer List
In your activity, one of the first things to do is initialize the navigation drawer's list of items. How you do so depends on the content of your app, but a navigation drawer often consists of a
ListView
, so the list should be populated by an
Adapter
(such as
ArrayAdapter
or
SimpleCursorAdapter
).
接下来在项目中实现初始化Drawer List的操作。
1.首先对组件进行声明,包括DrawerLayout和ListView。
private DrawerLayout
mDrawerLayout
;
private ListView
mDrawerList
;
2.使用ArrayList集合来储存菜单项,使用ArrayAdapter对ListView的内容进行填充。
//
使用集合来储存侧滑菜单的菜单项
private ArrayList<String>
menuLists
;
//
使用
ArrayAdapter
来对
ListView
的内容进行填充
private ArrayAdapter<String>
adapter
;
3.在onCreate方法中对z组件进行查找和初始化数据操作。
mDrawerLayout= (DrawerLayout) findViewById(R.id.
draw_layout)
;
mDrawerList= (ListView) findViewById(R.id.
left_drawer)
;
//
初始化
menulists
menuLists=
new ArrayList<String>()
;
for (
int i=
0
;i<
5
;i++){
menuLists.add(
"
测试菜单
"+i)
;
}
//
初始化
adapter
adapter=
new ArrayAdapter<String>(
this,android.R.layout.
simple_list_item_1
,
menuLists)
;
//
为侧边菜单填充上内容
mDrawerList.setAdapter(
adapter)
;
Handle Navigation Click Events
When the user selects an item in the drawer's list, the system calls
onItemClick()
on the
OnItemClickListener
given to
setOnItemClickListener()
.
What you do in the
onItemClick()
method depends on how you've implemented your app structure.
1.为菜单项添加点击事件监听器,并让主类实现事件监听器接口
mDrawerList.setOnItemClickListener(
this)
;
2.复写事件监听器中的方法,动态插入一个Fragment到FrameLayout中去
public void
onItemClick(AdapterView<?> parent
, View view
, int position
, long id) {
//
动态插入一个
Fragment
到
FrameLayout
当中
Fragment contentFragment=
new ContentFragment()
;
Bundle args=
new Bundle()
;
args.putString(
"text"
,
menuLists.get(position))
;
contentFragment.setArguments(args)
;
//?
//
新建一个
Fragment
FragmentManager fm=getFragmentManager()
;
fm.beginTransaction().replace(R.id.
content_frame
,contentFragment).commit()
;
//
点击完成后关闭菜单栏
mDrawerLayout.closeDrawer(
mDrawerList)
;
}
3.新建一Fragment ContentFragment,对应配置文件为fragment_content.xml
fragment_content.xml配置如下:
<FrameLayout
xmlns:
android
=
"http://schemas.android.com/apk/res/android"
xmlns:
tools
=
"http://schemas.android.com/tools"
android
:layout_width=
"match_parent"
android
:layout_height=
"match_parent"
android
:id=
"@+id/content_frame"
tools
:context=
"com.example.icarus.drawerlayoutusing.ContentFragment"
>
<!--
TODO: Update blank fragment layout
-->
<TextView
android
:layout_width=
"match_parent"
android
:layout_height=
"wrap_content"
android
:id=
"@+id/textView"
android
:textSize=
"25sp"
/>
</FrameLayout>
ContentFragment.java具体代码:
public class ContentFragment
extends Fragment {
private TextView
textView
;
@Override
public View
onCreateView(LayoutInflater inflater
, ViewGroup container
,
Bundle savedInstanceState) {
//
动态加载视图
View view=inflater.inflate(R.layout.
fragment_content
,container
,false)
;
//
查找组件
id
textView= (TextView) view.findViewById(R.id.
textView)
;
//
通过
Arguments
来传递数据
String text=getArguments().getString(
"text")
;
//
给
textview
设置文字
textView.setText(text)
;
//
返回加载的视图
return view
;
}
}
问题:
1. 为什么要用Fragment.setArguments(Bundle bundle)来传递参数 ?
Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递参数
具体: http://blog.csdn.net/tu_bingbing/article/details/24143249
Listen for Open and Close Events
To listen for drawer open and close events, call setDrawerListener()
on your DrawerLayout
and pass it an implementation of DrawerLayout.DrawerListener
. This interface provides callbacks for drawer events such asonDrawerOpened()
and onDrawerClosed()
.
However, rather than implementing the DrawerLayout.DrawerListener
, if your activity includes the action bar, you can instead extend the ActionBarDrawerToggle
class. The ActionBarDrawerToggle
implementsDrawerLayout.DrawerListener
so you can still override those callbacks, but it also facilitates the proper interaction behavior between the action bar icon and the navigation drawer (discussed further in the next section).
As discussed in the Navigation Drawer design guide, you should modify the contents of the action bar when the drawer is visible, such as to change the title and remove action items that are contextual to the main content.
知识点介绍:
1.mDrawerLayout.setDrawerListener(DrawerLayout.DrawerListener);
2.ActionBarDrawerToggle是
DrawerLayout.DrawerListener的具体实现类。
1)改变android.R.id.home图标(构造方法)
2)Drawer拉出、隐藏,带有android.R.id.home动画效果(syncState())
3)监听Drawer拉出、隐藏事件
3.复写ActionBarDrawerToggle的onDrawerOpended()和onDrawerClosed()以监听抽屉拉出或隐藏事件。
4.复写Activity中的onPostCreate()方法和onConfigurationChange()方法,是为了结合syncState()方法来s实现左上角图标动画的效果。当设备的y一些参数发生变化时(屏幕旋转等)就会调用onConfigurationChange方法,在方法中对ActionBarDrawerToggle进行配置。
在代码中实现:
(一)设置菜单被拉出时Title文字的改变
1.首先声明一个ActionBarDrawerToggle,注意是support.v7包中的(support.v4包中的已经失效)
使用v7替换v4方法请看:
ActionBarDrawerToggle is Deprecated
private ActionBarDrawerToggle
mDrawerToggle
;
2.接着初始化ActionBarDrawerToggle,(具体参数为:
ActionBarDrawerToggle
( Activity
activity,
DrawerLayout
drawerLayout, int openDrawerContentD
escRes, int closeDrawerContentDescRes)
)并复写其中的onDrawerOpended()和onDrawerClosed()方法来实现拉出或隐藏抽屉时对Title的更改。
注:使用v7包的时候得使用getSupportActionBar()来设置Title信息
mDrawerToggle=
new ActionBarDrawerToggle(
this,
mDrawerLayout
,
R.string.
drawer_open
,R.string.
drawer_close){
/**
*
当抽屉被打开是执行
*
设置抽屉被打开时的
Title
*
通过
getActionBar()
来改变
*
@param
drawerView
*/
@Override
public void
onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView)
;
//BUG:
此处产生空指针异常
,
使用
getSupportActionBar()
//getActionBar().setTitle("
请选择
");
getSupportActionBar().setTitle(
"
请选择
")
;
invalidateOptionsMenu()
;
//
重绘
actionbar
上的菜单项,会自动调用
onPrepareOptionsMenu
方法
}
/**
*
当抽屉被关闭时执行
*
设置抽屉被关闭时的
Title
*
@param
drawerView
*/
@Override
public void
onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView)
;
getSupportActionBar().setTitle(
mTitle)
;
invalidateOptionsMenu()
;
//
重绘
actionbar
上的菜单项
}
}
;
3.给DrawerLayout添加DrawerListener。setDrawerListener以失效,使用addDrawerListener
mDrawerLayout.addDrawerListener(mDrawerToggle);
(二)在ActionBar上添加搜索图标
1.在res中新建menu文件夹,在其中添加main.xml,其中
android:showAsAction。
这个属性可接受的值有:
1、always:这个值会使菜单项一直显示在Action Bar上。
2、ifRoom:如果有足够的空间,这个值会使菜单项显示在Action Bar上。
3、never:这个值使菜单项永远都不出现在Action Bar上。
4、withText:这个值使菜单项和它的图标,菜单文本一起显示。
main.xml配置如下:
<?
xml version=
"1.0"
encoding=
"utf-8"
?>
<menu
xmlns:
android
=
"http://schemas.android.com/apk/res/android"
xmlns:
app
=
"http://schemas.android.com/apk/res-auto"
>
<item
android
:id=
"@+id/action_websearch"
android
:icon=
"@drawable/action_search"
android
:title=
"webSearch"
app
:showAsAction=
"ifRoom|withText"
/>
</menu>
2.在MainActivity中使用onCreateOptionsMenu()来加载menu
public boolean
onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.
main
,menu)
;
return true;
}
3.在执行invalidateOptionsMenu()方法时会调用onPrepareOptionsMenu(),复写此方法来实现菜单图标的显示状态。首先先获取Drawer的打开状态,当Drawer打开时菜单图标被隐藏,当Drawer关闭时显示菜单图标。即菜单图标的显示状态与Drawer的打开状态正好相反。
public boolean
onPrepareOptionsMenu(Menu menu) {
//
获取
Drawer
的打开状态
boolean isDrawerOpen=
mDrawerLayout.isDrawerOpen(
mDrawerList)
;
//
对
menuItem
的可见状态进行改变
,
总是跟
Drawer
的打开状态相反
menu.findItem(R.id.
action_websearch).setVisible(!isDrawerOpen)
;
return super.onPrepareOptionsMenu(menu)
;
}
4.使用onOptionsItemSelected()方法来为菜单项设置点击事件。让其使用系统浏览器打开指定的网页(百度)。
public boolean
onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.
action_websearch:
Intent intent=
new Intent()
;
intent.setAction(
"android.intent.action.VIEW")
;
Uri uri=Uri.
parse(
"http://www.baidu.com")
;
intent.setData(uri)
;
startActivity(intent)
;
break;
}
return super.onOptionsItemSelected(item)
;
}
Open and Close with the App Icon
Users can open and close the navigation drawer with a swipe gesture from or towards the left edge of the screen, but if you're using the action bar, you should also allow users to open and close it by touching the app icon. And the app icon should also indicate the presence of the navigation drawer with a special icon. You can implement all this behavior by using the ActionBarDrawerToggle
shown in the previous section.
To make ActionBarDrawerToggle
work, create an instance of it with its constructor, which requires the following arguments:
Then, whether or not you've created a subclass of
ActionBarDrawerToggle
as your drawer listener, you need to call upon your
ActionBarDrawerToggle
in a few places throughout your activity lifecycle:
我们希望使用app的图标来打开和关闭侧边栏,根据谷歌开发者文档
1.首先打开ActionBar Icon的功能
getSupportActionBar().setDisplayHomeAsUpEnabled(
true)
;
2.使左上角的Home-Button可用,因为左上角的Home-Button也属于menu的一种所以点击时会回调onOptionItemSelected方法。
getSupportActionBar().setHomeButtonEnabled(
true)
;
3.在
onOptionItemSelected方法中将ActionBar上的图标与Drawer结合起来。
if (
mDrawerToggle.onOptionsItemSelected(item)){
return true;
}
4.需要将ActionDrawerToggler与DrawerLayout的状态同步 ,
将ActionBarDrawerTaggler中的drawer图标,设置为ActionBar中的Home-Button的icon
public void
onPostCreate(Bundle savedInstanceState
, PersistableBundle persistentState) {
super.onPostCreate(savedInstanceState
, persistentState)
;
mDrawerToggle.syncState()
;
}
5.谷歌开发者文档建议使用onConfigurationChanged方法 ,
当屏幕旋转等发生Configuration变化的时候执行
public void
onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig)
;
mDrawerToggle.onConfigurationChanged(newConfig)
;
}
到此,我们使用谷歌开发者文档提供的DrawerLayout成功完成了侧滑菜单的开发!
项目源码下载:
https://github.com/icaruswang/DrawerLayoutUsing