一. 前言

仿照系统的BottomNavigationView而做的效果。

Github地址:XLBottomView

二. 自定义View

1. 自定义用到的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//正常状态的颜色
private int normal_color = Color.BLACK;
//选中状态的颜色
private int select_color = Color.MAGENTA;
//两端是否给间距
private boolean hasLeftOrRightSize = true;
//item的大小
private int item_size = 50;
//item的布局
private int item_layout;
//按钮选中状态被点击是否响应事件
private boolean isSelectClick = false;

//上一个被选中的
private MyItem lastItem;

//监听者
private XLBottomViewItemListener listener;

//数据源
private List<BottomViewItem> items;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 内部类:管理数据模型
* 注意:外部操作内部类,内部类必须用static修饰
*/
public static class BottomViewItem {
//图片资源ID
private int icon_id;
//名称
private String title;

/**
* 构造方法
* @param icon_id
* @param title
*/
public BottomViewItem(int icon_id, String title) {
this.icon_id = icon_id;
this.title = title;
}
}

2. 在values文件夹下自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="XLBottomView">
<!--正常状态的颜色-->
<attr name="normal_color" format="color"/>
<!--选中状态的颜色-->
<attr name="select_color" format="color"/>
<!--两端是否给间距-->
<attr name="hasLeftOrRightSize" format="boolean"/>
<!--item的大小-->
<attr name="item_size" format="integer"/>
<!--item的布局-->
<attr name="item_layout" format="reference"/>
<!--按钮选中状态被点击是否响应事件-->
<attr name="isSelectClick" format="boolean"/>
</declare-styleable>
</resources>

3. 创建构造方法

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
/**
* 构造方法 Java代码初始化
* @param context
*/
public XLBottomView(Context context, int normal_color, int select_color, boolean hasLeftOrRightSize, int item_size, int item_layout) {
super(context);

this.normal_color = normal_color;
this.select_color = select_color;
this.hasLeftOrRightSize = hasLeftOrRightSize;
this.item_size = item_size;
this.item_layout = item_layout;

//初始化操作
init();
}

/**
* 构造方法 Xml代码初始化
* @param context
* @param attrs
*/
public XLBottomView(Context context, AttributeSet attrs) {
super(context, attrs);

//解析
if (attrs != null){
//1.获得所有属性值的集合
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XLBottomView);
//2.解析属性
normal_color = typedArray.getColor(R.styleable.XLBottomView_normal_color,normal_color);
select_color = typedArray.getColor(R.styleable.XLBottomView_select_color,select_color);
hasLeftOrRightSize = typedArray.getBoolean(R.styleable.XLBottomView_hasLeftOrRightSize,hasLeftOrRightSize);
item_size = typedArray.getInteger(R.styleable.XLBottomView_item_size,item_size);
isSelectClick = typedArray.getBoolean(R.styleable.XLBottomView_isSelectClick,isSelectClick);
//3.释放资源
typedArray.recycle();
}

//初始化操作
init();
}

/**
* 初始化操作
*/
private void init() {}

