Android常见多条件筛选菜单(美团、58)
来源:互联网 发布:绘影绘声 mac 编辑:程序博客网 时间:2024/05/17 01:47
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
- 效果介绍
- 简单实现
- 1布局分析
- 2内容的添加
- 3处理Tab点击
- 4处理Menu动画
- Android源码设计模式之Adapter适配器模式
- 1Android中适配器的运用
- 本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
- Android常见多条件筛选菜单(美团、58)
- Android常见多条件筛选菜单(美团、58)
- 自己造轮子--android常用多条件筛选菜单实现思路(类似美团,爱奇艺电影票下拉菜单)
- android 多条件筛选菜单效果
- Android最牛逼的多条件筛选菜单
- Android最牛逼的多条件筛选菜单
- -android常用多条件帅选菜单实现思路(类似美团,爱奇艺电影票下拉菜单)
- 安卓实现多下拉筛选多条件筛选菜单多级筛选菜单
- Android高阶UI之一个实用的多条件筛选菜单
- 类似美团的多条件筛选弹出框简单实现
- android筛选菜单实现
- Android仿美团筛选菜单
- Android 菜单筛选器
- 用phpcms做多种条件筛选菜单
- php多条件筛选
- 多条件筛选
- Android仿大众点评、美团筛选框
- 类似美团,糯米,大众点评的筛选排序菜单(EasyDropDownMenu)
- Mac Mysql5.7.6+免安装版教程
- Java学习----day9——(1)泛型初步了解
- JArray ,JObject 及反序列化实体类等问题
- 网络攻击技术
- 解决webstorm无法启动的问题
- Android常见多条件筛选菜单(美团、58)
- 生产者-消费者
- 对象的创建与销毁
- 露台花园的装修设计原则
- hdu_1864
- java程序员从入门到放弃资源大放送(文末附下载地址)
- webpack 时时编译
- (25):列表优先于数组
- PS学习小结(2)