一. 前言

完善之前的自定义View-XLBottomView

Github地址:XLNewBottomView

二. 问题概述

我们先看,与之前不同的地方,之前的子Item,我是想使用我之前写的自定义View:自定义View-XLItem/

但是由于我使用之后没有显示出来,就换成了代码里面的 MyItem类 作为代替。

在这篇文章中,我通过使用 XLNewItem类 来解决过去的问题。

在 XLItem类中,我是使用 xml文件 加载布局的,肯定在那一部分出现了问题。由于父视图肯定会在 onLayout方法中 让子视图调用其 layout 方法。那么我就在 子视图的 onLayout方法 中使用代码添加控件。

三. XLNewItem的内容

1. 自定义属性的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<declare-styleable name="XLNewItem">
<!--图标-->
<attr name="icon_id" format="reference"/>
<!--标题-->
<attr name="title" format="string"/>
<!--正常状态的颜色-->
<attr name="normal_color_item" format="color"/>
<!--选中状态的颜色-->
<attr name="select_color_item" format="color"/>
<!--字体大小-->
<attr name="text_size" format="integer"/>
<!--图片的大小-->
<attr name="icon_size" format="integer"/>
<!--图片和文本的间隔-->
<attr name="spacing" format="integer"/>
</declare-styleable>

2. 自定义的XLNewItem

由于文本居中不好设置,所以我通过自己 drawText 来绘制文本。

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
public class XLNewItem extends RelativeLayout {
//日志
public static final String TAG = XLNewItem.class.getSimpleName();

//资源
private int icon_id = R.drawable.ic_launcher_background;
//文本
private String title = "测试";
//绘制的画笔
private Paint paint;

//字体的大小
private int text_size = 14;

//图片的大小
private int icon_size = 20;

//图片和文本的间隔
private int spacing = 5;

//正常状态的颜色
private int normal_color = Color.BLACK;
private int select_color = Color.MAGENTA;

//记录自身的索引
private int item_index = 0;

//显示icon的视图
private ImageView icon_view;
//显示title的视图
private TextView title_view;

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

//绘制文本的素材
private TextResource textResource;

/**
* 构造方法:Java代码初始化
* @param context
*/
public XLNewItem(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;

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

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

//1.获得所有属性值的集合
if (attrs != null){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XLNewItem);
//2.解析属性
icon_id = typedArray.getResourceId(R.styleable.XLNewItem_icon_id,icon_id);
title = typedArray.getString(R.styleable.XLNewItem_title);
normal_color = typedArray.getColor(R.styleable.XLNewItem_normal_color_item,normal_color);
select_color = typedArray.getColor(R.styleable.XLNewItem_select_color_item,select_color);
text_size = typedArray.getInteger(R.styleable.XLNewItem_text_size,text_size);
icon_size = typedArray.getInteger(R.styleable.XLNewItem_icon_size, icon_size);
spacing = typedArray.getInteger(R.styleable.XLNewItem_spacing,spacing);

//3.释放资源
typedArray.recycle();
}

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

/**
* 初始化操作
*/
private void init() {
//setBackgroundColor(Color.RED);

//初始化画笔信息
paint = new Paint();
paint.setTextSize(PxUtil.spToPx(text_size,getContext()));
paint.setColor(normal_color);

//初始化绘制字体的信息
textResource = new TextResource();
}

/**
* 改变状态
* @param status
*/
public void changeStatus(int status){
//判断状态
if (status == STATUS_NORMAL){
icon_view.setColorFilter(normal_color);

paint.setColor(normal_color);
}else {
icon_view.setColorFilter(select_color);

paint.setColor(select_color);
}

//刷新界面
invalidate();
}

@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);

//计算文本的宽度,高度
int textWidth = (int) paint.measureText(title);
int textHeight = (int) (paint.getFontMetrics().bottom-paint.getFontMetrics().top);

//图片的大小
int icon_size = PxUtil.dpToPx(this.icon_size,getContext());
//图片的左右间隔
int padding_icon = (getWidth() - icon_size) / 2;
//图片和上边框的间距
int padding_top = PxUtil.dpToPx(this.spacing,getContext());
//图片和文本的间隔
int spacing = PxUtil.dpToPx(this.spacing,getContext());

//文本的左右间隔
int padding_text = (getWidth() - textWidth) / 2;

