仿百度糯米/美團二級菜單


好久沒有打開我的CSDN博客了,也是因爲年齡一年年大了,生活壓力大了,思考的事情已經不在項目上了,最近看見有人給我csdn上留言,說我好久沒有更新CSDN了,我剛好最近在開發一個商城項目,實現了一個簡單的控件,就和大家一起分享一下。
控件的效果就是類似百度糯米或者美團的二級菜單,我開發iOS的客戶端菜單功能,直接參考了git一個項目,對應的UI效果:




其實效果看起來還不錯。iOS開發完成以後,又要準備開發Android,發現對應網上的案例還是很少的,或者不是想要的效果。我想參考了別人的項目代碼,也爲開源項目做點貢獻,準備自己開發一個Android的menu項目;
折騰了大概三個小時,終於搞定了,效果如下:

從圖片不難看出,這是一個多級菜單,控制者填充數據源,所以實現的時候,儘量封裝的使用,使用者最好是能兩三行代碼搞定。

具體實現思路:
1、MenuView,實現了第一級菜單的封裝

①、view初始化和數據源定義;

②、繪製一級菜單;

③、控制子菜單的PopupWindow彈出框
代碼具體如下:

package com.spring.sky.menuproject.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;


import com.spring.sky.menuproject.AppInfoUtils;
import com.spring.sky.menuproject.R;

import java.util.List;

/**
 * Created by springsky on 16/10/24.
 */

public class MenuView extends LinearLayout implements View.OnClickListener, MenuPopupWindow.OnMenuListener {
    private String[] hintTexts;
    public List[] dataSource;
    public TextView[] textViews;
    private int textColor = R.color.gray_80;
    private int textColorSelected = R.color.orange;
    private int textSize;
    private int lineHeight ;
    private MenuPopupWindow menuPopupWindow;
    private OnMenuListener onMenuListener;
    View lineView;
    TextView lastTv;
    private IndexPath[] indexPaths;

    public MenuView(Context context) {
        super(context);
        init(context);
    }

    public MenuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MenuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public void setHintTexts(String[] hintTexts) {
        this.hintTexts = hintTexts;
    }

    public void setDataSource(List[] dataSource) {
        this.dataSource = dataSource;
        reloadData();
    }

    /***
     * 設置當前選中的數據
     * @param indexPath
     */
    public void setIndexPath(IndexPath indexPath) {
        setIndexPath(indexPath, false);
    }

    /***
     * 設置當前選中的內容
     * @param indexPath
     * @param actionMenu 是否通知監聽器
     */
    public void setIndexPath(IndexPath indexPath, boolean actionMenu) {
        indexPaths[indexPath.column] = indexPath;
        if (actionMenu) {
            TextView lastTv = textViews[indexPath.column];
            List<MenuModel> list = dataSource[indexPath.column];
            if(list == null || indexPath.row >= list.size()){
                return;
            }
            MenuModel left = list.get(indexPath.row);

            MenuModel menuModel = null;
            if (indexPath.item < 0) {
                menuModel = left;
            } else {
                MenuModel right = left.chindMenu.get(indexPath.item);
                menuModel = right;
            }
            lastTv.setText(menuModel.value);
            if (onMenuListener != null) {
                onMenuListener.onMenu(indexPath, menuModel);
            }
        }
    }


    public List[] getDataSource() {
        return dataSource;
    }

    /***
     * 初始化
     * @param context
     */
    private void init(Context context) {
        menuPopupWindow = new MenuPopupWindow(context);
        menuPopupWindow.setOnMenuListener(this);
        AppInfoUtils.getViewHeight(this);
        textSize = AppInfoUtils.spToPx(6);
        lineHeight = AppInfoUtils.dipToPx(1);
    }

