700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android自定义控件:仿美团下拉菜单及相关代码优化

Android自定义控件:仿美团下拉菜单及相关代码优化

时间:2020-09-21 13:33:40

相关推荐

Android自定义控件:仿美团下拉菜单及相关代码优化

背景

最近的项目中用到了类似美团中的下拉多选菜单,在实际开发过程中,也发现了一些问题,主要归纳如下:

1.当菜单较为复杂时,如果不能设计好代码逻辑,将造成控件难于维护 2.美团菜单可以连续点击顶部tab,切换不同菜单,而我使用的popupWindow似乎在展开一个菜单时点击其他tab,菜单就会收回。

本文将针对如上两个问题进行一些讨论,最终给出较为合理的解决方案。

程序结构

由于菜单涉及多级多项,如果把UI和其他逻辑堆在一起写,必然会造成代码过于庞大,甚至没有办法扩展,更谈不上及时变更需求。

ViewHolder与组合控件结合分割菜单逻辑

这里我采用了组合控件和ViewHolder结合的办法来处理耦合的问题。

组合控件的特点是可以直接定义在xml里无需做其他任何多余的操作,ViewHolder则可以灵活地提供View,并将这些View贴到需要的地方。

基于上述特征,我将固定的菜单栏设计为组合控件,提供各项菜单的tab,而将菜单的具体内容使用ViewHolder封装,在需要的时候从ViewHoder中拿到View,贴到我们需要放置的地方。同时,每个菜单中的UI逻辑也会被封装到ViewHolder中,这样,如果我们需要修改需求,直接改动对应的ViewHolder的代码,而不会影响其他代码。

这样我们代码就可以将复杂的UI逻辑分成相互独立的小块,想改哪里改哪里,妈妈再也不用担心产品经理为难我了…………

使用布局文件代替popupWindow

翻阅网上很多仿制的美团菜单例程,几乎都没有真正和美团app的菜单一样,我们可以查看官方app,点击一个tab展开菜单,当在点击下一个tab时,菜单并没有收回,而是显示了当前tab对应的内容。

由于很多demo都是使用popupWindow作为菜单的载体,而我在实际操作过程中发现popupWindow作为模态对话框非常难控制,而且还会引起其他问题,总之,我认为此处使用使用popupWindow并不合适。我在给菜单栏下面放了一块空布局,当向空布局中添加View时,空布局扩大,也就形成了下拉菜单的效果。

那么有同学要问了,这样的话不就会影响下面的其他布局的位置了?是的,所以我们的菜单栏必须放在RelativeLayout或者FrameLayout这类结构中,而且必须放在其顶层。

控件原型

了解了上述两个问题的解决方法,我们就可以大概勾勒一样我们的控件大体的模样了。如下图:

点击TAB1,TAB2,TAB3,内容View被对应的Holder中维护的View替换,我们清空内容View中的view时,由于这个View是包裹内容的,内容为空时高度自然变成0,也就是菜单收起的状态。我们可以为每个Holder设置相应的回调接口,这样我们的菜单View就能根据Holder的变化实时做出响应。

代码实现

1.ViewHolder

ViewHolder用来维护一个我们手动inflate出来的View,并提供刷新数据的方法。我们可以以此为基类,封装UI逻辑,ViewHolder间也可以灵活替换。

/*** 自绘控件封装类* Created by vonchenchen on /11/3 0003.*/public abstract class BaseWidgetHolder<T> {protected View mRootView;protected Context mContext;public abstract View initView();public abstract void refreshView(T data);public BaseWidgetHolder(Context context){mContext = context;mRootView = initView();mRootView.setTag(this);}public View getRootView(){return mRootView;}}

2.菜单View

这个View就是菜单栏主View,包括了三个TAB和下面的内容View,我们只需在工程中直接将这个类放入我们的布局文件中就可以了,注意,必须放在RelativieLayout或者FrameLayout中,而且必须是最顶层,否则内容View展开时会“挤”到其他布局。此处我们采用这种方式而不是popupWindow是因为popupWindow焦点改变可能会触发消失,这样无法实现点击不同的tab时,连续切换菜单的效果。