//如果布局发生改变
if (changed){
Log.d(TAG,"XLNewItem:+"+item_index+"开始layout了");

//创建控件
icon_view = new ImageView(getContext());
title_view = new TextView(getContext());
//设置数据
icon_view.setImageResource(icon_id);

//icon的位置
int icon_left = padding_icon;
int icon_top = padding_top;
int icon_right = icon_left + icon_size;
int icon_bottom = icon_top + icon_size;

//text的位置
int text_left = padding_text;
int text_top = icon_bottom + spacing;
int text_right = text_left + textWidth;
int text_bottom = text_top + textHeight;

//textResource
textResource.x = text_left;
int distance = (int) (textHeight / 2 - paint.getFontMetrics().bottom);
textResource.y = text_top + (getHeight()-text_top) / 2 + distance;
textResource.text = title;

//layout
icon_view.layout(icon_left,icon_top,icon_right,icon_bottom);
title_view.layout(text_left,text_top,text_right,text_bottom);

//添加到视图中
addView(icon_view);
addView(title_view);

//改为选中状态
changeStatus(STATUS_NORMAL);
}
}

/**
* 绘制文本所需要的条件
*/
private class TextResource {
int x;
int y;
String text;
}

@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);

//绘制文本
canvas.drawText(
textResource.text,
textResource.x,
textResource.y,
paint
);
}

//setter,getter方法
public int getItem_index() {
return item_index;
}

public int getIcon_id() {
return icon_id;
}

public String getTitle() {
return title;
}
}

四. XLNewBottomView

1. 自定义的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<declare-styleable name="XLNewBottomView">
<!--正常状态的颜色-->
<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>

2. XLNewBottomView

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
public class XLNewBottomView extends RelativeLayout {
//日志
public static final String TAG = XLNewBottomView.class.getSimpleName();
//正常状态的颜色
private int normal_color = Color.BLACK;
//选中状态的颜色
private int select_color = Color.MAGENTA;
//两端是否给间距
private boolean hasLeftOrRightSize = true;
//item的大小
private int item_size = 50;
//按钮选中状态被点击是否响应事件
private boolean isSelectClick = false;

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

//监听者
private XLBottomViewItemListener listener;

//数据源
private List<XLNewItem> items;

/**
* 构造方法 Java代码初始化
* @param context
*/
public XLNewBottomView(Context context, int normal_color, int select_color, boolean hasLeftOrRightSize, int item_size) {
super(context);

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

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

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

//解析
if (attrs != null){
//1.获得所有属性值的集合
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XLNewBottomView);
//2.解析属性
normal_color = typedArray.getColor(R.styleable.XLNewBottomView_normal_color,normal_color);
select_color = typedArray.getColor(R.styleable.XLNewBottomView_select_color,select_color);
hasLeftOrRightSize = typedArray.getBoolean(R.styleable.XLNewBottomView_hasLeftOrRightSize,hasLeftOrRightSize);
item_size = typedArray.getInteger(R.styleable.XLNewBottomView_item_size,item_size);
isSelectClick = typedArray.getBoolean(R.styleable.XLNewBottomView_isSelectClick,isSelectClick);
//3.释放资源
typedArray.recycle();
}

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

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

/**
* 子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) {
Log.d(TAG,"XLNewBottomView开始layout了");

//布局是否发生变化
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 获取对应的模型数据
XLNewItem item = items.get(i);

//2.2 创建视图
/*@SuppressLint("DrawAllocation") MyItem item_view = new MyItem(
getContext(),
item.icon_id,
item.title,
i,
normal_color,
select_color
);*/
@SuppressLint("DrawAllocation") XLNewItem item_view = new XLNewItem(
getContext(),
item.getIcon_id(),
item.getTitle(),
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(XLNewItem.STATUS_SELECT);

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

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

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

/**
* 定义回调接口
*/
public interface XLBottomViewItemListener{
void itemStatusDidChange(int index);
}

//setter方法
public void setItems(List<XLNewItem> items) {
this.items = items;
}

public void setXLBottomViewItemListener(XLBottomViewItemListener listener) {
this.listener = listener;
}

public void setSelectClick(boolean select) {
isSelectClick = select;
}
}

五. 具体的使用

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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.xlnewbottomview.XLNewBottomView
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"
app:isSelectClick="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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class MainActivity extends AppCompatActivity {

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

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

List<XLNewItem> items = new ArrayList<>();
XLNewItem item1 = new XLNewItem(
this,
R.drawable.contact,
"用户1",
0,
Color.BLACK,
Color.MAGENTA
);
XLNewItem item2 = new XLNewItem(
this,
R.drawable.contact,
"用户2",
0,
Color.BLACK,
Color.MAGENTA
);
XLNewItem item3 = new XLNewItem(
this,
R.drawable.contact,
"用户3",
0,
Color.BLACK,
Color.MAGENTA
);
XLNewItem item4 = new XLNewItem(
this,
R.drawable.contact,
"用户4",
0,
Color.BLACK,
Color.MAGENTA
);

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

bottomView.setItems(items);

bottomView.setXLBottomViewItemListener(new XLNewBottomView.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();
}
});
}
}