Android自定义标签列表控件LabelsView

Android自定义标签列表控件LabelsView

96 
作者 donkingliang 
2017.03.15 20:59* 字数 759 阅读 406评论 0

无论是在移动端的App,还是在前端的网页,我们经常会看到下面这种标签的列表效果:


标签列表

标签从左到右摆放,一行显示不下时自动换行。这样的效果用Android源生的控件很不好实现,所以往往需要我们自己去自定义控件。我在开发中就遇到过几次要实现这样的标签列表效果,所以就自己写了个控件,放到我的GitHub,方便以后使用。有兴趣的同学也欢迎访问我的GitHub、查看源码实现和使用该控件。下面我将为大家介绍该控件的具体实现和使用。
要实现这样一个标签列表其实并不难,列表中的item可以直接用TextView来实现,我们只需要关心列表控件的大小和标签的摆放就可以了。也就是说我们需要做的只要两件事:测量布局(onMeasure)和摆放标签(onLayout)。这是自定义ViewGroup的基本步骤,相信对自定义View有所了解的同学都不会陌生。下面我们就来看看具体的代码实现。
控件的测量:


 
 
  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. int count = getChildCount();
  4. int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
  5. int contentHeight = 0; //记录内容的高度
  6. int lineWidth = 0; //记录行的宽度
  7. int maxLineWidth = 0; //记录最宽的行宽
  8. int maxItemHeight = 0; //记录一行中item高度最大的高度
  9. boolean begin = true; //是否是行的开头
  10. //循环测量item并计算控件的内容宽高
  11. for (int i = 0; i < count; i++) {
  12. View view = getChildAt(i);
  13. measureChild(view, widthMeasureSpec, heightMeasureSpec);
  14. //当前行显示不下item时换行。
  15. if (maxWidth < lineWidth + view.getMeasuredWidth()) {
  16. contentHeight += mLineMargin;
  17. contentHeight += maxItemHeight;
  18. maxItemHeight = 0;
  19. maxLineWidth = Math.max(maxLineWidth, lineWidth);
  20. lineWidth = 0;
  21. begin = true;
  22. }
  23. maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
  24. if(!begin) {
  25. lineWidth += mWordMargin;
  26. }else {
  27. begin = false;
  28. }
  29. lineWidth += view.getMeasuredWidth();
  30. }
  31. contentHeight += maxItemHeight;
  32. maxLineWidth = Math.max(maxLineWidth, lineWidth);
  33. //测量控件的最终宽高
  34. setMeasuredDimension(measureWidth(widthMeasureSpec,maxLineWidth),
  35. measureHeight(heightMeasureSpec, contentHeight));
  36. }
  37. //测量控件的宽
  38. private int measureWidth(int measureSpec, int contentWidth) {
  39. int result = 0;
  40. int specMode = MeasureSpec.getMode(measureSpec);
  41. int specSize = MeasureSpec.getSize(measureSpec);
  42. if (specMode == MeasureSpec.EXACTLY) {
  43. result = specSize;
  44. } else {
  45. result = contentWidth + getPaddingLeft() + getPaddingRight();
  46. if (specMode == MeasureSpec.AT_MOST) {
  47. result = Math.min(result, specSize);
  48. }
  49. }
  50. //这一句是为了支持minWidth属性。
  51. result = Math.max(result, getSuggestedMinimumWidth());
  52. return result;
  53. }
  54. //测量控件的高
  55. private int measureHeight(int measureSpec, int contentHeight) {
  56. int result = 0;
  57. int specMode = MeasureSpec.getMode(measureSpec);
  58. int specSize = MeasureSpec.getSize(measureSpec);
  59. if (specMode == MeasureSpec.EXACTLY) {
  60. result = specSize;
  61. } else {
  62. result = contentHeight + getPaddingTop() + getPaddingBottom();
  63. if (specMode == MeasureSpec.AT_MOST) {
  64. result = Math.min(result, specSize);
  65. }
  66. }
  67. //这一句是为了支持minHeight属性。
  68. result = Math.max(result, getSuggestedMinimumHeight());
  69. return result;
  70. }

标签的摆放:


 
 
  1. @Override
  2. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  3. int x = getPaddingLeft();
  4. int y = getPaddingTop();
  5. int contentWidth = right - left;
  6. int maxItemHeight = 0;
  7. int count = getChildCount();
  8. //循环摆放item
  9. for (int i = 0; i < count; i++) {
  10. View view = getChildAt(i);
  11. //当前行显示不下item时换行。
  12. if (contentWidth < x + view.getMeasuredWidth() + getPaddingRight()) {
  13. x = getPaddingLeft();
  14. y += mLineMargin;
  15. y += maxItemHeight;
  16. maxItemHeight = 0;
  17. }
  18. view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight());
  19. x += view.getMeasuredWidth();
  20. x += mWordMargin;
  21. maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
  22. }
  23. }

onMeasure和onLayout的实现代码基本是一样的,不同的只是一个是测量宽高,一个是摆放位置而已。实现起来非常的简单。
以上是LabelsView的核心代码,LabelsView除了实现了item的测量和摆放以外,还提供了一系列的方法让使用者可以方便设置标签的样式(包括标签被选中的样式)和标签点击、选中的监听等。下面LabelsView的使用介绍。

1、引入依赖
在Project的build.gradle在添加以下代码


 
 
  1. allprojects {
  2. repositories {
  3. ...
  4. maven { url 'https://jitpack.io' }
  5. }
  6. }