4. layout的过程中添加视图

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
82
83
84
85
86
87
88
/**
* 子View布局
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//布局是否发生变化
if (changed){
//判断是否有数据
if (items.size() != 0) {
Log.d(TAG,"数据来了");

//1.计算间距
int space_num = hasLeftOrRightSize ? items.size()+1 : items.size()-1;
int horizon_size = (getWidth() - items.size() * PxUtil.dpToPx(item_size,getContext())) / space_num;
int vertical_size = (getHeight()- PxUtil.dpToPx(item_size,getContext())) / 2;

//2.根据数据源依次创建视图
for (int i = 0; i < items.size(); i++) {
//2.1 获取对应的模型数据
BottomViewItem item = items.get(i);

//2.2 创建视图
@SuppressLint("DrawAllocation") MyItem item_view = new MyItem(
getContext(),
item.icon_id,
item.title,
i,
normal_color,
select_color
);

//2.3 设置参数
int left = hasLeftOrRightSize ?
horizon_size + (horizon_size + PxUtil.dpToPx(item_size,getContext())) * i
:
(horizon_size + PxUtil.dpToPx(item_size,getContext())) * i;
if (!hasLeftOrRightSize){
//左右两端不设置间距也留一点间距
if (i == 0) left += PxUtil.dpToPx(20,getContext());
if (i == items.size()-1) left -= PxUtil.dpToPx(20,getContext());
}
int top = vertical_size;
int right = left + PxUtil.dpToPx(item_size,getContext());
int bottom = top + PxUtil.dpToPx(item_size,getContext());
item_view.layout(left,top,right,bottom);
Log.d(TAG,"left:"+left+" top:"+top);

//2.4 添加视图
addView(item_view);

//2.5 默认选中第一个
if (i == 0) {
item_view.changeStatus(MyItem.STATUS_SELECT);

lastItem = item_view;
}else {
item_view.changeStatus(MyItem.STATUS_NORMAL);
}

//2.6 添加点击事件
item_view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
MyItem selectItem = (MyItem) v;

if (!lastItem.equals(selectItem)) {
//1.上一个还原
lastItem.changeStatus(MyItem.STATUS_NORMAL);
//2.当前点选中
selectItem.changeStatus(MyItem.STATUS_SELECT);
//3.记录当前的按钮
lastItem = selectItem;
//4.回调
if (listener != null){
listener.itemStatusDidChange(selectItem.getItem_index());
}
}
}
});
}
}
}
}

5. 子项类

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
82
83
84
85
/**
* 自定义View:子项
*/
public class MyItem extends androidx.appcompat.widget.AppCompatTextView {
//图片
private int icon_id;
//文本
private String title;
//索引
private int item_index;
//正常状态的颜色
private int normal_color;
//选中状态的颜色
private int select_color;

//正常状态
public static final int STATUS_NORMAL = 0;
//选中状态
public static final int STATUS_SELECT = 1;

/**
* 构造方法:Java代码初始化
* @param context
*/
public MyItem(Context context, int icon_id, String title, int item_index, int normal_color, int select_color) {
super(context);

//保存数据
this.icon_id = icon_id;
this.title = title;
this.item_index = item_index;
this.normal_color = normal_color;
this.select_color = select_color;
setBackground(null);

//初始化操作
init(STATUS_NORMAL);
}

/**
* 初始化操作
*/
private void init(int status) {
//setBackgroundColor(Color.MAGENTA);
//1.设置图标

//1.1获取图片,便于设置颜色滤镜
Drawable drawable = getResources().getDrawable(icon_id).mutate();
//1.2设置图片的尺寸
drawable.setBounds(0,PxUtil.dpToPx(5,getContext()),PxUtil.dpToPx(25,getContext()),PxUtil.dpToPx(25,getContext())+PxUtil.dpToPx(5,getContext()));
setCompoundDrawables(null,drawable,null,null);
setCompoundDrawablePadding(PxUtil.dpToPx(8,getContext()));

//2.设置文本
setText(title);
setTextSize(PxUtil.spToPx(5,getContext()));
setGravity(Gravity.CENTER);
setTextColor(normal_color);
}

/**
* 改变状态
*/
public void changeStatus(int status){
if (status == STATUS_SELECT){
//改变图片的颜色
getCompoundDrawables()[1].mutate().setColorFilter(select_color, PorterDuff.Mode.SRC_ATOP);

//改变文本的颜色
setTextColor(select_color);
}else if (status == STATUS_NORMAL){
//改变图片的颜色
getCompoundDrawables()[1].mutate().setColorFilter(normal_color, PorterDuff.Mode.SRC_ATOP);

//改变文本的颜色
setTextColor(normal_color);
}

}

//get方法
public int getItem_index() {
return item_index;
}
}

在这里,我们需要了解ColorFilter。

简单的理解可以参考这篇文章:ColorFilter的使用 以及 Android-使用 SetColorFilter 神奇地改变图片的颜色

但是,如果使用setColorFilter,还是有一个坑需要了解,参考这篇文章:Android-Drawable setColorFilter方法踩坑

三. 具体的使用

首先需要添加依赖,添加依赖之后,在布局文件中这样调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<swu.xl.bottomview.XLBottomView
android:id="@+id/bottom_view"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#999999"
app:normal_color="#000000"
app:select_color="#ff9900"
app:hasLeftOrRightSize="false"
app:item_size="50"
android:layout_alignParentBottom="true"
/>

</RelativeLayout>
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
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

XLBottomView bottomView = findViewById(R.id.bottom_view);

List<XLBottomView.BottomViewItem> items = new ArrayList<>();
XLBottomView.BottomViewItem item1 = new XLBottomView.BottomViewItem(
R.drawable.contact,
"用户1"
);
XLBottomView.BottomViewItem item2 = new XLBottomView.BottomViewItem(
R.drawable.contact,
"用户2"
);
XLBottomView.BottomViewItem item3 = new XLBottomView.BottomViewItem(
R.drawable.contact,
"用户3"
);
XLBottomView.BottomViewItem item4 = new XLBottomView.BottomViewItem(
R.drawable.contact,
"用户4"
);

items.add(item1);
items.add(item2);
items.add(item3);
items.add(item4);

bottomView.setItems(items);

bottomView.setXLBottomViewItemListener(new XLBottomView.XLBottomViewItemListener() {
@Override
public void itemStatusDidChange(int index) {
Toast toast = Toast.makeText(MainActivity.this, "第" + (index + 1) + "个按钮被点击", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER,0,0);
toast.show();
}
});
}
}