对于侧滑菜单的实现方式,一种比较标准化的实现方式是DrawerLayout + NavigationView的方式,该方式的实现过程可以看我的另一篇博客【Android】Material Design 之三 NavigationView 使用 。然而,DrawerLayout + NavigationView的侧滑实现有个缺点就是,菜单的布局是单一的,只有一个图标、一个标题,当我们想要实现丰富的菜单布局时(如下图所示)该方法就不能满足我们的需要,此时,我们需要考虑如何让侧滑的菜单能有丰富样式的布局,本篇博客就来解决这个问题。
实现上图中左右两种样式的侧滑菜单,同样需要DrawerLayout布局的支持,DrawerLayout布局中可有3个子布局,第一个布局必须为主界面,其他2个布局就是左、右两侧的布局,左右两个只放一个也可以。
这里我们在布局中左右两侧布局都使用,可以同时分别在左右两边布局中实现上图中两种侧滑菜单样式,只需要在2个子布局添加属性android:layout_gravity,值为start从左测滑出菜单,值为end从右侧滑出菜单。
上图中,左边是使用列表ListView控件实现菜单样式,右边是用网格GridView控件实现菜单样式,上方都是一个头布局。
布局文件:
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MyMenuViewActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是自定义侧滑菜单"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_gravity="start"> <include layout="@layout/header"/> <ListView android:id="@+id/lv" android:background="#ffffff" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#cacaca" android:dividerHeight="2dp" android:scrollbars="none"> </ListView> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_gravity="end"> <include layout="@layout/header"/> <GridView android:id="@+id/gv" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none" android:numColumns="2" android:background="#808080" android:horizontalSpacing="2dp" android:verticalSpacing="2dp"> </GridView> </LinearLayout> </android.support.v4.widget.DrawerLayout>
头布局header.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="180dp" android:orientation="vertical" android:gravity="center" android:background="@drawable/header_bg"> <ImageView android:layout_width="50dp" android:layout_height="50dp" android:src="@drawable/avator"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="摸爬滚打的程序媛" android:padding="5dp" android:textColor="#FFFFFF"/> </LinearLayout>
头布局中的背景 header_bg.xml:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:type="linear" android:angle="45" android:startColor="#e965d3" android:endColor="#ac68e7"/> </shape>
listview_item.xml布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp"> <ImageView android:id="@+id/iv" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true"/> <TextView android:id="@+id/tv" android:layout_toRightOf="@+id/iv" android:layout_marginLeft="5dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15sp" android:textColor="#000000" android:layout_centerVertical="true"/> <ImageView android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right" android:layout_centerVertical="true"/> </RelativeLayout>
gridview_item.xml布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp" android:orientation="vertical" android:gravity="center" android:background="#FFFFFF"> <ImageView android:id="@+id/iv" android:layout_width="50dp" android:layout_height="50dp" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15sp" android:textColor="#000000"/> </LinearLayout>
既然使用了列表,就需要适配器进行数据的填充,自定义可复用的适配器MyBaseAdapter.java:
public abstract class MyBaseAdapter<T> extends BaseAdapter{ private List<T> dataList; private int layoutResId; //列表item的布局id public MyBaseAdapter(List<T> dataList,int layoutResId) { this.dataList = dataList; this.layoutResId=layoutResId; } //获取数据个数 @Override public int getCount() { return dataList != null ? dataList.size() : 0; } //获取指定位置的数据 @Override public Object getItem(int i) { return dataList.get(i); } //获取指定位置下标 @Override public long getItemId(int i) { return i; } //获取item布局 @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder=ViewHolder.bind(viewGroup.getContext(),view,viewGroup,layoutResId,i); bindView(viewHolder,dataList.get(i)); return viewHolder.getItemView(); } public abstract void bindView(ViewHolder holder,T data); public static class ViewHolder{ private SparseArray<View> views; //存放ListView的子项item中的控件view private View convertView; //存放convertView private int position; //游标 private Context context; //上下文 //构造方法,完成相关初始化 private ViewHolder(Context context,ViewGroup parent,int layoutResId){ views=new SparseArray<>(); this.context=context; convertView=LayoutInflater.from(context).inflate(layoutResId,parent,false); convertView.setTag(this); } //绑定ViewHolder和Item public static ViewHolder bind(Context context,View convertView,ViewGroup parent,int layoutResId,int position){ ViewHolder viewHolder; if(convertView==null){ viewHolder=new ViewHolder(context,parent,layoutResId); }else{ viewHolder= (ViewHolder) convertView.getTag(); viewHolder.convertView=convertView; } viewHolder.position=position; return viewHolder; } //根据id获取集合中保存的控件 @SuppressWarnings("unchecked") private <T extends View> T getView(int resId){ T t= (T) views.get(resId); if(t==null){ t=convertView.findViewById(resId); views.put(resId,t); } return t; } //获取子项的View public View getItemView(){ return convertView; } //获取子项位置 public int getItemPosition(){ return position; } //文本控件 设置文字 public void setText(int resId, CharSequence text){ View view=getView(resId); if (view instanceof TextView) { ((TextView) view).setText(text); } } //图片控件或者可设置背景图片的控件 设置图片 public void setImageResource(int resId, int drawableResId){ View view=getView(resId); if (view instanceof ImageView) { ((ImageView) view).setImageResource(drawableResId); }else{ view.setBackgroundResource(drawableResId); } } //可监听控件 设置点击监听 public ViewHolder setOnClickListener(int resId,View.OnClickListener listener){ getView(resId).setOnClickListener(listener); return this; } //控件设置可见 public ViewHolder setVisibility(int resId,int visible){ getView(resId).setVisibility(visible); return this; } //设置标签 public ViewHolder setTag(int resId,Object obj){ getView(resId).setTag(obj); return this; } //其他方法可自行扩展 } }
定义填充的数据bean:
public class MenuBean { private int imgResId; private String title; public MenuBean(int imgResId, String title) { this.imgResId = imgResId; this.title = title; } public int getImgResId() { return imgResId; } public void setImgResId(int imgResId) { this.imgResId = imgResId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
代码如下:
public class MyMenuViewActivity extends AppCompatActivity { private ListView listView; private GridView gridView; private MyBaseAdapter<MenuBean> lvAdapter; private MyBaseAdapter<MenuBean> gvAdapter; private List<MenuBean> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_snavigation_view); listView=findViewById(R.id.lv); gridView=findViewById(R.id.gv); //数据源 list=new ArrayList<>(); list.add(new MenuBean(R.drawable.ic_menu_book,"头条")); list.add(new MenuBean(R.drawable.ic_menu_star,"收藏")); list.add(new MenuBean(R.drawable.ic_menu_argu,"论坛")); list.add(new MenuBean(R.drawable.ic_menu_load,"下载")); list.add(new MenuBean(R.drawable.ic_menu_rili,"日历")); list.add(new MenuBean(R.drawable.ic_menu_level,"等级")); list.add(new MenuBean(R.drawable.ic_menu_cloud,"云空间")); list.add(new MenuBean(R.drawable.ic_menu_help,"帮助")); lvAdapter=new MyBaseAdapter<MenuBean>(list,R.layout.listview_item) { @Override public void bindView(ViewHolder holder, MenuBean data) { holder.setImageResource(R.id.iv,data.getImgResId()); holder.setText(R.id.tv,data.getTitle()); } }; gvAdapter=new MyBaseAdapter<MenuBean>(list,R.layout.gridview_item) { @Override public void bindView(ViewHolder holder, MenuBean data) { holder.setImageResource(R.id.iv,data.getImgResId()); holder.setText(R.id.tv,data.getTitle()); } }; listView.setAdapter(lvAdapter); gridView.setAdapter(gvAdapter); } }
运行效果就如开始的那张图,ListView的分割线可以通过下面两个属性实现:
android:divider="#cacaca" //分割线颜色 android:dividerHeight="2dp" //分割线高度
GridView的分割线实现,首先在设置其item背景,然后在其控件中 设置下面三个属性:
android:background="#808080" //GridView背景颜色 android:horizontalSpacing="2dp" //列之间的水平间隔 android:verticalSpacing="2dp //行之间的垂直间隔
这里再补充说一下,使用ListView实现侧滑菜单时,除了可以在布局中添加头布局
<include layout="@layout/snavigation_view_header"/>,还可以使用listview.addHeaderView()方法添加头文件,效果和在布局中添加的效果有些不同,如下图:
View view=LayoutInflater.from(this).inflate(R.layout.header,null); listView.addHeaderView(view);