在Module的build.gradle在添加以下代码


 
 
  1. dependencies {
  2. compile 'com.github.donkingliang:LabelsView:1.2.0'
  3. }

2、编写布局:


 
 
  1. <com.donkingliang.labels.LabelsView
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. android:id="@+id/labels"
  4. android:layout_width="match_parent"
  5. android:layout_height="wrap_content"
  6. app:labelBackground="@drawable/label_bg" //标签的背景
  7. app:labelTextColor="@drawable/label_text_color" //标签的字体颜色 可以是一个颜色值
  8. app:labelTextSize="14sp" //标签的字体大小
  9. app:labelTextPaddingBottom="5dp" //标签的上下左右边距
  10. app:labelTextPaddingLeft="10dp"
  11. app:labelTextPaddingRight="10dp"
  12. app:labelTextPaddingTop="5dp"
  13. app:lineMargin="10dp" //行与行的距离
  14. app:wordMargin="10dp" //标签与标签的距离
  15. app:selectType="SINGLE" //标签的选择类型 有单选、多选、不可选三种类型
  16. app:maxSelect="5" /> //标签的最大选择数量,只有多选的时候才有用,0为不限数量

这里有两个地方需要说明一下:
1)标签的正常样式和选中样式是通过drawable来实现的。比如下面两个drawable。


 
 
  1. <!-- 标签的背景 label_bg -->
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  3. <!-- 标签选中时的背景 -->
  4. <item android:state_selected="true">
  5. <shape>
  6. <stroke android:width="2dp" android:color="#fb435b" />
  7. <corners android:radius="8dp" />
  8. <solid android:color="@android:color/white" />
  9. </shape>
  10. </item>
  11. <!-- 标签的正常背景 -->
  12. <item>
  13. <shape>
  14. <stroke android:width="2dp" android:color="#656565" />
  15. <corners android:radius="8dp" />
  16. <solid android:color="@android:color/white" />
  17. </shape>
  18. </item>
  19. </selector>

 
 
  1. <!-- 标签的文字颜色 label_text_color -->
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  3. <!-- 标签选中时的文字颜色 -->
  4. <item android:color="#fb435b" android:state_selected="true" />
  5. <!-- 标签的正常文字颜色 -->
  6. <item android:color="#2d2b2b" />
  7. </selector>

TextView的textColor属性除了可以设置一个颜色值以外,也可以通过资源来设置的,这一点很多同学都不知道。
2)标签的选择类型有三种:
NONE :标签不可选中,也不响应选中事件监听,这是默认值。
SINGLE:单选。
MULTI:多选,可以通过设置maxSelect限定选择的最大数量,0为不限数量。maxSelect只有在多选的时候才有效。

3、设置标签:


 
 
  1. labelsView = (LabelsView) findViewById(labels);
  2. ArrayList<String> label = new ArrayList<>();
  3. label.add("Android");
  4. label.add("IOS");
  5. label.add("前端");
  6. label.add("后台");
  7. label.add("微信开发");
  8. label.add("游戏开发");
  9. label.add("Java");
  10. label.add("JavaScript");
  11. label.add("C++");
  12. label.add("PHP");
  13. label.add("Python");
  14. label.add("Swift");
  15. labelsView.setLabels(label); //直接设置一个字符串数组就可以了。

4、设置事件监听:(如果需要的话)


 
 
  1. //标签的点击监听
  2. labelsView.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {
  3. @Override
  4. public void onLabelClick(View label, String labelText, int position) {
  5. //label是被点击的标签,labelText是标签的文字,position是标签的位置。
  6. }
  7. });
  8. //标签的选中监听
  9. labelsView.setOnLabelSelectChangeListener(new LabelsView.OnLabelSelectChangeListener() {
  10. @Override
  11. public void onLabelSelectChange(View label, String labelText, boolean isSelect, int position) {
  12. //label是被点击的标签,labelText是标签的文字,isSelect是是否选中,position是标签的位置。
  13. }
  14. });

5、常用方法


 
 
  1. //设置选中标签。
  2. //positions是个可变类型,表示被选中的标签的位置。
  3. //比喻labelsView.setSelects(1,2,5);选中第1,3,5个标签。如果是单选的话,只有第一个参数有效。
  4. public void setSelects(int... positions);
  5. //获取选中的标签。返回的是一个Integer的数组,表示被选中的标签的下标。如果没有选中,数组的size等于0。
  6. public ArrayList<Integer> getSelectLabels();
  7. //取消所有选中的标签。
  8. public void clearAllSelect();
  9. //设置标签的选择类型,有NONE、SINGLE和MULTI三种类型。
  10. public void setSelectType(SelectType selectType);
  11. //设置最大的选择数量,只有selectType等于MULTI是有效。
  12. public void setMaxSelect(int maxSelect);
  13. //设置标签背景
  14. public void setLabelBackgroundResource(int resId);
  15. //设置标签的文字颜色
  16. public void setLabelTextColor(int color);
  17. public void setLabelTextColor(ColorStateList color);
  18. //设置标签的文字大小(单位是px)
  19. public void setLabelTextSize(float size);
  20. //设置标签内边距
  21. public void setLabelTextPadding(int left, int top, int right, int bottom);
  22. //设置行间隔
  23. public void setLineMargin(int margin);
  24. //设置标签的间隔
  25. public void setWordMargin(int margin);

所以的set方法都有对应的get方法,这里就不说了。

效果图:


效果图.gif

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

转载于:https://www.cnblogs.com/liupengfei005257/p/7448834.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值