【Android】側滑菜單的實現方式

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章