    /***
     * 繪製一級菜單分類
     */
    private void reloadData() {
        removeAllViews();
        if (dataSource == null || dataSource.length < 1) {
            return;
        }
        int count = dataSource.length;
        int height = getMeasuredHeight() - lineHeight;
        setOrientation(LinearLayout.VERTICAL);
        LinearLayout menuBaseView = new LinearLayout(getContext());
        menuBaseView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height));
        menuBaseView.setWeightSum(count);
        menuBaseView.setGravity(Gravity.CENTER);
        menuBaseView.setOrientation(LinearLayout.HORIZONTAL);
        indexPaths = new IndexPath[count];
        textViews = new TextView[count];
        for (int i = 0; i < count; i++) {
            indexPaths[i] = new IndexPath(i, 0, -1);

            LinearLayout tempBaseView = new LinearLayout(getContext());
            tempBaseView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height, 1));
            tempBaseView.setGravity(Gravity.CENTER);
            TextView tv = new TextView(getContext());
            tv.setTextColor(getResources().getColor(textColor));
            tv.setTextSize(textSize);
            LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
            tv.setGravity(Gravity.CENTER);
            tv.setLayoutParams(params);
            tv.setMaxLines(1);
            tv.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.mipmap.triangle_down, 0);
            tv.setCompoundDrawablePadding(AppInfoUtils.dipToPx(2));
            tv.setId(i);
            tv.setOnClickListener(this);
            textViews[i] = tv;
            tempBaseView.addView(tv);
            menuBaseView.addView(tempBaseView);
            if (hintTexts != null && i < hintTexts.length) {
                tv.setText(hintTexts[i]);
            }
            View lineView = new View(getContext());
            lineView.setBackgroundColor(getResources().getColor(R.color.main_bg_in));
            menuBaseView.addView(lineView, new LayoutParams(AppInfoUtils.dipToPx(1), height - AppInfoUtils.dipToPx(8)));
        }
        addView(menuBaseView);

        lineView = new View(getContext());
        lineView.setBackgroundColor(getResources().getColor(R.color.main_bg_in));
        addView(lineView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, lineHeight));
    }

    /***
     * 一級菜單點擊事件觸發
     * @param v
     */
    @Override
    public void onClick(View v) {
        lastTv = (TextView) v;
        int column = v.getId();
        List<MenuModel> list = dataSource[column];
        lastTv.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.mipmap.triangle_up, 0);
        lastTv.setTextColor(getResources().getColor(textColorSelected));
        menuPopupWindow.setLeftList(column, list);
        IndexPath indexPath = indexPaths[column];
        menuPopupWindow.setSelect(indexPath.row, indexPath.item);
//        int[] location = new int[2];
//        lineView.getLocationOnScreen(location);
        menuPopupWindow.showAsDropDown(lineView);
//        menuPopupWindow.showAtLocation(this,Gravity.BOTTOM,0,0);
    }

    /***
     * 彈出框點擊事件處理
     * @param column
     * @param row
     * @param item
     * @param menuModel
     */
    @Override
    public void onMenu(int column, int row, int item, MenuModel menuModel) {
        TextView lastTv = textViews[column];
        lastTv.setText(menuModel.value);
        IndexPath indexPath = indexPaths[column];
        indexPath.row = row;
        indexPath.item = item;
        onMenuDismiss();
        if (onMenuListener != null) {
            onMenuListener.onMenu(indexPath, menuModel);
        }
    }

    /***
     * 彈出框關閉
     */
    @Override
    public void onMenuDismiss() {
        lastTv.setTextColor(getResources().getColor(R.color.gray_80));
        lastTv.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.mipmap.triangle_down, 0);
    }

    /***
     * 設置監聽器
     * @param onMenuListener
     */
    public void setOnMenuListener(OnMenuListener onMenuListener) {
        this.onMenuListener = onMenuListener;
    }

    public static interface OnMenuListener {
        void onMenu(IndexPath indexPath, MenuModel menuModel);
    }

    /****
     * 菜單列、行、二級子行
     */
    public static class IndexPath {
        public int column; //一級菜單
        public int row; //left row
        public int item; //right row

        public IndexPath(int column, int row, int item) {
            this.column = column;
            this.row = row;
            this.item = item;
        }
    }
}

2、PopupWIndow主要是實現了彈出框顯示子列的一級和二級菜單的數據。

我使用了兩個ListView來動態實現數據的加載。

具體代碼如下:

package com.spring.sky.menuproject.view;

import android.content.Context;
import android.graphics.drawable.PaintDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;


import com.spring.sky.menuproject.R;

import java.util.List;

/**
 * Created by springsky on 16/10/20.
 */

public class MenuPopupWindow extends PopupWindow implements AdapterView.OnItemClickListener {

    Context mContext;
    private ListView leftLv,rightLv;
    private OnMenuListener onMenuListener;
    private List<MenuModel> leftList,rightList;
    private MenuAdapter menuLeftAdapter,menuRightAdapter;
    private int column;
    boolean hasSecond;

