源码地址:https://github.com/licong/android-menudrawer

MenuDrawer ——简单的使用方法


今天,我们需要达到的效果是,使用MenuDrawer创建一个侧滑菜单视图,当我们点击菜单项(使用ListView)时,右面的内容视图(就是一个TextView)会响应这个点击事件

默认是菜单项隐藏在左边,当从左边缘滑动时,菜单视图出现,回滑时,菜单隐藏

先看看效果图吧:

android fragment展开列表 android展开菜单_ide

 

【用到的 MenuDrawer API】


1、将MenuDrawer绑定到当前的Activity中,并返回MenuDrawer对象

这几个方法是静态方法,是创建MenuDrawer对象的方法,注意:MenuDrawer没有构造方法

MenuDrawer.attach(Activity activity )

MenuDrawer.attach(Activity activity , int dragMode)   参数dragMode指定MenuDrawer的菜单视图的加载模式

MenuDrawer.attach(Activity activity, Position position) 参数position指定菜单视图加载在屏幕的哪一边:上下左右,本实例是加载在左边,默认也是在左边

MenuDrawer.attch(Activity activity, int dragMode, Position position)

MenuDrawer.attch(Activity activity, int dragMode, Position position, boolean attachStatic) 最后一个参数明显是:是否静态的显示一个Menu视图,即菜单不能够滑动

 

dragMode指定MenuDrawer的菜单视图的加载模式,总共有两种加载方式:

普通内容视图模式:MenuDrawer.MENU_DRAG_CONTENT,效果如下:

android fragment展开列表 android展开菜单_菜单项_02

 

 填充窗口模式:MenuDrawer.MENU_DRAG_WINDOW,效果如下:

android fragment展开列表 android展开菜单_菜单项_03

 

2、向MenuDrawer中添加内容视图

setContentView(int layoutResId)  通过布局文件的ID加载

setContentView(View view)         直接通过View加载

setContentView(View view, LayoutParams params)

 

3、向MenuDrawer中添加菜单视图 (一般是加载ListView)

setMenuView(int layoutResId)

setMenuView(View view)

setMenuView(View view, LayoutParams params)

 

4、向当前的选中项中添加 "指针" 用来标定当前的活动项

setActiveView(View view, int position) 

参数 View : 当前的菜单项对应的View

参数position: 当前View在adapter中的位置

注意:想要在程序中使用“指针”的功能的话,应该有三步:

①在Manifest.xml文件中为当前的Activity配置主题含有“指针”图片的Theme主题

②在Adapter的getView()方法中调用setTag(R.id.mdActiveViewPosition, position)方法

③调用上面的setActiveView()方法

一会儿通过下面的例子,就会秒懂了!一切尽在掌握之中

 

 5、关闭选项菜单/打开选项菜单

closeMenu();

closeMenu(boolean animate); 是否使用动画,但是和上面的方法在效果上基本没有什么分别

toggleMenu(); 这个是一个开关选项,当菜单视图处于打开模式时,调用这个方法,那么菜单视图就会关闭,反之,菜单就会打开

 

6、返回当前的菜单视图的状态

int  getDrawerState()

比如正在展开、正在关闭、已经展开、已经关闭等等

 

7、设置菜单视图的大小

setMenuSize(int size)

我们亦可以在style文件中更改这个属性,一会儿你就秒懂了

8、重新绘制当前的MenuDrawer对象

invalidate()

9、自动开启菜单视图

peekDrawer();

这个方法并不是将菜单视图整个显示出来,而是讲菜单视图轻轻的抻出一小条,这样为的是能够让用户发现在左边隐藏着一个菜单视图,这个方法一般放在Activity的onCreate()方法中,并且一般只使用一次

 

【实例代码】


主布局文件 MenuTuiCoolActivity.java 文件

 

