├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── lcodecore
│ │ └── labellayout
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── lcodecore
│ │ │ └── labellayout
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── lcodecore
│ └── labellayout
│ └── ExampleUnitTest.java
├── art
├── image0.png
├── image1.jpg
└── image2.jpg
├── build.gradle
├── labellibrary
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── lcodecore
│ │ ├── ILabel.java
│ │ └── LabelLayout.java
│ └── res
│ ├── drawable
│ ├── label_gray_selector.xml
│ └── label_selector.xml
│ ├── layout
│ └── view_label_common.xml
│ └── values
│ ├── attrs.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | /gradle
6 | /gradle.properties
7 | /gradlew
8 | /gradlew.bat
9 | .DS_Store
10 | /build
11 | /captures
12 | .externalNativeBuild
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 最简洁的标签(label/tag)选择/展示控件----LabelLayout
2 | LabelLayout只是用于标签布局(layout),搭配CheckBox使用,标签样式可以随意更换,完全没有束缚,小而精悍。(支持API 9 及以上)
3 |
4 | ### Demo图
5 |   
6 |
7 | ### 属性
8 | - label_horizontalSpacing 水平方向上标签之间的距离
9 | - label_verticalSpacing 竖直方向上标签之间的距离
10 | - label_checkboxLayout 传入CheckBox布局(也就是标签的样式)
11 | - label_enableDivider 是否需要在标签行间加分割线,默认为**false**
12 | - label_dividerHeight 行间分割线的高度
13 | - label_dividerColor 行间分割线的颜色
14 |
15 | ### 使用
16 | #### 添加gradle依赖
17 | ```
18 | compile 'com.lcodecorex:labellayout:1.0.1'
19 | ```
20 |
21 | #### 方法及回调
22 | 1. 定义标签数据Bean实现ILabel接口
23 | ```java
24 | public interface ILabel {
25 | //warning! Ids should be unique!!!
26 | public String getId();
27 | public String getName();
28 | }
29 | ```
30 | 注意getId()放回的id值必须是独特的,不可重复。
31 |
32 | 2. layout布局
33 | ```xml
34 |
44 | ```
45 |
46 | 2. 设置标签,使用setLabels()方法
47 | ```java
48 | labelLayout.setLabels(List labels);
49 | ```
50 |
51 | 3. 添加标签选择监听器
52 | ```java
53 | label_me.setOnCheckChangedListener(new LabelLayout.OnCheckListener() {
54 | @Override
55 | public void onCheckChanged(ILabel label,boolean isChecked) {}
56 |
57 | @Override
58 | public void onBeyondMaxCheckCount() {
59 |
60 | }
61 | });
62 | ```
63 |
64 | 4. 如果是需要选择标签,可以调用setMaxCheckCount(int)方法,选中的标签超过这个数checkListener的onBeyondMaxCheckCount()方法会被回调。
65 |
66 | 6. 其它方法
67 | - getCheckedLabelsCount 获取当前被选中的标签数量
68 | - getCheckedLabelIds() 获取被选中的标签的id列表
69 | - getCheckedIdsAsJson() 获取json序列化之后的id的json串
70 |
71 | #### 个性化(可参考默认样式:view_label_common.xml)
72 | --- 主布局
73 | ```xml
74 |
85 | ```
86 |
87 | --- checkbox_gray.xml
88 | ```xml
89 |
90 |
102 | ```
103 |
104 | --- 图一中用的CheckBox样式
105 | ```xml
106 |
107 |
115 | ```
116 |
117 | > ps
118 | > Contact me:lcodecore@163.com
119 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.1"
6 | defaultConfig {
7 | applicationId "com.lcodecore.labellayout"
8 | minSdkVersion 14
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | lintOptions {
21 | abortOnError false
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
28 | exclude group: 'com.android.support', module: 'support-annotations'
29 | })
30 | compile 'com.android.support:appcompat-v7:25.2.0'
31 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
32 | testCompile 'junit:junit:4.12'
33 | }
34 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\lcodecore\Applications\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/lcodecore/labellayout/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.lcodecore.labellayout;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.lcodecore.labellayout", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/lcodecore/labellayout/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.lcodecore.labellayout;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends AppCompatActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LabelLayout
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/lcodecore/labellayout/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.lcodecore.labellayout;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/art/image0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/art/image0.png
--------------------------------------------------------------------------------
/art/image1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/art/image1.jpg
--------------------------------------------------------------------------------
/art/image2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lcodecorex/LabelLayout/835120003b0973415dbb56b68da9add9624a595c/art/image2.jpg
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.0'
9 | classpath 'com.novoda:bintray-release:0.3.4'
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | tasks.withType(Javadoc) {
20 | options.addStringOption('Xdoclint:none', '-quiet')
21 | options.addStringOption('encoding', 'UTF-8')
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/labellibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/labellibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release'
3 |
4 | publish {
5 | userOrg = 'lcodecorex'
6 | groupId = 'com.lcodecorex'
7 | artifactId = 'labellayout'
8 | publishVersion = '1.0.1'
9 | website = 'https://github.com/lcodecorex/LabelLayout'
10 | }
11 |
12 | android {
13 | compileSdkVersion 25
14 | buildToolsVersion "25.0.1"
15 |
16 | defaultConfig {
17 | minSdkVersion 9
18 | targetSdkVersion 25
19 | versionCode 1
20 | versionName "1.0"
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | lintOptions {
29 | abortOnError false
30 | }
31 | }
32 |
33 | dependencies {
34 | compile fileTree(dir: 'libs', include: ['*.jar'])
35 | }
36 |
--------------------------------------------------------------------------------
/labellibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\lcodecore\Applications\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/labellibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/labellibrary/src/main/java/com/lcodecore/ILabel.java:
--------------------------------------------------------------------------------
1 | package com.lcodecore;
2 |
3 | public interface ILabel {
4 | //warning! Ids should be unique!!!
5 | public String getId();
6 | public String getName();
7 | }
--------------------------------------------------------------------------------
/labellibrary/src/main/java/com/lcodecore/LabelLayout.java:
--------------------------------------------------------------------------------
1 | package com.lcodecore;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.CheckBox;
12 | import android.widget.CompoundButton;
13 |
14 | import com.lcodecore.labellibrary.R;
15 |
16 | import org.json.JSONArray;
17 |
18 | import java.util.ArrayList;
19 | import java.util.HashMap;
20 | import java.util.HashSet;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.Set;
24 |
25 | /**
26 | * A LabelLayout ViewGroup that can hold labels with the help of CheckBox.
27 | * 配合CheckBox使用,可设置允许的最大标签选择数量
28 | */
29 | public class LabelLayout extends ViewGroup {
30 |
31 | private int mMaxCheckCount = Integer.MAX_VALUE;
32 | /**
33 | * 竖直方向间距, default is 8.0dp.
34 | */
35 | private int horizontalSpacing;
36 |
37 | /**
38 | * 水平方向间距, default is 4.0dp.
39 | */
40 | private int verticalSpacing;
41 |
42 | //whether or not to draw the divider between labels at horizon.
43 | private boolean enableDivider = false; //是否允许显示分割线 默认不显示
44 | private int dividerColor = 0xffECECEC;
45 | private float dividerHeight;
46 |
47 | private int checkboxLayoutId;
48 |
49 | //nark checked labels.
50 | private Map labelcheckMap;
51 | //mark the first position in a row.
52 | private Set rowPositons = new HashSet<>();
53 |
54 | //The paint to draw the divider.
55 | Paint dividerPaint;
56 |
57 | public LabelLayout(Context context) {
58 | this(context, null);
59 | }
60 |
61 | public LabelLayout(Context context, AttributeSet attrs) {
62 | this(context, attrs, 0);
63 | }
64 |
65 | public LabelLayout(Context context, AttributeSet attrs, int defStyleAttr) {
66 | super(context, attrs, defStyleAttr);
67 | setWillNotDraw(false);
68 |
69 | labelcheckMap = new HashMap<>();
70 |
71 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabelLayout, defStyleAttr, R.style.LabelLayoutDefault);
72 | try {
73 | horizontalSpacing = (int) a.getDimension(R.styleable.LabelLayout_label_horizontalSpacing, dp2px(8.0f));
74 | verticalSpacing = (int) a.getDimension(R.styleable.LabelLayout_label_verticalSpacing, dp2px(4.0f));
75 |
76 | checkboxLayoutId = a.getResourceId(R.styleable.LabelLayout_label_checkboxLayout, R.layout.view_label_common);
77 | enableDivider = a.getBoolean(R.styleable.LabelLayout_label_enableDivider, false);
78 | dividerHeight = a.getDimension(R.styleable.LabelLayout_label_dividerHeight, dp2px(2));
79 | dividerColor = a.getColor(R.styleable.LabelLayout_label_dividerColor, 0xffECECEC);
80 | } finally {
81 | a.recycle();
82 | }
83 | }
84 |
85 | @Override
86 | protected void onDraw(Canvas canvas) {
87 | super.onDraw(canvas);
88 | if (enableDivider) {
89 | if (dividerPaint == null) {
90 | dividerPaint = new Paint();
91 | dividerPaint.setAntiAlias(true);
92 | dividerPaint.setColor(dividerColor);
93 | dividerPaint.setStyle(Paint.Style.FILL);
94 | }
95 |
96 | for (Integer top : rowPositons) {
97 | if (top != 0) {
98 | //draw lines between labels.
99 | canvas.drawRect(0, top - dividerHeight / 2, getMeasuredWidth(), top + dividerHeight / 2, dividerPaint);
100 | }
101 | }
102 | }
103 | }
104 |
105 | @Override
106 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
107 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
108 | final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
109 | final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
110 | final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
111 |
112 | measureChildren(widthMeasureSpec, heightMeasureSpec);
113 |
114 | int width = 0;
115 | int height = 0;
116 |
117 | int row = 0; // The row counter.
118 | int rowWidth = 0; // Calc the current row width.
119 | int rowMaxHeight = 0; // Calc the max tag height, in current row.
120 |
121 | final int count = getChildCount();
122 | for (int i = 0; i < count; i++) {
123 | final View child = getChildAt(i);
124 | final int childWidth = child.getMeasuredWidth();
125 | final int childHeight = child.getMeasuredHeight();
126 |
127 | if (child.getVisibility() != GONE) {
128 | rowWidth += childWidth;
129 | if (rowWidth > widthSize) { // Next line.
130 | rowWidth = childWidth; // The next row width.
131 | height += rowMaxHeight + verticalSpacing;
132 | rowMaxHeight = childHeight; // The next row max height.
133 | row++;
134 | } else { // This line.
135 | rowMaxHeight = Math.max(rowMaxHeight, childHeight);
136 | }
137 | rowWidth += horizontalSpacing;
138 | }
139 | // System.out.println("measured height:" + height);
140 | rowPositons.add(height - verticalSpacing / 2);
141 | }
142 | // Account for the last row height.
143 | height += rowMaxHeight;
144 |
145 | // Account for the padding too.
146 | height += getPaddingTop() + getPaddingBottom();
147 |
148 | // If the tags grouped in one row, set the width to wrap the tags.
149 | if (row == 0) {
150 | width = rowWidth;
151 | width += getPaddingLeft() + getPaddingRight();
152 | } else {// If the tags grouped exceed one line, set the width to match the parent.
153 | width = widthSize;
154 | }
155 |
156 | setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : width,
157 | heightMode == MeasureSpec.EXACTLY ? heightSize : height);
158 | }
159 |
160 | @Override
161 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
162 | final int parentLeft = getPaddingLeft();
163 | final int parentRight = r - l - getPaddingRight();
164 | final int parentTop = getPaddingTop();
165 | final int parentBottom = b - t - getPaddingBottom();
166 |
167 | int childLeft = parentLeft;
168 | int childTop = parentTop;
169 |
170 | int rowMaxHeight = 0;
171 |
172 | final int count = getChildCount();
173 | for (int i = 0; i < count; i++) {
174 | final View child = getChildAt(i);
175 | final int width = child.getMeasuredWidth();
176 | final int height = child.getMeasuredHeight();
177 |
178 | if (child.getVisibility() != GONE) {
179 | if (childLeft + width > parentRight) { // Next line
180 | childLeft = parentLeft;
181 | childTop += rowMaxHeight + verticalSpacing;
182 | rowMaxHeight = height;
183 | } else {
184 | rowMaxHeight = Math.max(rowMaxHeight, height);
185 | }
186 | child.layout(childLeft, childTop, childLeft + width, childTop + height);
187 |
188 | childLeft += width + horizontalSpacing;
189 | }
190 | }
191 | }
192 |
193 | /**
194 | * set the default labels that you wanna to add into LabelLayout.
195 | *
196 | * @param labels A collection contains objects that implement ILabel interface.
197 | */
198 | public void setLabels(List labels) {
199 | labelcheckMap.clear();
200 | removeAllViews();
201 | if (labels == null || labels.size() == 0) return;
202 | for (final ILabel label : labels) {
203 | final CheckBox tagView = (CheckBox) View.inflate(getContext(), checkboxLayoutId, null);
204 | tagView.setText(label.getName());
205 | addView(tagView);
206 | tagView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
207 | @Override
208 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
209 | if (isChecked) {//选中时添加到map
210 | if (labelcheckMap.size() > mMaxCheckCount - 1) {
211 | if (checkListener != null) {
212 | checkListener.onBeyondMaxCheckCount();
213 | }
214 | tagView.setChecked(false);
215 | } else {
216 | labelcheckMap.put(label.getId(), true);
217 | if (checkListener != null) {
218 | checkListener.onCheckChanged(label, true);
219 | }
220 | }
221 | } else {//否则及时清理map
222 | if (labelcheckMap.containsKey(label.getId())) {
223 | labelcheckMap.remove(label.getId());
224 | if (checkListener != null) {
225 | checkListener.onCheckChanged(label, false);
226 | }
227 | }
228 | }
229 | }
230 | });
231 | }
232 | }
233 |
234 | /**
235 | * set the maximum numbers of checked labels.
236 | */
237 | public void setMaxCheckCount(int count) {
238 | mMaxCheckCount = count;
239 | }
240 |
241 | /**
242 | * Get the current selected tag number.
243 | */
244 | public int getCheckedLabelsCount() {
245 | int count = 0;
246 | for (Map.Entry m : labelcheckMap.entrySet()) {
247 | if (m.getValue()) {
248 | count++;
249 | }
250 | }
251 | return count;
252 | }
253 |
254 | public List getCheckedLabelIds() {
255 | List chechedLabelIds = new ArrayList<>();
256 | for (Map.Entry m : labelcheckMap.entrySet()) {
257 | if (m.getValue()) {
258 | chechedLabelIds.add(m.getKey());
259 | }
260 | }
261 | return chechedLabelIds;
262 | }
263 |
264 | /**
265 | * To serialize checked-label ids as json, make benefit for use.
266 | *
267 | * @return json string
268 | */
269 | public String getCheckedIdsAsJson() {
270 | List chechedId = new ArrayList<>();
271 | for (Map.Entry m : labelcheckMap.entrySet()) {
272 | if (m.getValue()) {
273 | chechedId.add(m.getKey());
274 | }
275 | }
276 | return new JSONArray(chechedId).toString();
277 | }
278 |
279 |
280 | private float dp2px(float dp) {
281 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
282 | getResources().getDisplayMetrics());
283 | }
284 |
285 | private OnCheckChangeListener checkListener;
286 |
287 | public interface OnCheckChangeListener {
288 | void onCheckChanged(ILabel label, boolean isChecked);
289 |
290 | void onBeyondMaxCheckCount();
291 | }
292 |
293 | public void setOnCheckChangedListener(OnCheckChangeListener checkListener) {
294 | this.checkListener = checkListener;
295 | }
296 |
297 | }
298 |
--------------------------------------------------------------------------------
/labellibrary/src/main/res/drawable/label_gray_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/labellibrary/src/main/res/drawable/label_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/labellibrary/src/main/res/layout/view_label_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/labellibrary/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/labellibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LabelLibrary
3 |
4 |
--------------------------------------------------------------------------------
/labellibrary/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':labellibrary'
2 |
--------------------------------------------------------------------------------