Android仿大众点评多条目下拉菜单筛选
首先来看看我们要实现的效果,下面这张图是大众点评APP里面的一个多条目下拉菜单筛选的一个效果,这是很多App里面都比较常见的一种多条目筛选菜单。

结构分析:

整个控件我么用一个LinearLayout 实现,所以我们要继承LinearLayout .然后是上面红色长方形部分的Tab栏,下面最外层黑色框框是一个FrameLayout用来存放阴影(绿色框框部分)和菜单布局(最里面红色框框部分)
实现
分析完了,我们就可以用代码来实现了,代码如下:
1、基本布局:
public class ListDataScreenView extends LinearLayout {private Context mContext;// 1.1 创建头部用来存放 Tabprivate LinearLayout mMenuTabView;// 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)private FrameLayout mMenuMiddleView;// 阴影private View mShadowView;// 创建菜单用来存放菜单内容private FrameLayout mMenuContainerView;// 阴影的颜色private int mShadowColor = 0x88888888;// 筛选菜单的 Adapterprivate BaseMenuAdapter mAdapter;// 内容菜单的高度private int mMenuContainerHeight;// 当前打开的位置private int mCurrentPosition = -1;private long DURATION_TIME = 350;// 动画是否在执行private boolean mAnimatorExecute;public ListDataScreenView(Context context) {this(context, null);}public ListDataScreenView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public ListDataScreenView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;initLayout();}/*** 1.布局实例化好 (组合控件)*/private void initLayout() {// 1. 先创建一个 xml 布局 ,再加载,findViewById// 2. 简单的效果用代码去创建 早期IOS 用代码创建布局setOrientation(VERTICAL);// 1.1 创建头部用来存放 TabmMenuTabView = new LinearLayout(mContext);mMenuTabView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));addView(mMenuTabView);// 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)mMenuMiddleView = new FrameLayout(mContext);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);params.weight = 1;mMenuMiddleView.setLayoutParams(params);addView(mMenuMiddleView);// 创建阴影 可以不用设置 LayoutParams 默认就是 MATCH_PARENT ,MATCH_PARENTmShadowView = new View(mContext);mShadowView.setBackgroundColor(mShadowColor);mShadowView.setAlpha(0f);mShadowView.setOnClickListener(this);mShadowView.setVisibility(GONE);mMenuMiddleView.addView(mShadowView);//阴影View在下面// 创建菜单用来存放菜单内容mMenuContainerView = new FrameLayout(mContext);mMenuContainerView.setBackgroundColor(Color.WHITE);mMenuMiddleView.addView(mMenuContainerView);//菜单内容在上面}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.e("TAG", "onMeasure");int height = MeasureSpec.getSize(heightMeasureSpec);if (mMenuContainerHeight == 0 && height > 0) {// 内容的高度应该不是全部 应该是整个 View的 75%mMenuContainerHeight = (int) (height * 75f / 100);//获取菜单内容View的LayoutParamsViewGroup.LayoutParams params = mMenuContainerView.getLayoutParams();//设置菜单内容的高度params.height = mMenuContainerHeight;mMenuContainerView.setLayoutParams(params);// 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)//放菜单内容mMenuContainerView.setTranslationY(-mMenuContainerHeight);}}}
2、设置点击事件,以及过程中的动画
/*** 设置tab的点击** @param tabView* @param position*/private void setTabClick(final View tabView, final int position) {tabView.setOnClickListener(new OnClickListener() {public void onClick(View v) {if (mCurrentPosition == -1) {// 没打开openMenu(position, tabView);} else {if (mCurrentPosition == position) {// 打开了,关闭closeMenu();} else {// 切换一下显示//拿到菜单容器里的子view(TextView)View currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);currentMenu.setVisibility(View.GONE);//将子view(TextView)设置无不可见mAdapter.menuClose(mMenuTabView.getChildAt(mCurrentPosition));//拿到当前点击的位置mCurrentPosition = position;currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);currentMenu.setVisibility(View.VISIBLE);mAdapter.menuOpen(mMenuTabView.getChildAt(mCurrentPosition));}}}});}/*** 关闭菜单*/private void closeMenu() {//动画正在执行,点击无效if (mAnimatorExecute) {return;}// 关闭动画 位移动画 透明度动画ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContainerView, "translationY", 0, -mMenuContainerHeight);translationAnimator.setDuration(DURATION_TIME);translationAnimator.start();mShadowView.setVisibility(View.VISIBLE);ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0f);alphaAnimator.setDuration(DURATION_TIME);// 要等关闭动画执行完才能去隐藏当前菜单alphaAnimator.addListener(new AnimatorListenerAdapter() {//动画执行完毕public void onAnimationEnd(Animator animation) {View menuView = mMenuContainerView.getChildAt(mCurrentPosition);menuView.setVisibility(View.GONE);mCurrentPosition = -1;mShadowView.setVisibility(GONE);mAnimatorExecute = false;}//关闭菜单动画开始public void onAnimationStart(Animator animation) {mAnimatorExecute = true;mAdapter.menuClose(mMenuTabView.getChildAt(mCurrentPosition));}});alphaAnimator.start();}/*** 打开菜单** @param position* @param tabView*/private void openMenu(final int position, final View tabView) {//动画正在执行,点击无效if (mAnimatorExecute) {return;}//设置阴影可见mShadowView.setVisibility(View.VISIBLE);// 获取当前位置显示当前菜单,菜单是加到了菜单容器View menuView = mMenuContainerView.getChildAt(position);menuView.setVisibility(View.VISIBLE);// 打开开启动画 位移动画 透明度动画//位移动画ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContainerView, "translationY", -mMenuContainerHeight, 0);translationAnimator.setDuration(DURATION_TIME);translationAnimator.start();//透明度动画ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 0f, 1f);alphaAnimator.setDuration(DURATION_TIME);alphaAnimator.addListener(new AnimatorListenerAdapter() {public void onAnimationEnd(Animator animation) {mAnimatorExecute = false;mCurrentPosition = position;}public void onAnimationStart(Animator animation) {mAnimatorExecute = true;// 把当前的 tab 传到外面mAdapter.menuOpen(tabView);}});alphaAnimator.start();}
3、写Adapter提供数据源
这个时候我们的效果还是死的,我么得给他设置写一个Adapter,现实情况下,我们的数据也是通常要去网络获取,所以不能写死。
>BaseMenuAdapter/*** 筛选菜单的 Adapter*/public abstract class BaseMenuAdapter {// 获取总共有多少条public abstract int getCount();// 获取当前的TabViewpublic abstract View getTabView(int position, ViewGroup parent);// 获取当前的菜单内容public abstract View getMenuView(int position, ViewGroup parent);/*** 菜单打开* @param tabView*/public void menuOpen(View tabView) {}/*** 菜单关闭* @param tabView*/public void menuClose(View tabView) {}}
具体的Adapter:
public class ListScreenMenuAdapter extends BaseMenuAdapter{private Context mContext;public ListScreenMenuAdapter(Context context){this.mContext = context;}private String[] mItems ={"附近","美食","智能排序","筛选"};public int getCount() {return mItems.length;}public View getTabView(int position, ViewGroup parent) {TextView tabView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_tab,parent,false);tabView.setText(mItems[position]);tabView.setTextColor(Color.BLACK);return tabView;}public View getMenuView(int position, ViewGroup parent) {// 真正开发过程中,不同的位置显示的布局不一样TextView menuView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu,parent,false);menuView.setText(mItems[position]);return menuView;}public void menuClose(View tabView) {TextView tabTv = (TextView) tabView;tabTv.setTextColor(Color.BLACK);}public void menuOpen(View tabView) {TextView tabTv = (TextView) tabView;tabTv.setTextColor(Color.RED);}}
有了Adapter之后,我们就可以给ListDataScreenView 以下方法,是不是感觉跟ListView很像。
/*** 设置 Adapter** @param adapter*/public void setAdapter(BaseMenuAdapter adapter) {this.mAdapter = adapter;// 获取有多少条int count = mAdapter.getCount();for (int i = 0; i < count; i++) {// 获取菜单的TabView tabView = mAdapter.getTabView(i, mMenuTabView);mMenuTabView.addView(tabView);//加一个TextViewLinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();params.weight = 1;tabView.setLayoutParams(params);// 设置tab点击事件setTabClick(tabView, i);// 获取菜单的内容(一个TextView)View menuView = mAdapter.getMenuView(i, mMenuContainerView);menuView.setVisibility(GONE);mMenuContainerView.addView(menuView);}// 内容还没有显示出来,打开的时候显示当前位置的菜单,关闭的时候隐藏,阴影点击应该要关闭菜单// 动画在执行的情况下就不要在响应动画事件// 打开和关闭 变化tab的显示 , 肯定不能把代码写到 ListDataScreen 里面来// 当菜单是打开的状态 不要执行动画只要切换}
最后看下我们实现的效果:

到这里就结束啦。
评论