1 package com.penglee.tuicool;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import android.app.Activity;
  6 import android.os.Build;
  7 import android.os.Bundle;
  8 import android.view.MenuItem;
  9 import android.view.View;
 10 import android.view.ViewGroup;
 11 import android.widget.AbsListView;
 12 import android.widget.AdapterView;
 13 import android.widget.BaseAdapter;
 14 import android.widget.ListView;
 15 import android.widget.TextView;
 16 
 17 
 18 import net.simonvt.menudrawer.*;
 19 
 20 public class MenuTuicoolActivity extends Activity {
 21     
 22      //定义菜单适配器
 23      private MenuAdapter menuAdapter  ;
 24      
 25      //定义ListView菜单
 26      ListView menuList ;
 27      
 28      //保存当前的活动菜单项
 29      int currentActiveItem = -1 ;
 30      
 31      //定义内容视图
 32      TextView contentText ;
 33      
 34      //定义MenuDrawer对象
 35      private MenuDrawer  menuDrawer ;
 36 
 37     @Override
 38     protected void onCreate(Bundle savedInstanceState) {
 39         super.onCreate(savedInstanceState);
 40         
 41         //创建MenuDrawer,并设定加载模式
 42         menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_CONTENT);
 43         
 44         //创建菜单视图
 45         menuList= new ListView(this) ;
 46         menuAdapter = new MenuTuicoolActivity.MenuAdapter() ;
 47         menuList.setAdapter(menuAdapter);
 48         
 49         //为菜单视图添加事件响应
 50         menuList.setOnItemClickListener(mItemClickListener);
 51         menuList.setOnScrollListener(new AbsListView.OnScrollListener() {
 52             @Override
 53             public void onScrollStateChanged(AbsListView view, int scrollState) {
 54             }
 55 
 56             @Override
 57             public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 58                /*为什么要进行重绘呢?因为我们为菜单项设置了一个指示及“指针”
 59                 * 如果我们不重绘的话,那么当滚动菜单栏的时候,那个“指针”就不会移动
 60                 * 也就是说,那个指针不会随着当前的那个活动的菜单项上下移动
 61                 * 此外还要注意的一点是,这个方法必须在
 62                 * menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_CONTENT);
 63                 * 之后调用,因为在menuDrawer还没有创建之前,是不能够调用这个方法的,否则会抛出
 64                 * NullPointException**/
 65                 menuDrawer.invalidate();
 66             }
 67         });
 68         
 69         //创建内容视图
 70         contentText = new TextView(this) ;
 71         //contentText.setBackgroundResource(R.drawable.img_frame_background);
 72         
 73         //加载菜单视图和内容视图
 74         menuDrawer.setMenuView(menuList);
 75         menuDrawer.setContentView(contentText);
 76        
