版权声明:本文为博主原创文章,未经博主允许不得转载。

无论是在移动端的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

onMeasure和onLayout的实现代码基本是一样的,不同的只是一个是测量宽高,一个是摆放位置而已。实现起来非常的简单。 
控件的使用就更加的简单了,只需要三步: 
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

2、设置标签:

    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

3、设置点击监听:(如果需要的话)

    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

最后给出该控件在GitHub中的地址,欢迎大家访问和使用。 
https://github.com/donkingliang/LabelsView

文章已同步到我的简书

Android自定义标签列表控件LabelsView解析的更多相关文章

  1. (转载)Android自定义标签列表控件LabelsView解析

    Android自定义标签列表控件LabelsView解析 作者 donkingliang 关注 2017.03.15 20:59* 字数 759 阅读 406评论 0喜欢 3 无论是在移动端的App, ...

  2. android 自定义空间 组合控件中 TextView 不支持drawableLeft属性

    android 自定义空间 组合控件中 TextView 不支持drawableLeft属性.会报错Caused by: android.view.InflateException: Binary X ...

  3. Android 自定义View修炼-如何打造Android自定义的下拉列表框控件

    一.概述 Android中的有个原生的下拉列表控件Spinner,但是这个控件有时候不符合我们自己的要求, 比如有时候我们需要类似windows 或者web网页中常见的那种下拉列表控件,类似下图这样的 ...

  4. Android自定义控件进阶-打造Android自定义的下拉列表框控件

    技术:Android+java   概述 Android中的有个原生的下拉列表控件Spinner,但是这个控件有时候不符合我们自己的要求, 比如有时候我们需要类似windows 或者web网页中常见的 ...

  5. Android 如何打造Android自定义的下拉列表框控件

    一.概述 Android中的有个原生的下拉列表控件Spinner,但是这个控件有时候不符合我们自己的要求, 比如有时候我们需要类似windows 或者web网页中常见的那种下拉列表控件,类似下图这样的 ...

  6. Android自定义View和控件之一-定制属于自己的UI

    照例,拿来主义.我的学习是基于下面的三篇blog.前两是基本的流程,第三篇里有比较细致的绘制相关的属性.第4篇介绍了如何减少布局层次来提高效率. 1. 教你搞定Android自定义View 2. 教你 ...

  7. [转]android 自定义圆形imageview控件

      android布局 首先,定义定义圆形Imageview类: import android.content.Context; import android.graphics.Bitmap; imp ...

  8. Android自定义“图片+文字”控件四种实现方法之 二--------个人最推荐的一种

    http://blog.csdn.net/yanzi1225627/article/details/8633872 第二种方法也要新建一个图片+文字的xml布局文件,然后写一个类继承自LinearLa ...

  9. Android自定义上拉控件SpringView

    Demo 先看一下SpringView的效果图: 1.拖动灰色部分可拖动下方视图,点击jump按钮可让下方视图自行滑动. 使用方法 布局文件: <com.zql.android.springvi ...

随机推荐

  1. 生产环境ssh登陆策略

    生产环境ssh登陆策略 备份:cp /etc/ssh/sshd_config{,.bak} vim /etc/ssh/sshd_config   17 #Port 22 #修改ssh连接端口 38 # ...

  2. mvc控制器

    控制器 using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.We ...

  3. 文件操作fopen

    这块所谓的"文件操作"就是把文件的内容读进程序,然后根据具体的格式进行解析,或者是显示,或者是修改. 也就是把一个文件加载到程序里面,然后对其进行修改. 文件操作就三步(固定的三步 ...

  4. mysql5.7设置默认编码

    1.通过 show variables like '%char%';查看MySQL字符集情况 mysql> show variables like '%char%';+------------- ...

  5. 蓝图Blueprint

    ---恢复内容开始--- 蓝图: flask模块化处理操作和URL的方式 作用: 将不同的功能模块化,实现模块化应用 构建大型应用 优化项目结构 使用步骤 创建蓝图对象 对蓝图对象进行操作(注册路由; ...

  6. MySQL索引的原理,B+树、聚集索引和二级索引的结构分析

    索引是一种用于快速查询行的数据结构,就像一本书的目录就是一个索引,如果想在一本书中找到某个主题,一般会先找到对应页码.在mysql中,存储引擎用类似的方法使用索引,先在索引中找到对应值,然后根据匹配的 ...

  7. ubuntu安装后问题

    ubuntu安装后桌面显示不正常,出现闪屏,或者是缺图标的问题多数是ubuntu的3D加速显示问题 解决方法,vmware workstations 中的菜单栏:虚拟机->设置->显示器- ...

  8. 踩坑学习python自动化测试第二天!

    class put_out(object): def Helloword(str): print(str) Hello,Inder,Pist ="", [],{} word= st ...

  9. admin 自定义字段颜色 并加以简单判断

    在model中class Books(models.Model): nid = models.AutoField(primary_key=True, ) title = models.CharFiel ...

  10. vue 中生成二维码之爬坑之路

    最近在做vue中项目,有个需求是在你提交信息后把后台返给你的链接生成二维码. 一共使用了两种生成二维码的方法 1.vue-qr 这个是在一进入页面直接生成二维码,具体介绍见文档:https://www ...