版权声明:本文为博主原创文章,未经博主允许不得转载。
说明
本来很懒,但是还是会忍不住的写下这有用既没有用的所谓技术博客,希望会给你带来有所启发,因为这样的功能,写的人很多,也是为了自己能够理解的够透彻,也是为了大家也能更好的理解吧,其实自定义控件,也就是那么回事,没有什么可难的,好吧,可能是这个太简单了。
这是58,赶集都用到的控件,这样的控件,自定义起来其实也并不难,自己项目当中用到了这个,于是就研究了一番,可能在你的项目当中也有可能会遇到这样的需求,正好,也可以来研究下,说真的,今天你见到的这个效果,比58,和赶集做的效果 要好多了,他们做的真的很low。
效果展示
先来看下他长什么样子吧
对,就是这个样子,今天我们就来看看到底是怎么做出来的。
思路
整体的所有布局都是用代码创建出来的,不是写死在布局文件里的,这样会更灵活
- 创建菜单栏,就是地址呀,年龄,性别的布局,水平的LinearLayout
- 向菜单栏里添加布局,就是一个简单的TextView,和分割线,然后将该布局添加至父控件中,就是最外层布局
- 创建下划线,再添加至父控件中
- 创建内容显示区,注意,内容显示区包括上图中的显示的内容(这里是内容区域),还有点击下拉菜单出来的布局,和半透明的背景,都是在内容区的,他的布局是FrameLayout,注意FrameLayout的特点,有不熟悉他的特点的可以先查一下他的特点,这样就会更好的理解这个控件的绘制了。
- 内容显示区为FrameLayout,他里面包括3部分,最里层是真正的的内容显示区,就是上图中看到的文字(这里是内容区域),中间层是半透明区域,就是大家看到的变暗的背景,最外层是点击下拉菜单之后弹出来的内容
代码实现
下面将会带你用代码我做这个控件的思路
public class DropDownMenu extends LinearLayout {
//顶部菜单布局
private LinearLayout tabMenuView;
//底部容器,包含popupMenuViews,maskView
private FrameLayout containerView;
//弹出菜单父布局
private FrameLayout popupMenuViews;
//遮罩半透明View,点击可关闭DropDownMenu
private View maskView;
//tabMenuView里面选中的tab位置,-1表示未选中
private int current_tab_position = -1;
//分割线颜色
private int dividerColor = 0xffcccccc;
//tab选中颜色
private int textSelectedColor = 0xff890c85;
//tab未选中颜色
private int textUnselectedColor = 0xff111111;
//遮罩颜色
private int maskColor = 0x88888888;
//tab字体大小
private int menuTextSize = 14;
//tab选中图标
private int menuSelectedIcon;
//tab未选中图标
private int menuUnselectedIcon;
public DropDownMenu(Context context) {
super(context, null);
}
public DropDownMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DropDownMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOrientation(VERTICAL);
//为DropDownMenu添加自定义属性
int menuBackgroundColor = 0xffffffff;
int underlineColor = 0xffcccccc;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DropDownMenu);
underlineColor = a.getColor(R.styleable.DropDownMenu_underlineColor, underlineColor);
dividerColor = a.getColor(R.styleable.DropDownMenu_dividerColor, dividerColor);
textSelectedColor = a.getColor(R.styleable.DropDownMenu_textSelectedColor, textSelectedColor);
textUnselectedColor = a.getColor(R.styleable.DropDownMenu_textUnselectedColor, textUnselectedColor);
menuBackgroundColor = a.getColor(R.styleable.DropDownMenu_menuBackgroundColor, menuBackgroundColor);
maskColor = a.getColor(R.styleable.DropDownMenu_maskColor, maskColor);
menuTextSize = a.getDimensionPixelSize(R.styleable.DropDownMenu_menuTextSize, menuTextSize);
menuSelectedIcon = a.getResourceId(R.styleable.DropDownMenu_menuSelectedIcon, menuSelectedIcon);
menuUnselectedIcon = a.getResourceId(R.styleable.DropDownMenu_menuUnselectedIcon, menuUnselectedIcon);
a.recycle();
//初始化tabMenuView并添加到tabMenuView
tabMenuView = new LinearLayout(context);
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
tabMenuView.setOrientation(HORIZONTAL);
tabMenuView.setBackgroundColor(menuBackgroundColor);
tabMenuView.setLayoutParams(params);
addView(tabMenuView, 0);
//为tabMenuView添加下划线
View underLine = new View(getContext());
underLine.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpTpPx(1.0f)));
underLine.setBackgroundColor(underlineColor);
addView(underLine, 1);
//初始化containerView并将其添加到DropDownMenu
containerView = new FrameLayout(context);
containerView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
addView(containerView, 2);
}
}
- 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
- 76
- 77
- 78
- 79
- 80
- 81
- 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
- 76
- 77
- 78
- 79
- 80
- 81
注意下面的成员变量
private LinearLayout tabMenuView;顶部菜单布局
private FrameLayout containerView;//底部容器,包含popupMenuViews,maskView(暗色背景)
private FrameLayout popupMenuViews;//弹出菜单父布局
private View maskView;//遮罩半透明View,点击可关闭DropDownMenu
public class DropDownMenu extends LinearLayout {}
也就说整体的布局是继承自LinearLayout的
setOrientation(VERTICAL);//在第三个构造方法里指定了整体布局的方向
完成了以上步骤,其实大体上的布局差不多都搭好了,下面就是填充子布局和数据的问题了
/**
* 初始化DropDownMenu
*
* @param tabTexts
* @param popupViews
* @param contentView
*/
public void setDropDownMenu(@NonNull List<String> tabTexts, @NonNull List<View> popupViews, @NonNull View contentView) {
if (tabTexts.size() != popupViews.size()) {
throw new IllegalArgumentException("params not match, tabTexts.size() should be equal popupViews.size()");
}
for (int i = 0; i < tabTexts.size(); i++) {
addTab(tabTexts, i);
}
containerView.addView(contentView, 0);
maskView = new View(getContext());
maskView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
maskView.setBackgroundColor(maskColor);
maskView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
closeMenu();
}
});
containerView.addView(maskView, 1);
maskView.setVisibility(GONE);
popupMenuViews = new FrameLayout(getContext());
popupMenuViews.setVisibility(GONE);
containerView.addView(popupMenuViews, 2);
for (int i = 0; i < popupViews.size(); i++) {
popupViews.get(i).setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
popupMenuViews.addView(popupViews.get(i), i);
}
}
- 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
- 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
这一步是对外暴露的方法,专门给菜单栏,填充数据,还有为内容区填充布局的,布局可以填充任何布局,很灵活
addTab(tabTexts, i);这个方法,下面会给出代码,用来给菜单栏添加布局和菜单栏的分割线的
containerView.addView(contentView, 0);//将传入的内容布局添加到内容区(默认是显示的)
containerView.addView(maskView, 1);//添加暗色背景(默认是GONG的)
containerView.addView(popupMenuViews, 2);//将点击菜单栏后显示的布局,注意,这个布局里也有子布局,那就是listview,所以现在是讲父布局添加到内容区的(默认是GONG的)
popupMenuViews.addView(popupViews.get(i), i);//这一步才是为点击菜单栏后显示的布局填充子布局的,也就是listview
private void addTab(@NonNull List<String> tabTexts, int i) {
final TextView tab = new TextView(getContext());
tab.setSingleLine();
tab.setEllipsize(TextUtils.TruncateAt.END);
tab.setGravity(Gravity.CENTER);
tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, menuTextSize);
tab.setLayoutParams(new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f));
tab.setTextColor(textUnselectedColor);
tab.setCompoundDrawablesWithIntrinsicBounds(null, null, null, getResources().getDrawable(menuUnselectedIcon));
tab.setText(tabTexts.get(i));
tab.setPadding(dpTpPx(5), dpTpPx(12), dpTpPx(5), dpTpPx(12));
//添加点击事件
tab.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
switchMenu(tab);
}
});
tabMenuView.addView(tab);
//添加分割线
if (i < tabTexts.size() - 1) {
View view = new View(getContext());
LayoutParams layoutParams = new LayoutParams(dpTpPx(0.5f), dpTpPx(25));
layoutParams.gravity = Gravity.CENTER_VERTICAL;
view.setLayoutParams(layoutParams);
view.setBackgroundColor(dividerColor);
tabMenuView.addView(view);
}
}
- 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
- 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
这一步是为菜单栏添加布局,和分割线的,还有为菜单栏添加点击事件,没有什么可说的
/**
* 切换菜单
*
* @param view
*/
private void switchMenu(View view) {
for (int i = 0; i < tabMenuView.getChildCount(); i = i + 2) {
if (view == tabMenuView.getChildAt(i)) {
if (current_tab_position == i) {
closeMenu();
} else {
if (current_tab_position == -1) {
popupMenuViews.setVisibility(View.VISIBLE);
popupMenuViews.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_menu_in));
maskView.setVisibility(VISIBLE);
maskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.dd_mask_in));
popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE);
} else {
popupMenuViews.getChildAt(i / 2).setVisibility(View.VISIBLE);
}
current_tab_position = i;
((TextView) tabMenuView.getChildAt(i)).setTextColor(textSelectedColor);
((TextView) tabMenuView.getChildAt(i)).setCompoundDrawablesWithIntrinsicBounds(null, null,
null, getResources().getDrawable(menuSelectedIcon));
}
} else {
//设置其他未点击tab的状态
((TextView) tabMenuView.getChildAt(i)).setTextColor(textUnselectedColor);
((TextView) tabMenuView.getChildAt(i)).setCompoundDrawablesWithIntrinsicBounds(null, null,
null, getResources().getDrawable(menuUnselectedIcon));
popupMenuViews.getChildAt(i / 2).setVisibility(View.GONE);
}
}
}
- 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
- 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
这一步是切换菜单时要做的,只是改变其他状态而已
用法
做到这,也就意味着,自定义控件写完了,下面就是如何用他了
布局
<example.fussen.dropdownmenu.DropDownMenu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dropDownMenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:dividerColor="@color/blue_app"
app:maskColor="@color/mask_color"
app:menuBackgroundColor="@color/base_title_color"
app:menuSelectedIcon="@drawable/ic_img_up"
app:menuTextSize="16sp"
app:menuUnselectedIcon="@drawable/ic_img_down"
app:textSelectedColor="@color/blue_app"
app:textUnselectedColor="@color/drop_down_unselected"
app:underlineColor="@color/blue_app" />
将上面的布局copy到你的布局里就ok啦
自定义的属性
<declare-styleable name="DropDownMenu">
<!--下划线颜色-->
<attr name="underlineColor" format="color" />
<!--分割线颜色(菜单栏)-->
<attr name="dividerColor" format="color" />
<!--tab选中颜色-->
<attr name="textSelectedColor" format="color" />
<!--tab未选中颜色-->
<attr name="textUnselectedColor" format="color" />
<!--tab 背景颜色-->
<attr name="menuBackgroundColor" format="color" />
<!--遮罩颜色,一般是半透明-->
<attr name="maskColor" format="color" />
<!--字体大小-->
<attr name="menuTextSize" format="dimension" />
<!--tab选中状态图标-->
<attr name="menuSelectedIcon" format="reference" />
<!--tab未选中状态图标-->
<attr name="menuUnselectedIcon" format="reference" />
</declare-styleable>
结束
- ok,做到这,就大功告成了,具体的代码还是以我的Demo为主
- 如果需要源码,关注微信公共号:AppCode,回复关键字“筛选菜单”,即可拿到源码下载的链接
apk下载
如果这篇文章对你有用,欢迎关注我们的微信公共号:AppCode
扫描下面二维码即可关注