//Animates the drawer slightly open until the user opens the drawer.
menuDrawer.peekDrawer();
 77         //设置ActionBar中的程序图标可见,并且显示那个向左的指示箭头标志
 78         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
 79             getActionBar().setDisplayHomeAsUpEnabled(true);
 80         }
 81         
 82     }
 83     
 84 
 85 
 86     //定义菜单视图的菜单项的监听器
 87     private AdapterView.OnItemClickListener mItemClickListener = new AdapterView.OnItemClickListener() {
 88         @Override
 89         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 90             
 91             //当前的活动菜单项为position
 92             currentActiveItem = position ;
 93             
 94             //为当前的活动项添加“指针”
 95             menuDrawer.setActiveView(view, position);
 96             
 97             //改变contentText中的内容
 98             contentText.setText(((TuiCool_MenuItem)(menuAdapter.getItem(position))).getMenuText()) ;
 99             
100             
101             //关闭菜单视图
102             menuDrawer.closeMenu(true);
103         }
104     };
105     
106 
107     
108     //《当用户按下了"手机上的返回功能按键"的时候会回调这个方法》
109     @Override
110     public void onBackPressed() {
111         final int drawerState = menuDrawer.getDrawerState();
112         if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING) {
113             menuDrawer.closeMenu();
114             return;
115         }
116         //也就是说,当按下返回功能键的时候,不是直接对Activity进行弹栈,而是先将菜单视图关闭
117         super.onBackPressed();
118     }
119     
120     @Override
121     public boolean onOptionsItemSelected(MenuItem item) {
122         switch (item.getItemId()) {
123             case android.R.id.home:  // 当单击了程序图标的位置时返回android.R.id.home
124                 //一次点击菜单视图打开,再一次单击则关闭菜单视图
125                 menuDrawer.toggleMenu();
126                 return true;
127         }
128 
129         return super.onOptionsItemSelected(item);
130     }
131 
132     //定义菜单分隔条类
133         private static class Category {
134             String mTitle;
135             Category(String title) {
136                 mTitle = title;
137             }
138         }
139         
140         //定义菜单项类
141         private static class TuiCool_MenuItem{
142             
143             private String menuText ;
144             private int menuIcon ;
145             
146             public TuiCool_MenuItem(String menuText , int menuIcon){
147                 this.menuText=menuText ;
148                 this.menuIcon=menuIcon ;
149             }
150             
151             public String getMenuText(){
152                 return this.menuText ;
153             }
154             
155             public int getMenuIcon(){
156                 return this.menuIcon ;
157             }
158         }
159         
160         //定义自定义菜单项组Adapter
161          private class MenuAdapter extends BaseAdapter{
162 
163              //用来存贮菜单项和菜单分隔条对象
164              private List<Object> menuItems = new ArrayList<Object>() ;
165              
166              //加载所有的菜单项和菜单分隔条
167              public MenuAdapter(){
168                  
169                  menuItems.add(new Category("分组一")) ;
170                  menuItems.add(new TuiCool_MenuItem("离线",R.drawable.img_1)) ;
171                  menuItems.add(new TuiCool_MenuItem("站点",R.drawable.img_2));
172                  
173                  menuItems.add(new Category("分组二"));
174                  menuItems.add(new TuiCool_MenuItem("搜索",R.drawable.img_3)) ;
175                  menuItems.add(new TuiCool_MenuItem("发现",R.drawable.img_4)) ;
176                  menuItems.add(new TuiCool_MenuItem("设置",R.drawable.img_5)) ;
177              }
178              
179             @Override
180             public int getCount() {
181                 return  menuItems.size();
182             }
183 
184             @Override
185             public Object getItem(int position) {
186                 return menuItems.get(position);
187             }
188 
189             @Override
190             public long getItemId(int position) {
191                 return position;
192             }
193             
194             /*这个方法和下面的一个方法只是用于标定你所创建的菜单中有几种类型的项目,
195              *一般来说就有两种,一种是分隔条项目、一种是实际的可选项目,这两个方法不会自动回调
196              *只是为让程序员在getView()等方法中能够方便使用这两个方法来判定当前的项目的类型
197              **/
198             public int getItemViewType(int position) {
199                 return getItem(position) instanceof TuiCool_MenuItem ? 0 : 1;
200             }
201 
202             @Override
203             public int getViewTypeCount() {
204                 return 2;
205             }
206 
207             //当前的对象对应的组件是否能够被选中或者被点击,即菜单项对象能够被点击,分隔条对象不能够被点击
208             @Override
209             public boolean isEnabled(int position) {
210                 return getItem(position) instanceof TuiCool_MenuItem;
211             }
212 
213             //指明Adapter中的所有的对象对应的组件是否都能够被点击
214             @Override
215             public boolean areAllItemsEnabled() {
216                 return false;
217             }
218 
219             @Override
220             public View getView(int position, View convertView, ViewGroup parent) {
221                 
222                 View view = convertView ;
223                 Object item = menuItems.get(position) ;
224 
225                 if(item instanceof TuiCool_MenuItem){
226                     if(view == null){
227                           view =getLayoutInflater().inflate(R.layout.menu_tuicool, parent, false);
228                     }
229                     ((TextView) view).setText(((TuiCool_MenuItem)item).getMenuText());
230                     ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(
231                                       ((TuiCool_MenuItem)item).getMenuIcon(), 0, 0, 0);
232                 }else{
233                     view = getLayoutInflater().inflate(R.layout.category_style, parent, false);
234                     ((TextView) view).setText(((Category)item).mTitle);
235                 }
236                                 
237                 //为每个view添加Tag
238                 view.setTag(R.id.mdActiveViewPosition, position); 
239                
240                 if (position == currentActiveItem) {
241                     menuDrawer.setActiveView(view, position);
242                 }
243        /*当我们将MenuDrawer库加入到当前工程中后,R文件中会自动生成上面的R.id.mdActivity
244         *在调用setActiveView()方法之前,必须得为每个view指定Tag,
245         *并且第一个参数属性值必须为R.id.mdActiveViewPosition
246         *既然我们已经在菜单的监听器中为当前活动的菜单项添加了“指针”为什么还要添加呢?
247         *因为,我们在监听方法中将菜单关闭了,当在一次显示菜单视图时,会进行重绘
248         *即重新调用这个getview()方法,所以我们要重新设定一下这个“指针”**/
249                 return view;
250             }         
251          }
252 }

 

菜单分隔条布局文件 category_style.xml

 

1 <?xml version="1.0" encoding="utf-8"?>
2 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
3     android:layout_width="match_parent"
4     android:layout_height="match_parent" 
5     style="@style/category_style">
6 </TextView>

 

菜单项布局文件 menu_tuicool.xml

