时人不识凌云木,直待凌云始道高。这篇文章主要讲述Android自定义标签列表控件LabelsView解析相关的知识,希望能为你提供帮助。
版权声明:本文为博主原创文章,未经博主允许不得转载。
无论是在移动端的App,还是在前端的网页,我们经常会看到下面这种标签的列表效果:
文章图片
文章图片
标签从左到右摆放,一行显示不下时自动换行。这样的效果用Android源生的控件很不好实现,所以往往需要我们自己去自定义控件。我在开发中就遇到过几次要实现这样的标签列表效果,所以就自己写了个控件,放到我的GitHub,方便以后使用。有兴趣的同学也欢迎访问我的GitHub、查看源码实现和使用该控件。下面我将为大家介绍该控件的具体实现和使用。
要实现这样一个标签列表其实并不难,列表中的item可以直接用TextView来实现,我们只需要关心列表控件的大小和标签的摆放就可以了。也就是说我们需要做的只要两件事:测量布局(onMeasure)和摆放标签(onLayout)。这是自定义ViewGroup的基本步骤,相信对自定义View有所了解的同学都不会陌生。下面我们就来看看具体的代码实现。
控件的测量:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int count = getChildCount();
int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
int contentHeight = 0;
//记录内容的高度
int lineWidth = 0;
//记录行的宽度
int maxLineWidth = 0;
//记录最宽的行宽
int maxItemHeight = 0;
//记录一行中item高度最大的高度
boolean begin = true;
//是否是行的开头//循环测量item并计算控件的内容宽高
for (int i = 0;
i <
count;
i++) {
View view = getChildAt(i);
measureChild(view, widthMeasureSpec, heightMeasureSpec);
//当前行显示不下item时换行。
if (maxWidth <
lineWidth + view.getMeasuredWidth()) {
contentHeight += mLineMargin;
contentHeight += maxItemHeight;
maxItemHeight = 0;
maxLineWidth = Math.max(maxLineWidth, lineWidth);
lineWidth = 0;
begin = true;
}
maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
if(!begin) {
lineWidth += mWordMargin;
}else {
begin = false;
}
lineWidth += view.getMeasuredWidth();
}contentHeight += maxItemHeight;
maxLineWidth = Math.max(maxLineWidth, lineWidth);
//测量控件的最终宽高
setMeasuredDimension(measureWidth(widthMeasureSpec,maxLineWidth),
measureHeight(heightMeasureSpec, contentHeight));
}//测量控件的宽
private int measureWidth(int measureSpec, int contentWidth) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = contentWidth + getPaddingLeft() + getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
//这一句是为了支持minWidth属性。
result = Math.max(result, getSuggestedMinimumWidth());
return result;
}//测量控件的高
private int measureHeight(int measureSpec, int contentHeight) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = contentHeight + getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
//这一句是为了支持minHeight属性。
result = Math.max(result, getSuggestedMinimumHeight());
return result;
}
- 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
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {int x = getPaddingLeft();
int y = getPaddingTop();
int contentWidth = right - left;
int maxItemHeight = 0;
int count = getChildCount();
//循环摆放item
for (int i = 0;
i <
count;
i++) {
View view = getChildAt(i);
//当前行显示不下item时换行。
if (contentWidth <
x + view.getMeasuredWidth()) {
x = getPaddingLeft();
y += mLineMargin;
y += maxItemHeight;
maxItemHeight = 0;
}
view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight());
x += view.getMeasuredWidth();
x += mWordMargin;
maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
}
}
- 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
- 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
控件的使用就更加的简单了,只需要三步:
1、编写布局:
<
com.donkingliang.labels.LabelsView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/labels"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:labelBackground="@drawable/pink_frame_bg"//标签的背景
app:labelTextColor="#fb435b"//标签的字体颜色
app:labelTextSize="14sp"//标签的字体大小
app:labelTextPaddingBottom="5dp"//标签的上下左右边距
app:labelTextPaddingLeft="10dp"
app:labelTextPaddingRight="10dp"
app:labelTextPaddingTop="5dp"
app:lineMargin="10dp"//行与行的距离
app:wordMargin="10dp" />
//标签与标签的距离
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
ArrayList<
String>
list = new ArrayList<
>
();
list.add("Android");
list.add("ios");
list.add("前端");
list.add("后台");
list.add("微信开发");
list.add("java");
list.add("javascript");
list.add("C++");
list.add("php");
list.add("python");
labelsView.setLabels(list);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
labels.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {
@Override
public void onLabelClick(TextView label, int position) {
//label就是被点击的标签,position就是标签的位置。
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
文章图片
使用前不要忘了引入依赖:
allprojects {
repositories {
...
maven { url ‘https://jitpack.io‘ }
}
}dependencies {
compile ‘com.github.donkingliang:LabelsView:1.0.0‘
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
https://github.com/donkingliang/LabelsView
【Android自定义标签列表控件LabelsView解析】文章已同步到我的简书
推荐阅读
- 源码分析篇 - Android绘制流程measurelayoutdraw流程
- android中Path的使用
- Android四大组件——ContentProvider
- Android学习总结 ———— Handler 的使用
- Android键盘事件
- Android测试(从零开始2——local单元测试)
- 安卓开源项目周报0315
- Android Activity 和 ViewGroup中事件触发和传递机制
- android shape.xml 属性详解