Android仿大众点评多条目下拉菜单筛选

龙旋

共 10306字,需浏览 21分钟

 · 2022-07-07

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



结构分析:



整个控件我么用一个LinearLayout  实现,所以我们要继承LinearLayout  .然后是上面红色长方形部分的Tab栏,下面最外层黑色框框是一个FrameLayout用来存放阴影(绿色框框部分)和菜单布局(最里面红色框框部分)


实现

分析完了,我们就可以用代码来实现了,代码如下:


1、基本布局:

public class ListDataScreenView extends LinearLayout  {    private Context mContext;    // 1.1 创建头部用来存放 Tab    private LinearLayout mMenuTabView;    // 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)    private FrameLayout mMenuMiddleView;    // 阴影    private View mShadowView;    // 创建菜单用来存放菜单内容    private FrameLayout mMenuContainerView;    // 阴影的颜色    private int mShadowColor = 0x88888888;    // 筛选菜单的 Adapter    private 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 创建头部用来存放 Tab mMenuTabView = 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_PARENT mShadowView = 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);//菜单内容在上面 }
@Override protected 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的LayoutParams ViewGroup.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() {            @Override            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() { //动画执行完毕 @Override public void onAnimationEnd(Animator animation) { View menuView = mMenuContainerView.getChildAt(mCurrentPosition); menuView.setVisibility(View.GONE); mCurrentPosition = -1; mShadowView.setVisibility(GONE); mAnimatorExecute = false; }
//关闭菜单动画开始 @Override 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() { @Override public void onAnimationEnd(Animator animation) { mAnimatorExecute = false; mCurrentPosition = position; }
@Override 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(); // 获取当前的TabView public 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 ={"附近","美食","智能排序","筛选"};
@Override public int getCount() { return mItems.length; }
@Override 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; }
@Override 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; }
@Override public void menuClose(View tabView) { TextView tabTv = (TextView) tabView; tabTv.setTextColor(Color.BLACK); }
@Override 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++) {            // 获取菜单的Tab            View tabView = mAdapter.getTabView(i, mMenuTabView);            mMenuTabView.addView(tabView);//加一个TextView            LinearLayout.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 里面来 // 当菜单是打开的状态 不要执行动画只要切换    }

最后看下我们实现的效果:



到这里就结束啦。

浏览 130
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报