对于侧滑菜单的实现方式,一种比较标准化的实现方式是DrawerLayout + NavigationView的方式,该方式的实现过程可以看我的另一篇博客【Android】Material Design 之三 NavigationView 使用?。然而,DrawerLayout + NavigationView的侧滑实现有个缺点就是,菜单的布局是单一的,只有一个图标、一个标题,当我们想要实现丰富的菜单布局时(如下图所示)该方法就不能满足我们的需要,此时,我们需要考虑如何让侧滑的菜单能有丰富样式的布局,本篇博客就来解决这个问题。
?实现上图中左右两种样式的侧滑菜单,同样需要DrawerLayout布局的支持,DrawerLayout布局中可有3个子布局,第一个布局必须为主界面,其他2个布局就是左、右两侧的布局,左右两个只放一个也可以。
这里我们在布局中左右两侧布局都使用,可以同时分别在左右两边布局中实现上图中两种侧滑菜单样式,只需要在2个子布局添加属性android:layout_gravity,值为start从左测滑出菜单,值为end从右侧滑出菜单。
上图中,左边是使用列表ListView控件实现菜单样式,右边是用网格GridView控件实现菜单样式,上方都是一个头布局。
布局文件:
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">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是自定义侧滑菜单"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_gravity="start">
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">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_gravity="end">
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">
头布局header.xml:
android:layout_width="match_parent"
android:layout_height="180dp"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/header_bg">
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/avator"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="摸爬滚打的程序媛"
android:padding="5dp"
android:textColor="#FFFFFF"/>
头布局中的背景?header_bg.xml:
android:angle="45"
android:startColor="#e965d3"
android:endColor="#ac68e7"/>
?listview_item.xml布局:
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
android:id="@+id/iv"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"/>
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"/>
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_right"
android:layout_centerVertical="true"/>
?gridview_item.xml布局:
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical"
android:gravity="center"
android:background="#FFFFFF">
android:id="@+id/iv"
android:layout_width="50dp"
android:layout_height="50dp" />
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textColor="#000000"/>
既然使用了列表,就需要适配器进行数据的填充,自定义可复用的适配器MyBaseAdapter.java:
public abstract class MyBaseAdapter extends BaseAdapter{
private List dataList;
private int layoutResId; //列表item的布局id
public MyBaseAdapter(List 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 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 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 lvAdapter;
private MyBaseAdapter gvAdapter;
private List 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(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(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实现侧滑菜单时,除了可以在布局中添加头布局
,还可以使用listview.addHeaderView()方法添加头文件,效果和在布局中添加的效果有些不同,如下图:
View view=LayoutInflater.from(this).inflate(R.layout.header,null);
listView.addHeaderView(view);
?