源码地址:https://github.com/licong/android-menudrawer
MenuDrawer ——简单的使用方法
今天,我们需要达到的效果是,使用MenuDrawer创建一个侧滑菜单视图,当我们点击菜单项(使用ListView)时,右面的内容视图(就是一个TextView)会响应这个点击事件
默认是菜单项隐藏在左边,当从左边缘滑动时,菜单视图出现,回滑时,菜单隐藏
先看看效果图吧:
【用到的 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,效果如下:
填充窗口模式:MenuDrawer.MENU_DRAG_WINDOW,效果如下:
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>
【文档结构】
【分析】
虽然我们在内容视图部分使用的是一个TextView,在菜单视图中使用的是一个ListView,但是setContentView(View view)、setMenuView(View view)方法中并没有限制,你想所有的布局容器,如LinearLayout等,都是View 组件,我们可以以布局组件为View容器,之后可在里面添加任何的东西,甚至是Fragment,之后我们使用inflate方法将这个布局文件转化成View,装入MenuDrawer中,一切东西就都有了
正个项目文件源码在邮箱——名字叫做TuiCool