Android常见多条件筛选菜单(美团、58)

来源:互联网 发布:绘影绘声 mac 编辑:程序博客网 时间:2024/05/17 01:47

目录(?)[+]

  1. 效果介绍
  2. 简单实现
    1. 1布局分析
    2. 2内容的添加
    3. 3处理Tab点击
    4. 4处理Menu动画
  3. Android源码设计模式之Adapter适配器模式
    1. 1Android中适配器的运用
    2. 本View中Adapter适配器的运用

1. 效果介绍


这里写图片描述
  

2.简单实现


2.1布局分析:

  自定义ListPopuScreenMenuView 继承自 LinearLayout,大致分为三个部分:上面头部TabLinearLayout;中间的菜单内容menuContainerFrameLayout;下面的半透明的translucentView。但是为了配合动画效果我们要对布局稍作修改,需要把menuContainerFrameLayout和translucentView放进一个FrameLayout中:

public class ListPopuScreenMenuView extends LinearLayout{    private Context mContext;    // 顶部菜单布局    private LinearLayout mTabMenuView;    // 内容都放在这里面    private FrameLayout mMenuContainerView;    // 遮罩半透明View,点击可关闭HuiDropDownMenu    private View mMaskView;    // 遮罩颜色    private int mMaskColor = 0x88888888;    // menu的高度    protected int mMenuContainerHeight = 0;    public ListPopuScreenMenuView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        this.mContext = context;        initAttribute(attrs);        initLayout();    }    /**     * 初始化自定义属性     */    private void initAttribute(AttributeSet attrs) {        // TODO 设置自定义属性    }    /**    * 初始化布局    **/    private void initLayout() {        // 垂直排列        setOrientation(VERTICAL);        // 初始化tabMenuView并添加到this        mTabMenuView = new LinearLayout(mContext);        LayoutParams params = new LayoutParams(                ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.WRAP_CONTENT);        mTabMenuView.setOrientation(HORIZONTAL);        mTabMenuView.setBackgroundResource(R.drawable.menu_tab_bg);        mTabMenuView.setLayoutParams(params);        addView(mTabMenuView);        // 中间部分包括阴影和menu内容        FrameLayout mMiddleView = new FrameLayout(mContext);        mMiddleView.setLayoutParams(new FrameLayout.LayoutParams(                FrameLayout.LayoutParams.MATCH_PARENT,                FrameLayout.LayoutParams.MATCH_PARENT));        addView(mMiddleView);        // 先初始化遮罩半透明View        mMaskView = new View(getContext());        mMaskView.setBackgroundColor(mMaskColor);        mMaskView.setOnClickListener(this);        mMiddleView.addView(mMaskView);        mMaskView.setVisibility(GONE);        // 后初始化containerView并将其添加到mMiddleView        mMenuContainerView = new FrameLayout(mContext);        mMiddleView.addView(mMenuContainerView);        mMenuContainerView.setBackgroundColor(Color.WHITE);        mMenuContainerView.setVisibility(GONE);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        mMenuContainerHeight = MeasureSpec.getSize(heightMeasureSpec) * 75 / 100;        if(mMenuContainerHeight != 0 && mMenuContainerView.getHeight()<=0){            // 高度占整个父View的75%            mMenuContainerView.getLayoutParams().height = mMenuContainerHeight;        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

2.2内容的添加:

提供两个方法,我们往头部的TabLinearLayout中添加子Tab因为可以是TextView也可以是其他我们干脆就添加View:addTabView(View tabView);往中间的menuFrameLayout中添加菜单内容View:addMenuView(View menuView)

/*** 添加Tab头部* @param tabView*/public void addTabView(View tabView) {    mTabMenuView.addView(tabView);    LinearLayout.LayoutParams tabParams = (LayoutParams) tabView            .getLayoutParams();    // 权重设为1,每个子View占的宽度一样的    tabParams.weight = 1;}/*** 添加菜单View*/public void addMenuView(View menuView) {    mMenuContainerView.addView(menuView);    menuView.setVisibility(GONE);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

到了这一步可以在activity中测试一下看看效果是否ok。

2.3处理Tab点击

  我们在添加头部Tab的时候顺便要给他设置点击事件,根据转态来控制什么时候菜单该打开,什么时候菜单该关闭,什么时候该要改变菜单布局。

// 当前tab点击的位置protected int mCurrentPosition = -1;// 动画是否正在执行private boolean mAnimationExcute = false;/*** 添加Tab头部* @param tabView*/public void addTabView(View tabView) {    // ... 此处省略(上面有)    // 设置Tag就知道我点击的是哪一个位置    tabView.setTag(mTabViews.size());    // 把tabView存入到列表中,当点击的时候就可以根据位置取出来    mTabViews.add(tabView);    switchTabViewClick(tabView);}private void switchTabViewClick(final View tabView) {    tabView.setOnClickListener(new OnClickListener() {        @Override        public void onClick(View v) {        // 动画是否执行完毕        if(mAnimationExcute)return;        // 根据tag拿到点击的位置        int clickPosition = (int) tabView.getTag();        // 处理点击        tabClick(clickPosition,tabView);        }    });}/*** 头部点击*/private void tabClick(int position, View tabView) {    if(mCurrentPosition == position){        // 如果当前点击的位置 == 之前点击的位置 关闭菜单,mCurrentPosition置为-1        closeMenu(tabView);    }    else{        if(mCurrentPosition == -1){            // 显示当前的菜单            mMenuContainerView.getChildAt(position).setVisibility(VISIBLE);            // 打开菜单            openMenu(tabView,position);        }else{            // 如果菜单是打开的改变布局            exchangeLayout(position);        }        // 当前位置 == 点击位置        mCurrentPosition = position;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

2.4处理Menu动画

1.openMenu():menu内容View和半透明View设置显示,menuContainerView设置位移动画,半透明View设置Alpha动画;
2.closeMenu():menu内容View和半透明View设置隐藏,menuContainerView设置位移动画,半透明View设置Alpha动画;
3.exchangeLayout():将之前显示的menuView设置隐藏,当前点击位置的menuView设置显示。

// 菜单是否打开protected boolean mMenuIsOpen = false;/*** 关闭菜单*/public void closeMenu(View tabView) {    mMenuIsOpen = false;    mMenuContainerView.setVisibility(View.GONE);    mMaskView.setVisibility(GONE);    Animation menuOutAnimation = AnimationUtils.loadAnimation(getContext(),R.anim.dd_menu_out);    Animation maskOutAnimation = AnimationUtils.loadAnimation(getContext(),R.anim.dd_mask_out);    menuOutAnimation.setAnimationListener(this);    mMenuContainerView.setAnimation(menuOutAnimation);    mMaskView.setAnimation(maskOutAnimation);}/*** 改变显示布局*/private void exchangeLayout(int position) {    mMenuContainerView.getChildAt(position).setVisibility(VISIBLE);    mMenuContainerView.getChildAt(mCurrentPosition).setVisibility(GONE);}/*** 动画开始执行*/@Overridepublic void onAnimationStart(Animation animation) {    mAnimationExcute = true;}/*** 动画结束*/@Overridepublic void onAnimationEnd(Animation animation) {    mAnimationExcute = false;    if(!mMenuIsOpen){        // 如果是关闭菜单,隐藏当前menuView        mMenuContainerView.getChildAt(mCurrentPosition).setVisibility(GONE);        // 当前位置 == -1        mCurrentPosition = -1;    }}/*** 动画重复*/@Overridepublic void onAnimationRepeat(Animation animation) {    mAnimationExcute = true;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

到目前为止基本实现了功能,但是有几个问题:
1.点击的时候我要改变TabView的布局背景或是字体颜色,关闭的时候要还原等等;
2.View的形式千变万化各式各样如果在Activity中添加,那么activity的代码要有多少行呢?什么都放在activity中代码可读性就不说了。
3.等等 …
4.赶紧想办法解决
  

3.Android源码设计模式之Adapter适配器模式


3.1Android中适配器的运用

  ListView这个控件可以说是天天打交道,一般的用法大致如下:

// 代码省略 ListView myListView = (ListView)findViewById(listview_id); // 设置适配器 myListView.setAdapter(new MyAdapter(context, myDatas));// 适配器public class MyAdapter extends BaseAdapter{        private LayoutInflater mInflater;        List<String> mDatas ;         public MyAdapter(Context context, List<String> datas){            this.mInflater = LayoutInflater.from(context);            mDatas = datas ;        }        @Override        public int getCount() {            return mDatas.size();        }        @Override        public String getItem(int pos) {            return mDatas.get(pos);        }        @Override        public long getItemId(int pos) {            return pos;        }        // 解析、设置、缓存convertView以及相关内容        @Override        public View getView(int position, View convertView, ViewGroup parent) {             ViewHolder holder = null;            // Item View的复用            if (convertView == null) {                holder = new ViewHolder();                  convertView = mInflater.inflate(R.layout.my_listview_item, null);                // 获取title                holder.title = (TextView)convertView.findViewById(R.id.title);                convertView.setTag(holder);            } else {                holder = (ViewHolder)convertView.getTag();            }            holder.title.setText(mDatas.get(position));            return convertView;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

这看起来似乎还挺麻烦的,看到这里我们不禁要问,ListView为什么要使用Adapter模式呢?
我们知道,作为最重要的View,ListView需要能够显示各式各样的视图,每个人需要的显示效果各不相同,显示的数据类型、数量等也千变万化。那么如何隔离这种变化尤为重要。

Android的做法是增加一个Adapter层来应对变化,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了Adapter的接口,ListView就可以按照用户设定的显示效果、数量、数据来显示特定的Item View。通过代理数据集来告知ListView数据的个数( getCount函数 )以及每个数据的类型( getItem函数 ),最重要的是要解决Item View的输出。Item View千变万化,但终究它都是View类型,Adapter统一将Item View输出为View ( getView函数 ),这样就很好的应对了Item View的可变性。

至于内部是如何运作的我打算再写一篇:Android设计模式源码解析之适配器(Adapter)模式
  

3.2本View中Adapter适配器的运用

  需要改变tabView显示效果需要 3,显示多少条 1,根据position位置得到tabView 1,根据position位置得到menuView 1 , 总共6方法

    public abstract class MenuBaseAdapter {    /**     * 关闭菜单:用于改变tabView显示状态     * @param tabView     */    public abstract void overrideCloseMenu(View tabView);    /**     * 改变布局:     * @param cureentView 当前点击的View     * @param oldView 之前的View     */    public abstract void overrideExchangeLayout(View cureentView, View oldView,        int currentPostion,int oldPosition);    /**     * 打开菜单:用于改变tabView显示状态     */    public abstract void overrideOpenMenu(View tabView,int position);    /**     * 得到多少条     */    public abstract int getCount();      /**     * 得到Menu的内容     */    public abstract View getMenuView(int position,        FrameLayout menuContainerView,ListPopuScreenMenuView parent);      /**     * 得到Table的内容     */    public abstract View getTabView(int position,        LinearLayout tabContainerView,ListPopuScreenMenuView parent);     /**     * 关闭筛选菜单菜单     */    public void closeScreenMenu(View tabView){        mObservable.closeScreenMenu(tabView);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

ListPopuScreenMenuView中添加setAdapter(MenuAdapter adapter)

/*** 设置View适配器*/public void setAdapter(MenuBaseAdapter adapter){    if(adapter == null){        throw new NullPointerException("adapter is null...");    }    if(mAdapter != null){        this.mAdapter = null;        this.mTabMenuView.removeAllViews();        this.mMenuContainerView.removeAllViews();    }    mAdapter = adapter;    int count = mAdapter.getCount();    for (int index = 0; index < count; index++) {        // 添加Tab        View childTabView = mAdapter.getTabView(index, mTabMenuView, this);        if(childTabView != null){            addTabView(childTabView);        }        // 添加menu        View childMenuView = mAdapter.getMenuView(index, mMenuContainerView, this);        if(childMenuView != null){            addMenuView(childMenuView);        }    }}/*** 关闭菜单*/public void closeMenu(View tabView) {    // ... 省略    if(mAdapter != null)            mAdapter.overrideCloseMenu(tabView);}/*** 打开菜单*/public void openMenu(View tabView,int position) {    // ... 省略    if(mAdapter != null)            mAdapter.overrideOpenMenu(tabView, position);}/*** 改变布局*/public void exchangeLayout(View tabView) {    // ... 省略    if(mAdapter != null)            mAdapter.overrideExchangeLayout(cureentView, oldView,currentPostion,oldPosition);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

最后只有一个问题了我们要在adapter中关闭menu,可是我们adapter中根本没有ListPopuScreenMenuView,但是我们还有一个没有用那就是观察者模式,利用观察者模式就很容易实现了。
附源码地址:http://download.csdn.net/detail/z240336124/9402725

0 0
原创粉丝点击