android官方侧滑菜单DrawerLayout详解

泡在网上的日子 / 文 发表于2014-09-25 14:05 第次阅读 drawerLayout

DrawerLayoutSupport Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产物。drawerLayout分为侧边菜单和主内容区两部分,侧边菜单可以根据手势展开与隐藏(drawerLayout自身特性),主内容区的内容可以随着菜单的点击而变化(这需要使用者自己实现)。

drawerLayout的使用很方便,使用drawerLayout的要点如下:

1.drawerLayout其实是一个布局控件,跟LinearLayout等控件是一种东西,但是drawerLayout带有滑动的功能。只要按照drawerLayout的规定布局方式写完布局,就能有侧滑的效果。


????
????
????
????

其中:DrawerLayout最好为界面的根布局,官网是这样说的,否则可能会出现触摸事件被屏蔽的问题;主内容区的布局代码要放在侧滑菜单布局的前面,?因为 XML 顺序意味着按 z 序(层叠顺序)排序,并且抽屉式导航栏必须位于内容顶部;侧滑菜单部分的布局(这里是ListView)必须设置layout_gravity属性,他表示侧滑菜单是在左边还是右边,而且如果不设置在打开关闭抽屉的时候会报错,设置了layout_gravity="start/left"的视图才会被认为是侧滑菜单。

2.drawerLayout左侧菜单(或者右侧)的展开与隐藏可以被DrawerLayout.DrawerListener的实现监听到,这样你就可以在菜单展开与隐藏反生的时刻做一些希望做的事情,比如更新actionbar菜单等。如果你的activityactionbar的话,还是建议你用ActionBarDrawerToggle来监听,ActionBarDrawerToggle实现了DrawerListener,所以他能做DrawerListener可以做的任何事情,同时他还能将drawerLayout的展开和隐藏与actionbarapp 图标关联起来,当展开与隐藏的时候图标有一定的平移效果,点击图标的时候还能展开或者隐藏菜单。

mDrawerToggle?=?new?ActionBarDrawerToggle(
????????this,??????????????????/*?host?Activity?*/
????????mDrawerLayout,?????????/*?DrawerLayout?object?*/
????????R.drawable.ic_drawer,??/*?nav?drawer?image?to?replace?'Up'?caret?*/
????????R.string.drawer_open,??/*?"open?drawer"?description?for?accessibility?*/
????????R.string.drawer_close??/*?"close?drawer"?description?for?accessibility?*/
????????)?{
????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()
????}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);


3.何为侧边菜单。

侧边菜单其实只是一个普通的View,一般里面装的是ListView,看起来就像菜单,他完全可以是一个buttontextView等等。虽然称为菜单,但跟Activity的菜单形式是两码事,Activity的菜单只需要在资源文件中定义好,就能按照固定的形式显示出来。而drawerLayout的侧边菜单显示成什么样完全是取决于你自己,同样点击事件也完全由你自己去写。如下代码所示我们的侧边菜单是一个ListView显示的:

mDrawerList.setAdapter(new?ArrayAdapter(this,
????????????R.layout.drawer_list_item,?mPlanetTitles));
mDrawerList.setOnItemClickListener(new?DrawerItemClickListener());
/*?The?click?listner?for?ListView?in?the?navigation?drawer?*/
private?class?DrawerItemClickListener?implements?ListView.OnItemClickListener?{
????@Override
????public?void?onItemClick(AdapterView?parent,?View?view,?int?position,?long?id)?{
????????selectItem(position);
????}
}
private?void?selectItem(int?position)?{
????//?update?the?main?content?by?replacing?fragments
????Fragment?fragment?=?new?PlanetFragment();
????Bundle?args?=?new?Bundle();
????args.putInt(PlanetFragment.ARG_PLANET_NUMBER,?position);
????fragment.setArguments(args);
????FragmentManager?fragmentManager?=?getFragmentManager();
????fragmentManager.beginTransaction().replace(R.id.content_frame,?fragment).commit();
????//?update?selected?item?and?title,?then?close?the?drawer
????mDrawerList.setItemChecked(position,?true);
????setTitle(mPlanetTitles[position]);
????mDrawerLayout.closeDrawer(mDrawerList);
}


4.在代码中主动展开与隐藏侧边菜单。