1 <TextView
2      xmlns:android="http://schemas.android.com/apk/res/android"
3      android:layout_width="match_parent"
4      android:layout_height="wrap_content"
5      style="@style/item_style"/>

styles.xml文件

 

1 <resources>
 2 
 3     <!--
 4         Base application theme, dependent on API level. This theme is replaced
 5         by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
 6     -->
 7     <style name="AppBaseTheme" parent="android:Theme.Light">
 8         <!--
 9             Theme customizations available in newer API levels can go in
10             res/values-vXX/styles.xml, while customizations related to
11             backward-compatibility can go here.
12         -->
13     </style>
14 
15     <!-- Application theme. -->
16     <style name="AppTheme" parent="AppBaseTheme">
17         <!-- All customizations that are NOT specific to a particular API-level can go here. -->
18     </style>
19     
20     <!-- 为菜单项设置style -->
21     <style name="item_style">
22         <item name="android:background">@drawable/md__list_selector_disabled_holo_dark</item>
23         <item name="android:textAppearance">?android:attr/textAppearance</item>
24         <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
25         <item name="android:textSize">18sp</item>
26         <item name="android:paddingLeft">16dp</item>
27         <item name="android:paddingRight">32dp</item>
28         <item name="android:paddingTop">8dp</item>
29         <item name="android:paddingBottom">8dp</item>
30         <item name="android:drawablePadding">16dp</item>
31         <item name="android:gravity">center_vertical</item>
32     </style>
33     
34     <!-- 为分隔条设置style -->
35     <style name="category_style">
36         <item name="android:textStyle">bold</item>
37         <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
38         <item name="android:textSize">14sp</item>
39         <item name="android:textAllCaps">true</item>
40 
41         <item name="android:gravity">center_vertical</item>
42         <item name="android:paddingLeft">16dp</item>
43         <item name="android:background">@drawable/md__category_background</item>
44 
45         <item name="android:singleLine">true</item>
46         <item name="android:ellipsize">end</item>
47     </style>
48     
49     <!-- 为“左侧菜单”的“指针和宽度”设置style,在设置当前的activity的theme时要用 -->
       注意这里面的Widget.MenuDrawer、mdActiveIndicator、mdMenuSize字段都是MenuDrawer库中定义好的字段属性,我们只管直接用就行了
50     <style name="MenuDrawerStyle.Left" parent="Widget.MenuDrawer">       但是 MenuDrawerStyle.Left 这个名字可以自己取
51         <item name="mdActiveIndicator">@drawable/menu_arrow</item>       这张图就是效果中那个小的“指针”图片
52         <item name="mdMenuSize">250dp</item>  显然通过这个属性就能够更改菜单视图的宽度
53     </style>
54 </resources>

 

themes.xml文件

1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3     <style name="SampleTheme" parent="@android:style/Theme.Holo.Light">        SampleTheme这个明智可以自己取
4         <item name="menuDrawerStyle">@style/MenuDrawerStyle.Left</item>        menuDrawerStyle这个字段是MenuDrawer自定义的属性,只管用就好
5     </style>
6     
7 </resources>

Manifest.xml配置文件

1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.penglee.tuicool"
 4     android:versionCode="1"
 5     android:versionName="1.0" >
 6 
 7     <uses-sdk
 8         android:minSdkVersion="17"
 9         android:targetSdkVersion="17" />
10 
11     <application
12         android:allowBackup="true"
13         android:icon="@drawable/ic_launcher"
14         android:label="@string/app_name"
15         android:theme="@style/AppTheme"> 
16         <activity
17             android:name=".MenuTuicoolActivity"
18             android:label="@string/app_name" 
19          android:theme="@style/SampleTheme">  这样就能够使用“指针”功能了
20             <intent-filter>
21                 <action android:name="android.intent.action.MAIN" />
22 
23                 <category android:name="android.intent.category.LAUNCHER" />
24             </intent-filter>
25         </activity>
26     </application>
27 
28 </manifest>

【文档结构】


android fragment展开列表 android展开菜单_android_04

 

【分析】


虽然我们在内容视图部分使用的是一个TextView,在菜单视图中使用的是一个ListView,但是setContentView(View view)、setMenuView(View view)方法中并没有限制,你想所有的布局容器,如LinearLayout等,都是View 组件,我们可以以布局组件为View容器,之后可在里面添加任何的东西,甚至是Fragment,之后我们使用inflate方法将这个布局文件转化成View,装入MenuDrawer中,一切东西就都有了

 

正个项目文件源码在邮箱——名字叫做TuiCool