對於側滑菜單的實現方式,一種比較標準化的實現方式是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);