Android——自带侧滑菜单DrawerLayout的使用方法
【DrawerLayout】
1、在Android的support库中增加了一个专门用于创建侧滑菜单的组件DrawerLayout,接下来我们就讲解一下怎样使用这个原生的组件创建一个仿推酷的应用
2、先来看看使用DrawerLayout的步骤:
①在布局文件中添加<android.support.v4.widget.DrawerLayout> 根元素
②在这个根元素中首先添加一个 内容视图组件,比如:我们添加一个<FrameLayout>容器
③再在这个根元素中添加侧滑菜单视图组件,一般来说就是一个ListView组件
④为ListView设定Adapter,和点击事件监听器
⑤为DrawerLayout添加 开、关 状态监听器
3、只要遵循上面的几步就能够完成一个侧滑菜单的创建,非常的简单,下面我们就通过模仿推酷客户端,一步一步的说明怎样创建侧滑菜单
【仿推酷】—— 源代码已经上传邮箱
【效果】
【功能描述】
1、打开应用的时候,主界面如图1,ActionBar没有显示应用图标,只显示“推酷”两个字
2、ActionBar右边有一个绿色的图标是一个action菜单,当我们打开侧滑菜单的时候这个菜单图标就会隐藏,如图2
3、当我们点击了侧滑菜单中的某项时,ActionBar中的标题内容显示为当前选中的项目的标题
4、同时,如果我们已经选中了某一项,达到了图3所示的状态,这时我们在打开侧滑菜单,这时ActionBar中的标题内容改为“推酷”,也就是说,当侧滑菜单给关闭的时候,ActionBar中的标题是选中的项目的标题,当侧滑菜单处于打开状态的时候,ActionBar上的标题显示为全局的“推酷”,同事那个绿色的action菜单图标被隐藏
5、当侧滑菜单处于打开状态的时候,如果我么单击了手机上的“返回”物理按键的话,侧滑菜单关闭
【创建步骤】
步骤一:在主布局文件中创建DrawerLayout根元素布局——avtivity_main.xml文件
<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"
android:background="@drawable/content"/>
<!-- The navigation drawer -->
<ListView android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#33333333"
android:dividerHeight="0dp"
android:background="#ffeeeeee"
android:scrollbars="none"/>
</android.support.v4.widget.DrawerLayout>
注意事项:
1、必须指定菜单视图组件ListView必须指定 android:layout_gravity属性,当属性值设为left的话侧滑菜单位于左边,否则位于右面,由于有的语言是从右到左写,那么只要指定属性值为 start 的话,android 会自行将侧滑菜单放在书写起始的一边
2、android:layout_width 指定了侧滑菜单的宽度,但是不能够超过320dp,否则会覆盖整个视图,就不好看了
3、必须先放内容视图的控件,之后放侧滑菜单视图控件
步骤二:创建侧滑菜单ListView的Adapter——DrawerAdapter.java文件
1 package com.example.navigaterdrawer; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.content.Context; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.BaseAdapter; 11 import android.widget.TextView; 12 13 /**定义菜单项类*/ 14 class TuiCoolMenuItem { 15 String menuTitle ; 16 int menuIcon ; 17 18 //构造方法 19 public TuiCoolMenuItem(String menuTitle , int menuIcon ){ 20 this.menuTitle = menuTitle ; 21 this.menuIcon = menuIcon ; 22 } 23 24 } 25 /**自定义设置侧滑菜单ListView的Adapter*/ 26 public class DrawerAdapter extends BaseAdapter{ 27 28 //存储侧滑菜单中的各项的数据 29 List<TuiCoolMenuItem> MenuItems = new ArrayList<TuiCoolMenuItem>( ) ; 30 //构造方法中传过来的activity 31 Context context ; 32 33 //构造方法 34 public DrawerAdapter( Context context ){ 35 36 this.context = context ; 37 38 MenuItems.add(new TuiCoolMenuItem("", R.drawable.peng)) ; 39 MenuItems.add(new TuiCoolMenuItem("推荐", R.drawable.advise)) ; 40 MenuItems.add(new TuiCoolMenuItem("发现", R.drawable.find)) ; 41 MenuItems.add(new TuiCoolMenuItem("主题", R.drawable.theme)) ; 42 MenuItems.add(new TuiCoolMenuItem("站点", R.drawable.point)) ; 43 MenuItems.add(new TuiCoolMenuItem("搜索", R.drawable.search)) ; 44 MenuItems.add(new TuiCoolMenuItem("离线", R.drawable.leave)) ; 45 MenuItems.add(new TuiCoolMenuItem("设置", R.drawable.set)) ; 46 } 47 48 @Override 49 public int getCount() { 50 51 return MenuItems.size(); 52 53 } 54 55 @Override 56 public TuiCoolMenuItem getItem(int position) { 57 58 return MenuItems.get(position) ; 59 } 60 61 @Override 62 public long getItemId(int position) { 63 64 return position ; 65 } 66 67 @Override 68 public View getView(int position, View convertView, ViewGroup parent) { 69 70 View view = convertView ; 71 if(view == null){ 72 view =LayoutInflater.from(context).inflate(R.layout.menudrawer_item, parent, false); 73 ((TextView) view).setText(getItem(position).menuTitle) ; 74 ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(getItem(position).menuIcon, 0, 0, 0) ; 75 } 76 return view ; 77 } 78 79 }
menudrawer_item.xml文件
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/item_style"> </TextView>
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 <style name="item_style"> 20 <item name="android:textAppearance">?android:attr/textAppearance</item> 21 <item name="android:textSize">18sp</item> 22 <item name="android:paddingLeft">16dp</item> 23 <item name="android:paddingRight">32dp</item> 24 <item name="android:paddingTop">10dp</item> 25 <item name="android:paddingBottom">10dp</item> 26 <item name="android:drawablePadding">16dp</item> 27 <item name="android:gravity">center_vertical</item> 28 </style> 29 30 </resources>
步骤三:主Activity文件——MainActivity.java
1、非常明显的是:接下来我们要为ListView设定 Adapter 以及 设定点击事件监听器,在单击事件监听器中我们实现的是:用Fragment来替换FrameLayout
这些都非常的简单,不再赘述
2、之后,我们就要为DrawerLayout添加菜单开、关状态监听器,为的是能够动态的改变ActionBar中的显示内容,这里面就要注意一下:
①为了能够监听侧滑菜单的开关状态我们需要调用DrawerLayout的setDrawerListener()方法,之后将实现了DrawerLayout.DrawerListener接口的实例对象当做参数,传进去,这个DrawerLayout.DrawerListener监听器中有两个方法onDrawerOpened()和DrawerClosed(),当侧滑菜单开、关时,会回调他们之中的某一个
②如果你监听侧滑菜单的开、关事件,并不是为了和ActionBar发生关系,是为了处理其他的事件,那么使用上面的方法就行了
③如果你监听侧滑菜单的开、关事件,就是为了和ActionBar发生关系,动态的更改ActionBar的内容,那么鼓励使用ActionBarDrawerToggle类 ,这个类继承了上面的DrawerLayout.DrawerListener接口,所以你依然能够重写上面的两个回调方法,之后调用DrawerLayout的setDrawerListener()方法,与此同时,正如你下面将会看到的那样:使用ActionBarDrawerToggle类作为监听器是因为:它已经将ActionBar上的应用图标单击事件和侧滑菜单的开关绑定好了,无需在人为的进行这方面的逻辑控制;而且使用它能够非常方便的进行“指示图标”的更换,只需要在创建这个类的对象的时候,将要使用的图标作为构造参数传进去即可
④当然,一般我们就使用google自带的默认的“指示图标”——三横
⑤需要注意的是:为了使ActionBarDrawerToggle监听器能够适应于Activity的生命周期,我们需要在Activity中的几个方法中添加一些语句,在下面的程序中我们会提到,非常的简单
1 package com.example.navigaterdrawer; 2 3 import android.app.Activity; 4 import android.app.Fragment; 5 import android.app.FragmentManager; 6 import android.app.FragmentTransaction; 7 import android.content.res.Configuration; 8 import android.os.Bundle; 9 import android.support.v4.app.ActionBarDrawerToggle; 10 import android.support.v4.widget.DrawerLayout; 11 import android.view.Menu; 12 import android.view.MenuItem; 13 import android.view.View; 14 import android.widget.AdapterView; 15 import android.widget.ListView; 16 import android.widget.Toast; 17 18 public class MainActivity extends Activity { 19 20 ListView menuDrawer ; //侧滑菜单视图 21 DrawerAdapter menuDrawerAdapter ; // 侧滑菜单ListView的Adapter 22 DrawerLayout mDrawerLayout ; // DrawerLayout组件 23 //当前的内容视图下(即侧滑菜单关闭状态下),ActionBar上的标题, 24 String currentContentTitle ; 25 ActionBarDrawerToggle mDrawerToggle ; //侧滑菜单状态监听器 26 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 33 //开始时显示全局标题“推酷” 34 currentContentTitle = getResources().getString(R.string.global_title) ; 35 36 //为侧滑菜单设置Adapter,并为ListView添加单击事件监听器 37 menuDrawer = (ListView)findViewById(R.id.left_drawer) ; 38 menuDrawerAdapter = new DrawerAdapter(this) ; 39 menuDrawer.setAdapter(menuDrawerAdapter); 40 menuDrawer.setOnItemClickListener(new DrawerItemClickListener()); 41 42 //为DrawerLayout注册状态监听器 43 mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout); 44 mDrawerToggle = new DrawerMenuToggle( 45 this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) ; 46 mDrawerLayout.setDrawerListener(mDrawerToggle); 47 48 //设置ActionBar的指示图标可见,设置ActionBar上的应用图标位置处可以被单击 49 getActionBar().setDisplayHomeAsUpEnabled(true); 50 getActionBar().setHomeButtonEnabled(true); 51 getActionBar().setTitle(currentContentTitle); 52 //隐藏ActionBar上的应用图标,只显示文字label 53 getActionBar().setDisplayShowHomeEnabled(false); 54 55 } 56 57 58 /**侧滑菜单单击事件监听器*/ 59 private class DrawerItemClickListener implements ListView.OnItemClickListener { 60 61 @Override 62 public void onItemClick(AdapterView<?> parent, View view, int position, 63 long id) { 64 65 selectItem(position); 66 67 } 68 69 public void selectItem(int position){ 70 71 //为内容视图加载新的Fragment 72 Bundle bd = new Bundle() ; 73 bd.putString(ContentFragment.SELECTED_ITEM,menuDrawerAdapter.getItem(position).menuTitle); 74 75 Fragment contentFragment = new ContentFragment( ) ; 76 contentFragment.setArguments(bd); 77 78 FragmentManager fragmentManager =getFragmentManager(); 79 FragmentTransaction transaction = fragmentManager.beginTransaction(); 80 transaction.replace(R.id.content_frame, contentFragment).commit(); 81 82 //将选中的菜单项置为高亮 83 menuDrawer.setItemChecked(position, true); 84 //将ActionBar中标题更改为选中的标题项 85 setTitle(menuDrawerAdapter.getItem(position).menuTitle); 86 //将当前的侧滑菜单关闭,调用DrawerLayout的closeDrawer()方法即可 87 mDrawerLayout.closeDrawer(menuDrawer); 88 } 89 90 public void setTitle( String title ){ 91 currentContentTitle = title ; // 更改当前的CurrentContentTitle标题内容 92 getActionBar().setTitle(title); 93 94 } 95 } 96 97 /**侧滑菜单状态监听器(开、关),通过继承ActionBarDrawerToggle实现*/ 98 private class DrawerMenuToggle extends ActionBarDrawerToggle{ 99 100 /** 101 * @param drawerLayout :就是加载的DrawerLayout容器组件 102 * @param drawerImageRes : 要使用的ActionBar左上角的指示图标 103 * @param openDrawerContentDescRes 、closeDrawerContentDescRes:开启和关闭的两个描述字段,没有太大的用处 104 * 105 * */ 106 public DrawerMenuToggle(Activity activity, DrawerLayout drawerLayout, 107 int drawerImageRes, int openDrawerContentDescRes, 108 int closeDrawerContentDescRes) { 109 110 super(activity, drawerLayout, drawerImageRes, openDrawerContentDescRes,closeDrawerContentDescRes); 111 112 } 113 114 /** 当侧滑菜单达到完全关闭的状态时,回调这个方法 */ 115 public void onDrawerClosed(View view) { 116 super.onDrawerClosed(view); 117 //当侧滑菜单关闭后,显示ListView选中项的标题,如果并没有点击ListView中的任何项,那么显示原来的标题 118 getActionBar().setTitle(currentContentTitle); 119 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() 120 } 121 122 /** 当侧滑菜单完全打开时,这个方法被回调 */ 123 public void onDrawerOpened(View drawerView) { 124 super.onDrawerOpened(drawerView); 125 getActionBar().setTitle(R.string.global_title); //当侧滑菜单打开时ActionBar显示全局标题"推酷" 126 invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() 127 } 128 }; 129 130 /**为了能够让ActionBarDrawerToggle监听器 131 * 能够在Activity的整个生命周期中都能够以正确的逻辑工作 132 * 需要添加下面两个方法*/ 133 @Override 134 protected void onPostCreate(Bundle savedInstanceState) { 135 super.onPostCreate(savedInstanceState); 136 // Sync the toggle state after onRestoreInstanceState has occurred. 137 mDrawerToggle.syncState(); 138 } 139 140 @Override 141 public void onConfigurationChanged(Configuration newConfig) { 142 super.onConfigurationChanged(newConfig); 143 mDrawerToggle.onConfigurationChanged(newConfig); 144 } 145 146 /**最后做一些菜单上处理*/ 147 @Override 148 public boolean onCreateOptionsMenu(Menu menu) { 149 // Inflate the menu; this adds items to the action bar if it is present. 150 getMenuInflater().inflate(R.menu.main, menu); 151 return true; 152 } 153 154 @Override 155 public boolean onOptionsItemSelected(MenuItem item) { 156 // Handle action bar item clicks here. The action bar will 157 // automatically handle clicks on the Home/Up button, so long 158 // as you specify a parent activity in AndroidManifest.xml. 159 int id = item.getItemId(); 160 //第一个if 要加上,为的是让ActionBarDrawerToggle以正常的逻辑工作 161 if (mDrawerToggle.onOptionsItemSelected(item)) { 162 return true; 163 } 164 if (id == R.id.action_settings) { 165 return true; 166 } 167 if(id == R.id.action_websearch){ 168 Toast.makeText(this, "webSearch 菜单项被单击", Toast.LENGTH_SHORT).show(); 169 } 170 return super.onOptionsItemSelected(item); 171 } 172 173 /**每次调用 invalidateOptionsMenu() ,下面的这个方法就会被回调*/ 174 @Override 175 public boolean onPrepareOptionsMenu(Menu menu) { 176 177 // 如果侧滑菜单的状态监听器在侧滑菜单打开和关闭时都调用了invalidateOptionsMenu()方法, 178 //当侧滑菜单打开时将ActionBar上的某些菜单图标隐藏起来,使得这时仅显示“推酷”这个全局标题 179 //本应用中是将ActiongBar上的action菜单项隐藏起来 180 181 boolean drawerOpen = mDrawerLayout.isDrawerOpen(menuDrawer);//判定当前侧滑菜单的状态 182 menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); 183 return super.onPrepareOptionsMenu(menu); 184 } 185 186 /**《当用户按下了"手机上的返回功能按键"的时候会回调这个方法》*/ 187 @Override 188 public void onBackPressed() { 189 boolean drawerState = mDrawerLayout.isDrawerOpen(menuDrawer); 190 if (drawerState) { 191 mDrawerLayout.closeDrawers(); 192 return; 193 } 194 //也就是说,当按下返回功能键的时候,不是直接对Activity进行弹栈,而是先将菜单视图关闭 195 super.onBackPressed(); 196 } 197 198 199 200 }
Fragment文件的创建——ContentFragment.java
1 package com.example.navigaterdrawer; 2 3 import android.app.Fragment; 4 import android.os.Bundle; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.TextView; 9 10 public class ContentFragment extends Fragment { 11 12 public static final String SELECTED_ITEM = "selected_item" ; 13 14 @Override 15 public View onCreateView(LayoutInflater inflater, ViewGroup container, 16 Bundle savedInstanceState) { 17 18 Bundle bd = getArguments( ) ; 19 20 View view = inflater.inflate(R.layout.fragment_content, null) ; 21 ( (TextView ) view ).setText(bd.getString(SELECTED_ITEM)) ; 22 return view ; 23 } 24 25 }
fragment_content.xml文件
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> </TextView>
主菜单的创建—— main.xml文件
1 <menu xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 tools:context="com.example.navigaterdrawer.MainActivity" > 4 5 <item 6 android:id="@+id/action_settings" 7 android:orderInCategory="100" 8 android:showAsAction="never" 9 android:title="@string/action_settings"/> 10 <!-- 添加一个action菜单项 ,显示一张图片,标题设为空--> 11 <item 12 android:id="@+id/action_websearch" 13 android:showAsAction="withText|ifRoom" 14 android:icon="@drawable/find" 15 android:title= ""/> 16 17 </menu>
string.xml文件
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">NavigaterDrawer</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="drawer_open"> Drawer is opened </string> <string name="drawer_close">Drawer is closed </string> <string name="global_title">推酷</string> </resources>
配置文件——Mainfest.xml文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.navigaterdrawer" 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="@android:style/Theme.DeviceDefault.Light"> 16 <activity 17 android:name=".MainActivity" 18 android:label="@string/app_name"> 19 <intent-filter> 20 <action android:name="android.intent.action.MAIN" /> 21 <category android:name="android.intent.category.LAUNCHER" /> 22 </intent-filter> 23 </activity> 24 </application> 25 26 </manifest>
【文件结构】
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架