/**** 搜索菜单栏* Created by vonchenchen on /4/5 0005.*/public class SelectMenuView extends LinearLayout{private static final int TAB_SUBJECT = 1;private static final int TAB_SORT = 2;private static final int TAB_SELECT = 3;private Context mContext;private View mSubjectView;private View mSortView;private View mSelectView;private View mRootView;private View mPopupWindowView;private RelativeLayout mMainContentLayout;private View mBackView;/** type1 */private SubjectHolder mSubjectHolder;/** type2 */private SortHolder mSortHolder;/** type3 */private SelectHolder mSelectHolder;/** 与外部通信传递数据的接口 */private OnMenuSelectDataChangedListener mOnMenuSelectDataChangedListener;private RelativeLayout mContentLayout;private TextView mSubjectText;private ImageView mSubjectArrowImage;private TextView mSortText;private ImageView mSortArrowImage;private TextView mSelectText;private ImageView mSelectArrowImage;private List<String> mGroupList;private List<String> mPrimaryList;private List<String> mJuniorList;private List<String> mHighList;private List<List<String>> mSubjectDataList;private int mTabRecorder = -1;public SelectMenuView(Context context) {super(context);this.mContext = context;this.mRootView = this;init();}public SelectMenuView(Context context, AttributeSet attrs) {super(context, attrs);this.mContext = context;this.mRootView = this;init();}private void init(){mGroupList = new ArrayList<String>();mGroupList.add("A");mGroupList.add("B");mGroupList.add("C");mPrimaryList = new ArrayList<String>();mPrimaryList.add("A1");mPrimaryList.add("A2");mPrimaryList.add("A3");mJuniorList = new ArrayList<String>();mJuniorList.add("B1");mJuniorList.add("B2");mJuniorList.add("B3");mJuniorList.add("B4");mJuniorList.add("B5");mJuniorList.add("B6");mJuniorList.add("B7");mJuniorList.add("B8");mJuniorList.add("B9");mHighList = new ArrayList<String>();mHighList.add("C1");mHighList.add("C2");mHighList.add("C3");mHighList.add("C4");mHighList.add("C5");mHighList.add("C6");mHighList.add("C7");mHighList.add("C8");mHighList.add("C9");mSubjectDataList = new ArrayList<List<String>>();mSubjectDataList.add(mGroupList);mSubjectDataList.add(mPrimaryList);mSubjectDataList.add(mJuniorList);mSubjectDataList.add(mHighList);//type1mSubjectHolder = new SubjectHolder(mContext);mSubjectHolder.refreshData(mSubjectDataList, 0, -1);mSubjectHolder.setOnRightListViewItemSelectedListener(new SubjectHolder.OnRightListViewItemSelectedListener() {@Overridepublic void OnRightListViewItemSelected(int leftIndex, int rightIndex, String text) {if(mOnMenuSelectDataChangedListener != null){int grade = leftIndex+1;int subject = getSubjectId(rightIndex);mOnMenuSelectDataChangedListener.onSubjectChanged(grade+"", subject+"");}dismissPopupWindow();//Toast.makeText(UIUtils.getContext(), text, Toast.LENGTH_SHORT).show();mSubjectText.setText(text);}});//type2mSortHolder = new SortHolder(mContext);mSortHolder.setOnSortInfoSelectedListener(new SortHolder.OnSortInfoSelectedListener() {@Overridepublic void onSortInfoSelected(String info) {if(mOnMenuSelectDataChangedListener != null){mOnMenuSelectDataChangedListener.onSortChanged(info);}dismissPopupWindow();mSortText.setText(getSortString(info));//Toast.makeText(UIUtils.getContext(), info, Toast.LENGTH_SHORT).show();}});//type3mSelectHolder = new SelectHolder(mContext);mSelectHolder.setOnSelectedInfoListener(new SelectHolder.OnSelectedInfoListener() {@Overridepublic void OnselectedInfo(String gender, String type) {if(mOnMenuSelectDataChangedListener != null){mOnMenuSelectDataChangedListener.onSelectedChanged(gender, type);}dismissPopupWindow();//Toast.makeText(UIUtils.getContext(), gender+" "+type, Toast.LENGTH_SHORT).show();}});}private int getSubjectId(int index){return index;}@Overrideprotected void onFinishInflate() {super.onFinishInflate();View.inflate(mContext, R.layout.layout_search_menu, this);mSubjectText = (TextView) findViewById(R.id.subject);mSubjectArrowImage = (ImageView) findViewById(R.id.img_sub);mSortText = (TextView) findViewById(prehensive_sorting);mSortArrowImage = (ImageView) findViewById(R.id.img_cs);mSelectText = (TextView) findViewById(R.id.tv_select);mSelectArrowImage = (ImageView) findViewById(R.id.img_sc);mContentLayout = (RelativeLayout) findViewById(R.id.rl_content);mPopupWindowView = View.inflate(mContext, R.layout.layout_search_menu_content, null);mMainContentLayout = (RelativeLayout) mPopupWindowView.findViewById(R.id.rl_main);//mBackView = mPopupWindowView.findViewById(R.id.ll_background);mSubjectView = findViewById(R.id.ll_subject);mSortView = findViewById(R.id.ll_sort);mSelectView = findViewById(R.id.ll_select);//点击 type1 弹出菜单mSubjectView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if(mOnMenuSelectDataChangedListener != null){mOnMenuSelectDataChangedListener.onViewClicked(mSubjectView);}handleClickSubjectView();}});//点击 type2 弹出菜单mSortView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if(mOnMenuSelectDataChangedListener != null){mOnMenuSelectDataChangedListener.onViewClicked(mSortView);}handleClickSortView();}});//点击 type3 弹出菜单mSelectView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if(mOnMenuSelectDataChangedListener != null){mOnMenuSelectDataChangedListener.onViewClicked(mSelectView);}handleClickSelectView();}});//点击黑色半透明部分,菜单收回mContentLayout.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {dismissPopupWindow();}});}private void handleClickSubjectView(){//清空内容View中的ViewmMainContentLayout.removeAllViews();//将我们已经创建好的ViewHolder拿出,取出其中的View贴到内容View中mMainContentLayout.addView(mSubjectHolder.getRootView(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);//处理弹窗动作popUpWindow(TAB_SUBJECT);}private void handleClickSortView(){mMainContentLayout.removeAllViews();mMainContentLayout.addView(mSortHolder.getRootView(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);popUpWindow(TAB_SORT);}private void handleClickSelectView(){mMainContentLayout.removeAllViews();mMainContentLayout.addView(mSelectHolder.getRootView(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);popUpWindow(TAB_SELECT);}private void popUpWindow(int tab){if(mTabRecorder != -1) {resetTabExtend(mTabRecorder);}extendsContent();setTabExtend(tab);mTabRecorder = tab;}private void extendsContent(){mContentLayout.removeAllViews();RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);mContentLayout.addView(mPopupWindowView, params);}private void dismissPopupWindow(){mContentLayout.removeAllViews();setTabClose();}public void setOnMenuSelectDataChangedListener(OnMenuSelectDataChangedListener onMenuSelectDataChangedListener){this.mOnMenuSelectDataChangedListener = onMenuSelectDataChangedListener;}public interface OnMenuSelectDataChangedListener{void onSubjectChanged(String grade, String subjects);void onSortChanged(String sortType);void onSelectedChanged(String gender, String classType);void onViewClicked(View view);//筛选菜单,当点击其他处菜单收回后,需要更新当前选中项void onSelectedDismissed(String gender, String classType);}private void setTabExtend(int tab){if(tab == TAB_SUBJECT){mSubjectText.setTextColor(getResources().getColor(R.color.blue));mSubjectArrowImage.setImageResource(R.mipmap.ic_up_blue);}else if(tab == TAB_SORT){mSortText.setTextColor(getResources().getColor(R.color.blue));mSortArrowImage.setImageResource(R.mipmap.ic_up_blue);}else if(tab == TAB_SELECT){mSelectText.setTextColor(getResources().getColor(R.color.blue));mSelectArrowImage.setImageResource(R.mipmap.ic_up_blue);}}private void resetTabExtend(int tab){if(tab == TAB_SUBJECT){mSubjectText.setTextColor(getResources().getColor(R.color.gray));mSubjectArrowImage.setImageResource(R.mipmap.ic_down);}else if(tab == TAB_SORT){mSortText.setTextColor(getResources().getColor(R.color.gray));mSortArrowImage.setImageResource(R.mipmap.ic_down);}else if(tab == TAB_SELECT){mSelectText.setTextColor(getResources().getColor(R.color.gray));mSelectArrowImage.setImageResource(R.mipmap.ic_down);}}private void setTabClose(){mSubjectText.setTextColor(getResources().getColor(R.color.text_color_gey));mSubjectArrowImage.setImageResource(R.mipmap.ic_down);mSortText.setTextColor(getResources().getColor(R.color.text_color_gey));mSortArrowImage.setImageResource(R.mipmap.ic_down);mSelectText.setTextColor(getResources().getColor(R.color.text_color_gey));mSelectArrowImage.setImageResource(R.mipmap.ic_down);}private String getSortString(String info){if(SortHolder.SORT_BY_NORULE.equals(info)){return "sort1";}else if(SortHolder.SORT_BY_EVALUATION.equals(info)){return "sort2";}else if(SortHolder.SORT_BY_PRICELOW.equals(info)){return "sort3";}else if(SortHolder.SORT_BY_PRICEHIGH.equals(info)){return "sort4";}else if(SortHolder.SORT_BY_DISTANCE.equals(info)){return "sort5";}return "sort1";}public void clearAllInfo(){//清除控件内部选项mSubjectHolder.refreshData(mSubjectDataList, 0, -1);mSortHolder.refreshView(null);mSelectHolder.refreshView(null);//清除菜单栏显示mSubjectText.setText("type1");mSortText.setText("type2");}}

以下是demo的实现效果,点击不同tab,下面菜单实现连续切换:

代码地址 /vonchenchen/menu_demo.git

下载地址 /detail/lidec/9498648

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。