当前位置:主页 > 移动开发 > iOS代码 >

iOS的客户端菜单功能仿百度糯米/美团二级菜单

时间:2021-03-30 09:09:19 | 栏目:iOS代码 | 点击:

我刚好最近在开发一个商城项目,实现了一个简单的控件,就和大家一起分享一下。

控件的效果就是类似百度糯米或者美团的二级菜单,我开发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

您可能感兴趣的文章:

相关文章