    /***
     * 初始化
     * @param context
     */
    public MenuPopupWindow(Context context){
        this.mContext = context;
        View view = LayoutInflater.from(mContext).inflate(R.layout.menu_popup_window, null);
        leftLv = (ListView) view.findViewById(R.id.leftLv);
        leftLv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        rightLv = (ListView) view.findViewById(R.id.rightLv);
        rightLv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

        setContentView(view);

        setBackgroundDrawable(new PaintDrawable());
        setFocusable(true);


        setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);

        setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() { 
                leftLv.setSelection(0);
                rightLv.setSelection(0);
                if( onMenuListener != null ){
                    onMenuListener.onMenuDismiss();
                }
            }
        });

        menuLeftAdapter = new MenuAdapter(mContext);
        menuLeftAdapter.setColumn(0);
        menuLeftAdapter.setList(leftList);
        leftLv.setAdapter(menuLeftAdapter);
        leftLv.setOnItemClickListener(this);

        menuRightAdapter = new MenuAdapter(mContext);
        menuRightAdapter.setColumn(1);
        menuRightAdapter.setList(rightList);
        rightLv.setAdapter(menuRightAdapter);
        rightLv.setOnItemClickListener(this);
    }

    @Override
    public void showAsDropDown(View anchor) {
        super.showAsDropDown(anchor);
    }

    /***
     * 加載數據
     * @param column
     * @param leftList
     */
    public void setLeftList(int column,List<MenuModel> leftList) {
        this.column = column;
        this.leftList = leftList;
        hasSecond = false;
        for (MenuModel childModel : leftList){
            if(childModel.hasChind()){
                hasSecond = true;
                break;
            }
        }
        menuLeftAdapter.setList(leftList);
        if(!hasSecond){
            rightLv.setVisibility(View.GONE);
            setRightList(null);
        }else {
            rightLv.setVisibility(View.VISIBLE);
        }
    }

    /***
     * 默認選中的一級和二級行
     * @param row
     * @param item
     */
    public void setSelect(int row,int item){
        if(row < 0 || leftList == null || row >= leftList.size()){
            return;
        }
        MenuModel leftModel = leftList.get(row);
        leftLv.setSelection(row);
        menuLeftAdapter.setSelectPosition(row);

        setRightList(leftModel.chindMenu);
        if(item < 0 || rightList ==null || item >= rightList.size()){
            return;
        }
        rightLv.setSelection(item);
        menuRightAdapter.setSelectPosition(item);
    }

    private void setRightList(List<MenuModel> rightList) {
        this.rightList = rightList;
        menuRightAdapter.setList(rightList);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        if(parent.getId() == leftLv.getId()){
            MenuModel model = leftList.get(position);
            if(leftLv.getSelectedItemPosition() == position){
                return;
            }
            if(model.hasChind()){
                menuLeftAdapter.setSelectPosition(position);
                setRightList(model.chindMenu);
            }else {
                dismiss();
            }
            onMenuClick(position,0,model);
        }else {
            menuRightAdapter.setSelectPosition(position);
            MenuModel model = rightList.get(position);
            onMenuClick(menuLeftAdapter.getSelectPosition(),position,model);
            dismiss();
        }
    }

    void onMenuClick(int row,int item,MenuModel model){
        if(onMenuListener != null){
            onMenuListener.onMenu(column,row,item,model);
        }
    }

    public void setOnMenuListener(OnMenuListener onMenuListener) {
        this.onMenuListener = onMenuListener;
    }

    public static interface OnMenuListener{
        void onMenu(int column, int row, int item, MenuModel menuModel);
        void onMenuDismiss();
    }

}


3、其他的就是MenuModel,考慮是多級層次關係,所以建議使用鏈結構。

package com.spring.sky.menuproject.view;

import java.util.List;

/**
 * Created by springsky on 16/10/20.
 */

public class MenuModel {
    public Object key; //key
    public String value; //顯示的內容
    public List<MenuModel> chindMenu; //子列表數據

    public MenuModel(){
        super();
    }

    public MenuModel(Object key, String value, List<MenuModel> chindMenu){
        super();
        this.key = key;
        this.value = value;
        this.chindMenu = chindMenu;
    }

    /***
     * 是否有子列表數據
     * @return
     */
    public boolean hasChind(){
        return (chindMenu != null && chindMenu.size() > 0);
    }
}


誒,生活壓力大了,也不會寫博客了,就簡單描述一下,希望大家不要見怪。
項目的源碼,我已經提交到git上了。
下載地址:https://github.com/skyfouk/AndroidMenuProject.git

希望大家繼續完善吐舌頭


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