在点击侧边菜单选项的时候我们往往需要隐藏菜单来显示整个菜单对应的内容。DrawerLayout.closeDrawer方法用于隐藏侧边菜单,DrawerLayout.openDrawer方法用于展开侧边菜单(参见第3点中的代码部分)

5.如何在菜单展开或者隐藏的时候更新activitymenu

上面的的第2点讲到DrawerLayout.DrawerListener监听展开与隐藏事件,在监听的回调方法中我们用invalidateOptionsMenu通知activity重绘menu,然后activity就有机会在onPrepareOptionsMenu方法中更新menu元素的显示与隐藏

代码:

/*?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);
}


6.如何让app图标点击的时候能够展开或者隐藏侧边菜单。

一般的想法是在activityonOptionsItemSelected方法中判断点击事件是否来自于app图标,然后用DrawerLayout.closeDrawerDrawerLayout.openDrawer来隐藏与展开(参见第4点:在代码中主动展开与隐藏侧边菜单)。但是drawerLayout提供了更优雅的方式:使用ActionBarDrawerToggleonOptionsItemSelected方法。该方法activityonOptionsItemSelected方法中根据传递进来的menu item做了上面我们在“一般想法中提到的事情。用官方的说法是”ActionBarDrawerTogglewill take care of this”。我们只需这样做就ok了:

Activity中:

@Override
public?booleanonOptionsItemSelected(MenuItem?item)?{
//?The?action?bar?home/up?actionshould?open?or?close?the?drawer.
//?ActionBarDrawerToggle?will?takecare?of?this.
if(mDrawerToggle.onOptionsItemSelected(item))?{
return?true;
}
……….//处理其他菜单点击事件
returnsuper.onOptionsItemSelected(item);
}

如果不仔细阅读官方文档,估计我们很难看出(mDrawerToggle.onOptionsItemSelected(item)在这里的作用。这也是我刚开始最疑惑的地方。

7. drawerLayoutFragment是什么关系?

我们看到很多使用drawerLayout的代码中都同时使用了Fragment,这会造成误解,以为使用drawerLayout必须用到Fragment,其实这是错误的,使用Fragment是因为在侧滑菜单被点击的时候,主内容区如果内容比较复杂,用Fragment去填充会更容易,如果你的主内容区只是一个简单的字符串,只想在不同菜单点击的时候更新一下字符串的内容,我觉得没必要用Fragment。不过官方的例子其实中,Fragment所做的就是更新字符串内容这么简单。


最后我们来看看一个完整的drawerLayout的例子,来源于官方网站的demo,代码中反映了上述我们提到的所有要点:

Activity


/*
?*?Copyright?2013?The?Android?Open?Source?Project
?*
?*?Licensed?under?the?Apache?License,?Version?2.0?(the?"License");
?*?you?may?not?use?this?file?except?in?compliance?with?the?License.
?*?You?may?obtain?a?copy?of?the?License?at
?*
?*?????http://www.apache.org/licenses/LICENSE-2.0
?*
?*?Unless?required?by?applicable?law?or?agreed?to?in?writing,?software
?*?distributed?under?the?License?is?distributed?on?an?"AS?IS"?BASIS,
?*?WITHOUT?WARRANTIES?OR?CONDITIONS?OF?ANY?KIND,?either?express?or?implied.
?*?See?the?License?for?the?specific?language?governing?permissions?and
?*?limitations?under?the?License.
?*/
package?com.example.android.navigationdrawerexample;
import?java.util.Locale;
import?android.app.Activity;
import?android.app.Fragment;
import?android.app.FragmentManager;
import?android.app.SearchManager;
import?android.content.Intent;
import?android.content.res.Configuration;
import?android.os.Bundle;
import?android.support.v4.app.ActionBarDrawerToggle;
import?android.support.v4.view.GravityCompat;
import?android.support.v4.widget.DrawerLayout;
import?android.view.LayoutInflater;
import?android.view.Menu;
import?android.view.MenuInflater;
import?android.view.MenuItem;
import?android.view.View;
import?android.view.ViewGroup;
import?android.widget.AdapterView;
import?android.widget.ArrayAdapter;
import?android.widget.ImageView;
import?android.widget.ListView;
import?android.widget.Toast;
/**
?*?This?example?illustrates?a?common?usage?of?the?DrawerLayout?widget
?*?in?the?Android?support?library.
?*?

?*?

When?a?navigation?(left)?drawer?is?present,?the?host?activity?should?detect?presses?of ?*?the?action?bar's?Up?affordance?as?a?signal?to?open?and?close?the?navigation?drawer.?The ?*?ActionBarDrawerToggle?facilitates?this?behavior. ?*?Items?within?the?drawer?should?fall?into?one?of?two?categories:

?*?

?*?
    ?*?
  • View?switches.?A?view?switch?follows?the?same?basic?policies?as ?*?list?or?tab?navigation?in?that?a?view?switch?does?not?create?navigation?history. ?*?This?pattern?should?only?be?used?at?the?root?activity?of?a?task,?leaving?some?form ?*?of?Up?navigation?active?for?activities?further?down?the?navigation?hierarchy.
  • ?*?
  • Selective?Up.?The?drawer?allows?the?user?to?choose?an?alternate ?*?parent?for?Up?navigation.?This?allows?a?user?to?jump?across?an?app's?navigation ?*?hierarchy?at?will.?The?application?should?treat?this?as?it?treats?Up?navigation?from ?*?a?different?task,?replacing?the?current?task?stack?using?TaskStackBuilder?or?similar. ?*?This?is?the?only?form?of?navigation?drawer?that?should?be?used?outside?of?the?root ?*?activity?of?a?task.
  • ?*?
?*?

?*?

Right?side?drawers?should?be?used?for?actions,?not?navigation.?This?follows?the?pattern ?*?established?by?the?Action?Bar?that?navigation?should?be?to?the?left?and?actions?to?the?right. ?*?An?action?should?be?an?operation?performed?on?the?current?contents?of?the?window, ?*?for?example?enabling?or?disabling?a?data?overlay?on?top?of?the?current?content.

?*/ public?class?MainActivity?extends?Activity?{ ????private?DrawerLayout?mDrawerLayout; ????private?ListView?mDrawerList; ????private?ActionBarDrawerToggle?mDrawerToggle; ????private?CharSequence?mDrawerTitle; ????private?CharSequence?mTitle; ????private?String[]?mPlanetTitles; ????@Override ????protected?void?onCreate(Bundle?savedInstanceState)?{ ????????super.onCreate(savedInstanceState); ????????setContentView(R.layout.activity_main); ????????mTitle?=?mDrawerTitle?=?getTitle(); ????????mPlanetTitles?=?getResources().getStringArray(R.array.planets_array); ????????mDrawerLayout?=?(DrawerLayout)?findViewById(R.id.drawer_layout); ????????mDrawerList?=?(ListView)?findViewById(R.id.left_drawer); ????????//?set?a?custom?shadow?that?overlays?the?main?content?when?the?drawer?opens ????????mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,?GravityCompat.START); ????????//?set?up?the?drawer's?list?view?with?items?and?click?listener ????????mDrawerList.setAdapter(new?ArrayAdapter(this, ????????????????R.layout.drawer_list_item,?mPlanetTitles)); ????????mDrawerList.setOnItemClickListener(new?DrawerItemClickListener()); ????????//?enable?ActionBar?app?icon?to?behave?as?action?to?toggle?nav?drawer ????????getActionBar().setDisplayHomeAsUpEnabled(true); ????????getActionBar().setHomeButtonEnabled(true); ????????//?ActionBarDrawerToggle?ties?together?the?the?proper?interactions ????????//?between?the?sliding?drawer?and?the?action?bar?app?icon ????????mDrawerToggle?=?new?ActionBarDrawerToggle( ????????????????this,??????????????????/*?host?Activity?*/ ????????????????mDrawerLayout,?????????/*?DrawerLayout?object?*/ ????????????????R.drawable.ic_drawer,??/*?nav?drawer?image?to?replace?'Up'?caret?*/ ????????????????R.string.drawer_open,??/*?"open?drawer"?description?for?accessibility?*/ ????????????????R.string.drawer_close??/*?"close?drawer"?description?for?accessibility?*/ ????????????????)?{ ????????????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() ????????????} ????????}; ????????mDrawerLayout.setDrawerListener(mDrawerToggle); ????????if?(savedInstanceState?==?null)?{ ????????????selectItem(0); ????????} ????} ????@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)?{ ????????//?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); ????} ????@Override ????public?boolean?onOptionsItemSelected(MenuItem?item)?{ ?????????//?The?action?bar?home/up?action?should?open?or?close?the?drawer. ?????????//?ActionBarDrawerToggle?will?take?care?of?this. ????????if?(mDrawerToggle.onOptionsItemSelected(item))?{ ????????????return?true; ????????} ????????//?Handle?action?buttons ????????switch(item.getItemId())?{ ????????case?R.id.action_websearch: ????????????//?create?intent?to?perform?web?search?for?this?planet ????????????Intent?intent?=?new?Intent(Intent.ACTION_WEB_SEARCH); ????????????intent.putExtra(SearchManager.QUERY,?getActionBar().getTitle()); ????????????//?catch?event?that?there's?no?activity?to?handle?intent ????????????if?(intent.resolveActivity(getPackageManager())?!=?null)?{ ????????????????startActivity(intent); ????????????}?else?{ ????????????????Toast.makeText(this,?R.string.app_not_available,?Toast.LENGTH_LONG).show(); ????????????} ????????????return?true; ????????default: ????????????return?super.onOptionsItemSelected(item); ????????} ????} ????/*?The?click?listner?for?ListView?in?the?navigation?drawer?*/ ????private?class?DrawerItemClickListener?implements?ListView.OnItemClickListener?{ ????????@Override ????????public?void?onItemClick(AdapterView?parent,?View?view,?int?position,?long?id)?{ ????????????selectItem(position); ????????} ????} ????private?void?selectItem(int?position)?{ ????????//?update?the?main?content?by?replacing?fragments ????????Fragment?fragment?=?new?PlanetFragment(); ????????Bundle?args?=?new?Bundle(); ????????args.putInt(PlanetFragment.ARG_PLANET_NUMBER,?position); ????????fragment.setArguments(args); ????????FragmentManager?fragmentManager?=?getFragmentManager(); ????????fragmentManager.beginTransaction().replace(R.id.content_frame,?fragment).commit(); ????????//?update?selected?item?and?title,?then?close?the?drawer ????????mDrawerList.setItemChecked(position,?true); ????????setTitle(mPlanetTitles[position]); ????????mDrawerLayout.closeDrawer(mDrawerList); ????} ????@Override ????public?void?setTitle(CharSequence?title)?{ ????????mTitle?=?title; ????????getActionBar().setTitle(mTitle); ????} ????/** ?????*?When?using?the?ActionBarDrawerToggle,?you?must?call?it?during ?????*?onPostCreate()?and?onConfigurationChanged()... ?????*/ ????@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); ????????//?Pass?any?configuration?change?to?the?drawer?toggls ????????mDrawerToggle.onConfigurationChanged(newConfig); ????} ????/** ?????*?Fragment?that?appears?in?the?"content_frame",?shows?a?planet ?????*/ ????public?static?class?PlanetFragment?extends?Fragment?{ ????????public?static?final?String?ARG_PLANET_NUMBER?=?"planet_number"; ????????public?PlanetFragment()?{ ????????????//?Empty?constructor?required?for?fragment?subclasses ????????} ????????@Override ????????public?View?onCreateView(LayoutInflater?inflater,?ViewGroup?container, ????????????????Bundle?savedInstanceState)?{ ????????????View?rootView?=?inflater.inflate(R.layout.fragment_planet,?container,?false); ????????????int?i?=?getArguments().getInt(ARG_PLANET_NUMBER); ????????????String?planet?=?getResources().getStringArray(R.array.planets_array)[i]; ????????????int?imageId?=?getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()), ????????????????????????????"drawable",?getActivity().getPackageName()); ????????????((ImageView)?rootView.findViewById(R.id.image)).setImageResource(imageId); ????????????getActivity().setTitle(planet); ????????????return?rootView; ????????} ????} }


Xml

activity_main.xml



????
????
????
????

fragment_planet.xml

drawer_list_item.xml

DrawerLayout官网介绍?。

上一篇:java代码中实现android背景选择的selector-StateListDrawable的应用
下面的代码应该很多人都熟悉: ?xml version="1.0" encoding="utf-8"?selector xmlns:android="http://schemas.android.com/apk/res/android" item android:drawable="@drawable/numpad_button_bg_selected" android:state_selected="true"/item item androi
下一篇:Android动态改变布局
下面这篇文章是我在csdn上看到的,有所启发,主要是下面提到的当软件盘弹出时将原本上下排列的布局改成左右排列这种交互设计比较好,另外在java代码中添加RelativeLayout的布局规则也值得学习,原文如下: 遇到这么个需求,先看图: --- 其实是一个软件的登