diff --git a/.gitignore b/.gitignore
index fbe08b1..6c9b4c3 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,46 +1,46 @@
-.gradle
-/local.properties
-/.idea/workspace.xml
-.DS_Store
-/build
-# built application files
-*.ap_
-
-# files for the dex VM
-*.dex
-
-# Java class files
-*.class
-.DS_Store
-
-# generated files
-bin/
-gen/
-Wiki/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Eclipse project files
-.classpath
-.project
-.settings/
-
-# Proguard folder generated by Eclipse
-proguard/
-
-#Android Studio
-build/
-src/androidTest/
-
-# Intellij project files
-*.iml
-*.ipr
-*.iws
-.idea/
-
-#gradle
-.gradle/
-.idea
-app/build
+.gradle
+/local.properties
+/.idea/workspace.xml
+.DS_Store
+/build
+# built application files
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+.DS_Store
+
+# generated files
+bin/
+gen/
+Wiki/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Eclipse project files
+.classpath
+.project
+.settings/
+
+# Proguard folder generated by Eclipse
+proguard/
+
+#Android Studio
+build/
+src/androidTest/
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#gradle
+.gradle/
+.idea
+app/build
app/src/androidTest
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 836dace..a3a152b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,26 +1,32 @@
-language: android
-android:
- components:
- # Uncomment the lines below if you want to
- # use the latest revision of Android SDK Tools
- # - platform-tools
- # - tools
-
- # The BuildTools version used by your project
- - build-tools-22.0.1
-
- # The SDK version used to compile your project
- - android-23
-
- # Additional components
- - extra-google-google_play_services
- - extra-google-m2repository
- - extra-android-m2repository
- - addon-google_apis-google-19
-
- # Specify at least one system image,
- # if you need to run emulator(s) during your tests
- - sys-img-armeabi-v7a-android-19
- - sys-img-x86-android-17
-
-script: ./gradlew assembleDebug
\ No newline at end of file
+language: android
+jdk: oraclejdk8
+
+env:
+ global:
+ - ANDROID_ABI=armeabi-v7a
+
+android:
+ components:
+ - tools
+ - android-22
+ - android-28
+ - sys-img-armeabi-v7a-android-22
+ - extra-android-m2repository
+ - extra-android-support
+ - extra
+ licenses:
+ - android-sdk-license-.+
+
+before_install:
+ - mkdir "$ANDROID_HOME/licenses" || true
+ - echo "d56f5187479451eabf01fb78af6dfcb131a6481e" > "$ANDROID_HOME/licenses/android-sdk-license"
+
+before_script:
+ - chmod +x gradlew
+ - echo no | android create avd --force -n test -t android-22 --abi $ANDROID_ABI
+ - emulator -avd test -no-window &
+ - android-wait-for-emulator
+ - adb shell input keyevent 82 &
+
+script:
+ - ./gradlew clean connectedAndroidTest
\ No newline at end of file
diff --git a/README.md b/README.md
index 5340bd6..6acc61b 100644
--- a/README.md
+++ b/README.md
@@ -1,170 +1,292 @@
-# AndroidTagView
-
-[](https://travis-ci.org/whilu/AndroidTagView) [](https://android-arsenal.com/details/1/2992)
-
-An Android TagView library. You can customize awesome TagView by using this library.
-
-## Screenshots
-
-
-
-## Usage
-
-### Step 1
-
-Add below dependency in your **build.gradle** file.
-
-```groovy
-dependencies {
- compile 'co.lujun:androidtagview:1.0.0'
-}
-```
-
-### Step 2
-
-Use the AndroidTagView in layout file, you can add customized attributes here.
-
-```xml
-
-```
-
-### Step 3
-
-Use TagView in your code.
-
-```java
-TagContainerLayout mTagContainerLayout = (TagContainerLayout) findViewById(R.id.tagcontainerLayout);
-mTagContainerLayout.setTags(List tags);
-```
-
-Now, you have successfully created some TagViews. The following will show some more useful features for you customize.
-
-## Attributes
-
-|name|format|description|
-|:---:|:---:|:---:|
-| vertical_interval | dimension | Vertical interval, default 5(dp)
-| horizontal_interval | dimension | Horizontal interval, default 5(dp)
-| container_border_width | dimension | TagContainerLayout border width(default 0.5dp)
-| container_border_radius | dimension | TagContainerLayout border radius(default 10.0dp)
-| container_border_color | color | TagContainerLayout border color(default #22FF0000)
-| container_background_color | color | TagContainerLayout background color(default #11FF0000)
-| container_enable_drag | boolean | Can drag TagView(default false)
-| container_drag_sensitivity | float | The sensitive of the ViewDragHelper(default 1.0f, normal)
-| tag_border_width | dimension | TagView Border width(default 0.5dp)
-| tag_corner_radius | dimension | TagView Border radius(default 15.0dp)
-| tag_horizontal_padding | dimension | Horizontal padding for TagView, include left and right padding(left and right padding are equal, default 20px)
-| tag_vertical_padding | dimension | Vertical padding for TagView, include top and bottom padding(top and bottom padding are equal, default 17px)
-| tag_text_size | dimension | TagView Text size(default 14sp)
-| tag_text_color | color | TagView text color(default #FF666666)
-| tag_border_color | color | TagView border color(default #88F44336)
-| tag_background_color | color | TagView background color(default #33F44336)
-| tag_max_length | integer | The max length for TagView(default max length 23)
-| tag_clickable | boolean | Whether TagView can clickable(default unclickable)
-| tag_theme | enum | The TagView [theme](#themes)
-
-**You can set these attributes in layout file, or use setters(each attribute has get and set method) to set them.**
-
-## Themes
-
-|theme|code|description
-|:---:|:---:|:---:|
-| none | ColorFactory.NONE | If you customize TagView with your way, set this theme
-| random | ColorFactory.RANDOM | Create each TagView using random color
-| pure_cyan | ColorFactory.PURE_CYAN | All TagView created by pure cyan color
-| pure_teal | ColorFactory.PURE_TEAL | All TagView created by pure teal color
-
-
-## Methods
-
-* Set a ```TagView.OnTagClickListener``` for TagView, for ```onTagClick``` and ```onTagLongClick``` callback
-```java
-mTagContainerLayout.setOnTagClickListener(new TagView.OnTagClickListener() {
-
- @Override
- public void onTagClick(int position, String text) {
- // ...
- }
-
- @Override
- public void onTagLongClick(final int position, String text) {
- // ...
- }
-});
-```
-* Use ```setTagMaxLength(int max)``` to set text max length for all TagView.
-```java
-mTagContainerLayout.setTagMaxLength(int max);
-```
-* Use ```getTagText(int position)``` to get TagView text at the specified location.
-```java
-String text = mTagContainerLayout.getTagText(int position);
-```
-* If you set the attribute ```container_enable_drag``` to ```true```, when drag the TagView you can get latest state by using ```getTagViewState()```. There are 4 state:```ViewDragHelper.STATE_IDLE```, ```ViewDragHelper.STATE_DRAGGING```, and ```ViewDragHelper.STATE_SETTLING```.
-```java
-int state = mTagContainerLayout.getTagViewState();
-```
-* Set the [theme](#themes). If you want to customize theme, remember set theme with ```ColorFactory.NONE``` first, then set other attributes.
-```java
-// Set library provides theme
-mTagContainerLayout.setTheme(ColorFactory.PURE_CYAN);
-```
-```java
-// Set customize theme
-mTagContainerLayout.setTheme(ColorFactory.NONE);
-mTagContainerLayout.setTagBackgroundColor(Color.TRANSPARENT);
-```
-
-**After set the attributes, set tags or add a tag.**
-
-* Use ```setTags()``` to set tags, require a parameter of type ```List``` or ```String[]```.
-```java
-mTagContainerLayout.setTags(List tags);
-```
-* Insert a TagView into ContainerLayout at the end.
-```java
-mTagContainerLayout.addTag(String text);
-```
-* Insert a TagView into ContainerLayout at the specified location, the TagView is inserted before the current element at the specified location.
-```java
-mTagContainerLayout.addTag(String text, int position);
-```
-* Remove TagView on particular position, require the position of the TagView
-```java
-mTagContainerLayout.removeTag(int position);
-```
-
-## Change logs
-###1.0.0
-- First release
-
-## Sample App
-[APK](/sample/sample-release.apk)
-
-## About
-If you have any questions, contact me: [lujun.byte#gmail.com](mailto:lujun.byte@gmail.com).
-
-## License
-
- Copyright 2015 lujun
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
\ No newline at end of file
+# AndroidTagView
+
+[](https://travis-ci.org/whilu/AndroidTagView) [](https://android-arsenal.com/details/1/2992)
+
+An Android TagView library. You can customize awesome TagView by using this library.
+
+## Screenshots
+
+
+
+## Usage
+
+### Step 1
+
+Add below dependency in your **build.gradle** file.
+
+```groovy
+dependencies {
+ implementation 'co.lujun:androidtagview:1.1.7'
+ implementation 'androidx.appcompat:appcompat:1.0.1'
+}
+```
+
+### Step 2
+
+Use the AndroidTagView in layout file, you can add customized attributes here.
+
+```xml
+
+```
+
+### Step 3
+
+Use TagView in your code.
+
+```java
+TagContainerLayout mTagContainerLayout = (TagContainerLayout) findViewById(R.id.tagcontainerLayout);
+mTagContainerLayout.setTags(List tags);
+```
+
+Now, you have successfully created some TagViews. The following will show some more useful features for you customize.
+
+## Attributes
+
+|name|format|description|
+|:---:|:---:|:---:|
+| vertical_interval | dimension | Vertical interval, default 5(dp)
+| horizontal_interval | dimension | Horizontal interval, default 5(dp)
+| container_border_width | dimension | TagContainerLayout border width(default 0.5dp)
+| container_border_radius | dimension | TagContainerLayout border radius(default 10.0dp)
+| container_border_color | color | TagContainerLayout border color(default #22FF0000)
+| container_background_color | color | TagContainerLayout background color(default #11FF0000)
+| container_enable_drag | boolean | Can drag TagView(default false)
+| container_drag_sensitivity | float | The sensitive of the ViewDragHelper(default 1.0f, normal)
+| container_gravity | enum | The TagContainerLayout [gravity](#gravity)
+| container_max_lines | integer | The max lines for TagContainerLayout(default 0, auto increase)
+| tag_border_width | dimension | TagView Border width(default 0.5dp)
+| tag_corner_radius | dimension | TagView Border radius(default 15.0dp)
+| tag_horizontal_padding | dimension | Horizontal padding for TagView, include left and right padding(left and right padding are equal, default 10dp)
+| tag_vertical_padding | dimension | Vertical padding for TagView, include top and bottom padding(top and bottom padding are equal, default 8dp)
+| tag_text_size | dimension | TagView Text size(default 14sp)
+| tag_bd_distance | dimension | The distance between baseline and descent(default 2.75dp)
+| tag_text_color | color | TagView text color(default #FF666666)
+| tag_border_color | color | TagView border color(default #88F44336)
+| tag_background_color | color | TagView background color(default #33F44336)
+| tag_max_length | integer | The max length for TagView(default max length 23)
+| tag_clickable | boolean | Whether TagView can clickable(default false)
+| tag_selectable | boolean | Whether TagView can be selectable(default false)
+| tag_theme | enum | The TagView [theme](#themes)
+| tag_text_direction | enum | The TagView text [direction](#directions)
+| tag_ripple_color | color | The ripple effect color(default #EEEEEE)
+| tag_ripple_alpha | integer | The ripple effect color alpha(the value may between 0 - 255, default 128)
+| tag_ripple_duration | integer | The ripple effect duration(In milliseconds, default 1000ms)
+| tag_enable_cross | boolean | Enable draw cross icon(default false)
+| tag_cross_width | dimension | The cross area width(your cross click area, default equal to the TagView's height)
+| tag_cross_color | color | The cross icon color(default Color.BLACK)
+| tag_cross_line_width | dimension | The cross line width(default 1dp)
+| tag_cross_area_padding | dimension | The padding of the cross area(default 10dp)
+| tag_support_letters_rlt | boolean | Whether to support 'letters show with RTL(eg: Android -> diordnA)' style(default false)
+| tag_background | reference | TagView background resource(default none background)
+
+**You can set these attributes in layout file, or use setters(each attribute has get and set method) to set them.**
+
+## Themes
+
+|theme|code|value|description
+|:---:|:---:|:---:|:---:|
+| none | ColorFactory.NONE | -1 | **If you customize TagView with your way, set this theme**
+| random | ColorFactory.RANDOM | 0 | Create each TagView using random color
+| pure_cyan | ColorFactory.PURE_CYAN | 1 | All TagView created by pure cyan color
+| pure_teal | ColorFactory.PURE_TEAL | 2 | All TagView created by pure teal color
+
+## Directions
+
+|direction|code|value|description
+|:---:|:---:|:---:|:---:|
+| ltr | View.TEXT_DIRECTION_LTR | 3 | Text direction is forced to LTR(default)
+| rtl | View.TEXT_DIRECTION_RTL | 4 | Text direction is forced to RTL
+
+## Gravity
+
+|gravity|code|value|description
+|:---:|:---:|:---:|:---:|
+| left | Gravity.LEFT | 3 | Push TagView to the left of TagContainerLayout(default)
+| center | Gravity.CENTER | 17 | Push TagView to the center of TagContainerLayout
+| right | Gravity.RIGHT | 5 | Push TagView to the right of TagContainerLayout
+
+## Methods
+
+* Set a ```TagView.OnTagClickListener``` for TagView, for ```onTagClick``` , ```onTagLongClick``` and ```onTagCrossClick``` callback
+```java
+mTagContainerLayout.setOnTagClickListener(new TagView.OnTagClickListener() {
+
+ @Override
+ public void onTagClick(int position, String text) {
+ // ...
+ }
+
+ @Override
+ public void onTagLongClick(final int position, String text) {
+ // ...
+ }
+
+ @Override
+ public void onSelectedTagDrag(int position, String text){
+ // ...
+ }
+
+ @Override
+ public void onTagCrossClick(int position) {
+ // ...
+ }
+});
+```
+* Use ```setTagMaxLength(int max)``` to set text max length for all TagView.
+```java
+mTagContainerLayout.setTagMaxLength(int max);
+```
+* Use ```getTagText(int position)``` to get TagView text at the specified location.
+```java
+String text = mTagContainerLayout.getTagText(int position);
+```
+* ```getTags()``` return a string list for all tags in TagContainerLayout.
+```java
+List list = mTagContainerLayout.getTags();
+```
+* If you set the attribute ```container_enable_drag``` to ```true```, when drag the TagView you can get latest state by using ```getTagViewState()```. There are 4 state:```ViewDragHelper.STATE_IDLE```, ```ViewDragHelper.STATE_DRAGGING```, and ```ViewDragHelper.STATE_SETTLING```.
+```java
+int state = mTagContainerLayout.getTagViewState();
+```
+* Set the [theme](#themes). If you want to customize theme, remember set theme with ```ColorFactory.NONE``` first, then set other attributes.
+```java
+// Set library provides theme
+mTagContainerLayout.setTheme(ColorFactory.PURE_CYAN);
+```
+```java
+// Set customize theme
+mTagContainerLayout.setTheme(ColorFactory.NONE);
+mTagContainerLayout.setTagBackgroundColor(Color.TRANSPARENT);
+```
+* Set the text [direction](#directions). The library support two direction ```View.TEXT_DIRECTION_LTR``` and ```View.TEXT_DIRECTION_RTL```.
+```java
+mTagContainerLayout.setTagTextDirection(View.TEXT_DIRECTION_RTL);
+```
+* Use ```setTagTypeface(Typeface typeface)``` to set TagView text typeface.
+```java
+Typeface typeface = Typeface.createFromAsset(getAssets(), "iran_sans.ttf");
+mTagContainerLayout.setTagTypeface(typeface);
+```
+
+**After set the attributes, set tags or add a tag.**
+
+* Use ```setTags()``` to set tags, require a parameter of type ```List``` or ```String[]```.
+```java
+mTagContainerLayout.setTags(List tags);
+```
+* Insert a TagView into ContainerLayout at the end.
+```java
+mTagContainerLayout.addTag(String text);
+```
+* Insert a TagView into ContainerLayout at the specified location, the TagView is inserted before the current element at the specified location.
+```java
+mTagContainerLayout.addTag(String text, int position);
+```
+* Remove TagView on particular position, require the position of the TagView.
+```java
+mTagContainerLayout.removeTag(int position);
+```
+* Remove all TagViews.
+```java
+mTagContainerLayout.removeAllTags();
+```
+* Get a TagView in specified position.
+```java
+mTagContainerLayout.getTagView(int position);
+```
+* Set color for each TagView.
+```java
+List colors = new ArrayList();
+//int[] color = {TagBackgroundColor, TabBorderColor, TagTextColor, TagSelectedBackgroundColor}
+int[] color1 = {Color.RED, Color.BLACK, Color.WHITE, Color.YELLOW};
+int[] color2 = {Color.BLUE, Color.BLACK, Color.WHITE, Color.YELLOW};
+colors.add(color1);
+colors.add(color2);
+mTagcontainerLayout.setTags(tags, colors);
+```
+
+## Change logs
+
+### 1.1.7(2019-01-21)
+- Fix bugs
+
+### 1.1.6(2018-12-1)
+- Support tag selectable
+
+### 1.1.5(2018-8-20)
+- Allow images on tags (in LTR languages).
+
+### 1.1.4(2017-6-1)
+- Add attribute for TagView background.
+
+### 1.1.3(2017-5-17)
+- Add ```getTagView(int position)``` method to get TagView in specified position.
+
+### 1.1.2(2017-5-16)
+- Fix bugs
+
+### 1.1.1(2017-4-16)
+- Customize the color of the TagView, see [#51](https://github.com/whilu/AndroidTagView/pull/51)
+- Fixed issue [#50](https://github.com/whilu/AndroidTagView/issues/50), [#49](https://github.com/whilu/AndroidTagView/issues/49)
+
+### 1.1.0(2017-3-5)
+- Fixed issue [#45](https://github.com/whilu/AndroidTagView/issues/45)
+- Support 'letters show with RTL(eg: Android -> diordnA)' style
+
+### 1.0.6(2017-2-14)
+- Fix bugs
+
+### 1.0.5(2016-11-9)
+- Add cross view for TagView
+
+### 1.0.4(2016-10-30)
+- Support ripple effect(Call requires API level 11), like [Android CustomButton](https://github.com/whilu/AndroidSample/tree/master/CustomButton)
+- Fix bugs
+
+### 1.0.3(2016-4-3)
+- Add ```getTags()``` method to get the list for all tags
+- Fixed bugs in ListView/RecyclerView
+
+### 1.0.2(2016-1-18)
+- Support [gravity](#gravity) for ```TagContainerLayout```
+- Support set typeface
+
+### 1.0.1(2016-1-14)
+- Support text [direction](#directions)
+- Add ```removeAllTags()``` method for remove all TagViews
+- Fixed issue [#1](https://github.com/whilu/AndroidTagView/issues/1)
+- Fixed other bugs
+
+### 1.0.0(2016-1-6)
+- First release
+
+## Sample App
+[APK](/sample/sample-release.apk)
+
+## About
+If you have any questions, contact me: [lujun.byte#gmail.com](mailto:lujun.byte@gmail.com).
+
+## License
+
+ Copyright 2015 lujun
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/androidtagview/.gitignore b/androidtagview/.gitignore
index fbe08b1..6c9b4c3 100755
--- a/androidtagview/.gitignore
+++ b/androidtagview/.gitignore
@@ -1,46 +1,46 @@
-.gradle
-/local.properties
-/.idea/workspace.xml
-.DS_Store
-/build
-# built application files
-*.ap_
-
-# files for the dex VM
-*.dex
-
-# Java class files
-*.class
-.DS_Store
-
-# generated files
-bin/
-gen/
-Wiki/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Eclipse project files
-.classpath
-.project
-.settings/
-
-# Proguard folder generated by Eclipse
-proguard/
-
-#Android Studio
-build/
-src/androidTest/
-
-# Intellij project files
-*.iml
-*.ipr
-*.iws
-.idea/
-
-#gradle
-.gradle/
-.idea
-app/build
+.gradle
+/local.properties
+/.idea/workspace.xml
+.DS_Store
+/build
+# built application files
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+.DS_Store
+
+# generated files
+bin/
+gen/
+Wiki/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Eclipse project files
+.classpath
+.project
+.settings/
+
+# Proguard folder generated by Eclipse
+proguard/
+
+#Android Studio
+build/
+src/androidTest/
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#gradle
+.gradle/
+.idea
+app/build
app/src/androidTest
\ No newline at end of file
diff --git a/androidtagview/build.gradle b/androidtagview/build.gradle
index 9119f90..a04e40a 100644
--- a/androidtagview/build.gradle
+++ b/androidtagview/build.gradle
@@ -1,26 +1,25 @@
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion 23
- buildToolsVersion "22.0.1"
-
- defaultConfig {
- minSdkVersion 9
- targetSdkVersion 23
- versionCode 100
- versionName "1.0.0"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- testCompile 'junit:junit:4.12'
- compile 'com.android.support:appcompat-v7:23.1.1'
-}
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 28
+ versionCode 117
+ versionName "1.1.7"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ testImplementation 'junit:junit:4.12'
+ implementation 'androidx.appcompat:appcompat:1.0.1'
+}
apply from: 'https://raw.githubusercontent.com/whilu/AndroidPublishLibrary/master/project/library/bintray_publish.gradle'
\ No newline at end of file
diff --git a/androidtagview/proguard-rules.pro b/androidtagview/proguard-rules.pro
index 700c8df..6828981 100644
--- a/androidtagview/proguard-rules.pro
+++ b/androidtagview/proguard-rules.pro
@@ -1,17 +1,17 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in F:\code\android\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in F:\code\android\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/androidtagview/project.properties b/androidtagview/project.properties
new file mode 100644
index 0000000..1a8d96f
--- /dev/null
+++ b/androidtagview/project.properties
@@ -0,0 +1,10 @@
+#project
+project.name=AndroidTagView
+project.groupId=co.lujun
+project.artifactId=androidtagview
+project.packaging=aar
+project.siteUrl=https://github.com/whilu/AndroidTagView
+project.gitUrl=https://github.com/whilu/AndroidTagView.git
+
+#javadoc
+javadoc.name=AndroidTagView
\ No newline at end of file
diff --git a/androidtagview/src/main/AndroidManifest.xml b/androidtagview/src/main/AndroidManifest.xml
index 3cf689e..4b17c55 100644
--- a/androidtagview/src/main/AndroidManifest.xml
+++ b/androidtagview/src/main/AndroidManifest.xml
@@ -1,11 +1,8 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/androidtagview/src/main/java/co/lujun/androidtagview/ColorFactory.java b/androidtagview/src/main/java/co/lujun/androidtagview/ColorFactory.java
index 4052232..f7871b7 100644
--- a/androidtagview/src/main/java/co/lujun/androidtagview/ColorFactory.java
+++ b/androidtagview/src/main/java/co/lujun/androidtagview/ColorFactory.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2015 lujun
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package co.lujun.androidtagview;
import android.graphics.Color;
@@ -9,9 +25,9 @@
public class ColorFactory {
/**
- * ============= -->border color
- * background color<---||- Text --||-->text color
- * =============
+ * ============= --border color
+ * background color---||- Text --||--text color
+ * =============
*/
public static final String BG_COLOR_ALPHA = "33";
@@ -49,7 +65,8 @@ public static int[] onRandomBuild(){
int bgColor = Color.parseColor("#" + BG_COLOR_ALPHA + COLORS[random]);
int bdColor = Color.parseColor("#" + BD_COLOR_ALPHA + COLORS[random]);
int tColor = SHARP666666;
- return new int[]{bgColor, bdColor, tColor};
+ int tColor2 = SHARP727272;
+ return new int[]{bgColor, bdColor, tColor, tColor2};
}
public static int[] onPureBuild(PURE_COLOR type){
@@ -57,7 +74,8 @@ public static int[] onPureBuild(PURE_COLOR type){
int bgColor = Color.parseColor("#" + BG_COLOR_ALPHA + color);
int bdColor = Color.parseColor("#" + BD_COLOR_ALPHA + color);
int tColor = SHARP727272;
- return new int[]{bgColor, bdColor, tColor};
+ int tColor2 = SHARP666666;
+ return new int[]{bgColor, bdColor, tColor, tColor2};
}
}
diff --git a/androidtagview/src/main/java/co/lujun/androidtagview/TagContainerLayout.java b/androidtagview/src/main/java/co/lujun/androidtagview/TagContainerLayout.java
index ed874a8..e36dccf 100644
--- a/androidtagview/src/main/java/co/lujun/androidtagview/TagContainerLayout.java
+++ b/androidtagview/src/main/java/co/lujun/androidtagview/TagContainerLayout.java
@@ -1,832 +1,1611 @@
-package co.lujun.androidtagview;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.support.v4.widget.ViewDragHelper;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Author: lujun(http://blog.lujun.co)
- * Date: 2015-12-30 17:14
- */
-public class TagContainerLayout extends ViewGroup {
-
- /** Vertical interval, default 5(dp)*/
- private int mVerticalInterval;
-
- /** Horizontal interval, default 5(dp)*/
- private int mHorizontalInterval;
-
- /** TagContainerLayout border width(default 0.5dp)*/
- private float mBorderWidth = 0.5f;
-
- /** TagContainerLayout border radius(default 10.0dp)*/
- private float mBorderRadius = 10.0f;
-
- /** The sensitive of the ViewDragHelper(default 1.0f, normal)*/
- private float mSensitivity = 1.0f;
-
- /** Tagview average height*/
- private int mChildHeight;
-
- /** TagContainerLayout border color(default #22FF0000)*/
- private int mBorderColor = Color.parseColor("#22FF0000");
-
- /** TagContainerLayout background color(default #11FF0000)*/
- private int mBackgroundColor = Color.parseColor("#11FF0000");
-
- /** The max length for TagView(default max length 23)*/
- private int mTagMaxLength = 23;
-
- /** TagView Border width(default 0.5dp)*/
- private float mTagBorderWidth = 0.5f;
-
- /** TagView Border radius(default 15.0dp)*/
- private float mTagBorderRadius = 15.0f;
-
- /** TagView Text size(default 14sp)*/
- private float mTagTextSize = 14;
-
- /** Horizontal padding for TagView, include left & right padding(left & right padding are equal, default 20px)*/
- private int mTagHorizontalPadding = 20;
-
- /** Vertical padding for TagView, include top & bottom padding(top & bottom padding are equal, default 17px)*/
- private int mTagVerticalPadding = 17;
-
- /** TagView border color(default #88F44336)*/
- private int mTagBorderColor = Color.parseColor("#88F44336");
-
- /** TagView background color(default #33F44336)*/
- private int mTagBackgroundColor = Color.parseColor("#33F44336");
-
- /** TagView text color(default #FF666666)*/
- private int mTagTextColor = Color.parseColor("#FF666666");
-
- /** Whether TagView can clickable(default unclickable)*/
- private boolean isTagViewClickable;
-
- /** Tags*/
- private List mTags;
-
- /** Can drag TagView(default false)*/
- private boolean mDragEnable;
-
- /** TagView drag state(default STATE_IDLE)*/
- private int mTagViewState = ViewDragHelper.STATE_IDLE;
-
- /** OnTagClickListener for TagView*/
- private TagView.OnTagClickListener mOnTagClickListener;
-
- private Paint mPaint;
-
- private RectF mRectF;
-
- private ViewDragHelper mViewDragHelper;
-
- private List mChildViews;
-
- private int[] mViewPos;
-
- /** View theme(default PURE_CYAN)*/
- private int mTheme = 1;
-
- /** Default interval(dp)*/
- private static final float DEFAULT_INTERVAL = 5;
-
- /** Default tag min length*/
- private static final int TAG_MIN_LENGTH = 3;
-
- public TagContainerLayout(Context context) {
- this(context, null);
- }
-
- public TagContainerLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TagContainerLayout(Context context, AttributeSet attrs, int defStyleAttr){
- super(context, attrs, defStyleAttr);
- init(context, attrs, defStyleAttr);
- }
-
- private void init(Context context, AttributeSet attrs, int defStyleAttr){
- TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.AndroidTagView,
- defStyleAttr, 0);
- mVerticalInterval = (int)attributes.getDimension(R.styleable.AndroidTagView_vertical_interval,
- dp2px(context, DEFAULT_INTERVAL));
- mHorizontalInterval = (int)attributes.getDimension(R.styleable.AndroidTagView_horizontal_interval,
- dp2px(context, DEFAULT_INTERVAL));
- mBorderWidth = attributes.getDimension(R.styleable.AndroidTagView_container_border_width,
- dp2px(context, mBorderWidth));
- mBorderRadius = attributes.getDimension(R.styleable.AndroidTagView_container_border_radius,
- dp2px(context, mBorderRadius));
- mBorderColor = attributes.getColor(R.styleable.AndroidTagView_container_border_color,
- mBorderColor);
- mBackgroundColor = attributes.getColor(R.styleable.AndroidTagView_container_background_color,
- mBackgroundColor);
- mDragEnable = attributes.getBoolean(R.styleable.AndroidTagView_container_enable_drag, false);
- mSensitivity = attributes.getFloat(R.styleable.AndroidTagView_container_drag_sensitivity,
- mSensitivity);
- mTagMaxLength = attributes.getInt(R.styleable.AndroidTagView_tag_max_length, mTagMaxLength);
- mTheme = attributes.getInt(R.styleable.AndroidTagView_tag_theme, mTheme);
- mTagBorderWidth = attributes.getDimension(R.styleable.AndroidTagView_tag_border_width,
- dp2px(context, mTagBorderWidth));
- mTagBorderRadius = attributes.getDimension(
- R.styleable.AndroidTagView_tag_corner_radius, dp2px(context, mTagBorderRadius));
- mTagHorizontalPadding = (int) attributes.getDimension(
- R.styleable.AndroidTagView_tag_horizontal_padding, mTagHorizontalPadding);
- mTagVerticalPadding = (int) attributes.getDimension(
- R.styleable.AndroidTagView_tag_vertical_padding, mTagVerticalPadding);
- mTagTextSize = attributes.getDimension(R.styleable.AndroidTagView_tag_text_size,
- sp2px(context, mTagTextSize));
- mTagBorderColor = attributes.getColor(R.styleable.AndroidTagView_tag_border_color,
- mTagBorderColor);
- mTagBackgroundColor = attributes.getColor(R.styleable.AndroidTagView_tag_background_color,
- mTagBackgroundColor);
- mTagTextColor = attributes.getColor(R.styleable.AndroidTagView_tag_text_color, mTagTextColor);
- isTagViewClickable = attributes.getBoolean(R.styleable.AndroidTagView_tag_clickable, false);
- attributes.recycle();
-
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mRectF = new RectF();
- mChildViews = new ArrayList();
- mViewDragHelper = ViewDragHelper.create(this, mSensitivity, new DragHelperCallBack());
- setWillNotDraw(false);
- setDragState();
- setTagMaxLength(mTagMaxLength);
- setTagHorizontalPadding(mTagHorizontalPadding);
- setTagVerticalPadding(mTagVerticalPadding);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- measureChildren(widthMeasureSpec, heightMeasureSpec);
- final int childCount = getChildCount();
- int lines = childCount == 0 ? 0 : getChildLines(childCount);
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
-
- if (childCount == 0){
- setMeasuredDimension(0, 0);
- }else if (heightSpecMode == MeasureSpec.AT_MOST) {
- setMeasuredDimension(widthSpecSize, (mVerticalInterval + mChildHeight) * lines
- - mVerticalInterval + getPaddingTop() + getPaddingBottom());
- }else {
- setMeasuredDimension(widthSpecSize, heightSpecSize);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int availableW = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
- int childCount = getChildCount();
- int curLeft = getPaddingLeft(), curTop = getPaddingTop();
- mViewPos = new int[childCount * 2];
- for (int i = 0; i < childCount; i++) {
- final View childView = getChildAt(i);
- if (childView.getVisibility() != GONE) {
- int width = childView.getMeasuredWidth();
- if (curLeft + width + mHorizontalInterval - getPaddingLeft() > availableW){
- curLeft = getPaddingLeft();
- curTop += mChildHeight + mVerticalInterval;
- }
- mViewPos[i * 2] = curLeft;
- mViewPos[i * 2 + 1] = curTop;
- childView.layout(curLeft, curTop, curLeft + width, curTop + mChildHeight);
- curLeft += width + mHorizontalInterval;
- }
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- mRectF.set(canvas.getClipBounds().left, canvas.getClipBounds().top,
- canvas.getClipBounds().right, canvas.getClipBounds().bottom);
-
- mPaint.setStyle(Paint.Style.FILL);
- mPaint.setColor(mBackgroundColor);
- canvas.drawRoundRect(mRectF, mBorderRadius, mBorderRadius, mPaint);
-
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(mBorderWidth);
- mPaint.setColor(mBorderColor);
- canvas.drawRoundRect(mRectF, mBorderRadius, mBorderRadius, mPaint);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return mViewDragHelper.shouldInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- mViewDragHelper.processTouchEvent(event);
- return true;
- }
-
- @Override
- public void computeScroll() {
- super.computeScroll();
- if (mViewDragHelper.continueSettling(true)){
- requestLayout();
- }
- }
-
- private int getChildLines(int childCount){
- int availableW = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
- int lines = 1;
- for (int i = 0, curLineW = 0; i < childCount; i++) {
- View childView = getChildAt(i);
- int dis = childView.getMeasuredWidth() + (int) mHorizontalInterval;
- int height = childView.getMeasuredHeight();
- mChildHeight = i == 0 ? height : Math.min(mChildHeight, height);
- curLineW += dis;
- if (curLineW > availableW){
- lines++;
- curLineW = dis;
- }
- }
- return lines;
- }
-
- private int[] onUpdateColorFactory(){
- int[] colors;
- if (mTheme == ColorFactory.RANDOM){
- colors = ColorFactory.onRandomBuild();
- }else if (mTheme == ColorFactory.PURE_TEAL){
- colors = ColorFactory.onPureBuild(ColorFactory.PURE_COLOR.TEAL);
- }else if (mTheme == ColorFactory.PURE_CYAN){
- colors = ColorFactory.onPureBuild(ColorFactory.PURE_COLOR.CYAN);
- }else {
- colors = new int[]{mTagBackgroundColor, mTagBorderColor, mTagTextColor};
- }
- return colors;
- }
-
- private void onSetTag(){
- if (mTags == null || mTags.size() == 0){
- return;
- }
- for (int i = 0; i < mTags.size(); i++) {
- onAddTag(mTags.get(i), mChildViews.size());
- }
- postInvalidate();
- }
-
- private void onAddTag(String text, int position) {
- if (position < 0 || position > mChildViews.size()){
- throw new RuntimeException("Illegal position!");
- }
- TagView tagView = new TagView(getContext(), text);
- initTagView(tagView);
- mChildViews.add(position, tagView);
- if (position < mChildViews.size()){
- for (int i = position; i < mChildViews.size(); i++) {
- mChildViews.get(i).setTag(i);
- }
- }else {
- tagView.setTag(position);
- }
- addView(tagView, position);
- }
-
- private void initTagView(TagView tagView){
- tagView.setTagMaxLength(mTagMaxLength);
- tagView.setOnTagClickListener(mOnTagClickListener);
- int[] colors = onUpdateColorFactory();
- tagView.setTagBackgroundColor(colors[0]);
- tagView.setTagBorderColor(colors[1]);
- tagView.setTagTextColor(colors[2]);
- tagView.setBorderWidth(mTagBorderWidth);
- tagView.setBorderRadius(mTagBorderRadius);
- tagView.setTextSize(mTagTextSize);
- tagView.setHorizontalPadding(mTagHorizontalPadding);
- tagView.setVerticalPadding(mTagVerticalPadding);
- tagView.setIsViewClickable(isTagViewClickable);
- }
-
- private void onRemoveTag(int position){
- if (position < 0 || position >= mChildViews.size()){
- throw new RuntimeException("Illegal position!");
- }
- mChildViews.remove(position);
- removeViewAt(position);
- for (int i = position; i < mChildViews.size(); i++) {
- mChildViews.get(i).setTag(i);
- }
- // TODO, make removed view null?
- }
-
- private int[] onGetNewPosition(View view){
- int left = view.getLeft();
- int top = view.getTop();
- int bestMatchLeft = mViewPos[(int)view.getTag() * 2];
- int bestMatchTop = mViewPos[(int)view.getTag() * 2 + 1];
- int tmpTopDis = Math.abs(top - bestMatchTop);
- for (int i = 0; i < mViewPos.length / 2; i++) {
- if (Math.abs(top - mViewPos[i * 2 +1]) < tmpTopDis){
- bestMatchTop = mViewPos[i * 2 +1];
- tmpTopDis = Math.abs(top - mViewPos[i * 2 +1]);
- }
- }
- int rowChildCount = 0;
- int tmpLeftDis = 0;
- for (int i = 0; i < mViewPos.length / 2; i++) {
- if (mViewPos[i * 2 + 1] == bestMatchTop){
- if (rowChildCount == 0){
- bestMatchLeft = mViewPos[i * 2];
- tmpLeftDis = Math.abs(left - bestMatchLeft);
- }else {
- if (Math.abs(left - mViewPos[i * 2]) < tmpLeftDis){
- bestMatchLeft = mViewPos[i * 2];
- tmpLeftDis = Math.abs(left - bestMatchLeft);
- }
- }
- rowChildCount++;
- }
- }
- return new int[]{bestMatchLeft, bestMatchTop};
- }
-
- private int onGetCoordinateReferPos(int left, int top){
- int pos = 0;
- for (int i = 0; i < mViewPos.length / 2; i++) {
- if (left == mViewPos[i * 2] && top == mViewPos[i * 2 + 1]){
- pos = i;
- }
- }
- return pos;
- }
-
- private void setDragState(){
- mTagViewState = mDragEnable ? ViewDragHelper.STATE_DRAGGING : ViewDragHelper.STATE_IDLE;
- }
-
- private void onChangeView(View view, int newPos, int originPos){
- mChildViews.remove(originPos);
- mChildViews.add(newPos, view);
- for (View child : mChildViews) {
- child.setTag(mChildViews.indexOf(child));
- }
-
- removeViewAt(originPos);
- addView(view, newPos);
- }
-
- private int ceilTagBorderWidth(){
- return (int)Math.ceil(mTagBorderWidth);
- }
-
- private class DragHelperCallBack extends ViewDragHelper.Callback{
-
- @Override
- public void onViewDragStateChanged(int state) {
- super.onViewDragStateChanged(state);
- mTagViewState = state;
- }
-
- @Override
- public boolean tryCaptureView(View child, int pointerId) {
- return mDragEnable;
- }
-
- @Override
- public int clampViewPositionHorizontal(View child, int left, int dx) {
- final int leftX = getPaddingLeft();
- final int rightX = getWidth() - child.getWidth() - getPaddingRight();
- return Math.min(Math.max(left, leftX), rightX);
- }
-
- @Override
- public int clampViewPositionVertical(View child, int top, int dy) {
- final int topY = getPaddingTop();
- final int bottomY = getHeight() - child.getHeight() - getPaddingBottom();
- return Math.min(Math.max(top, topY), bottomY);
- }
-
- @Override
- public int getViewHorizontalDragRange(View child) {
- return getMeasuredWidth() - child.getMeasuredWidth();
- }
-
- @Override
- public int getViewVerticalDragRange(View child) {
- return getMeasuredHeight() - child.getMeasuredHeight();
- }
-
- @Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
- super.onViewReleased(releasedChild, xvel, yvel);
- int[] pos = onGetNewPosition(releasedChild);
- int posRefer = onGetCoordinateReferPos(pos[0], pos[1]);
- onChangeView(releasedChild, posRefer, (int) releasedChild.getTag());
- mViewDragHelper.settleCapturedViewAt(pos[0], pos[1]);
- invalidate();
- }
- }
-
- /**
- * Get current drag view state.
- * @return
- */
- public int getTagViewState(){
- return mTagViewState;
- }
- /**
- * Set tags
- * @param tags
- */
- public void setTags(List tags){
- mTags = tags;
- onSetTag();
- }
-
- /**
- * Set tags
- * @param tags
- */
- public void setTags(String... tags){
- mTags = Arrays.asList(tags);
- onSetTag();
- }
-
- /**
- * Inserts the specified TagView into this ContainerLayout at the end.
- * @param text
- */
- public void addTag(String text){
- addTag(text, mChildViews.size());
- }
-
- /**
- * Inserts the specified TagView into this ContainerLayout at the specified location.
- * The TagView is inserted before the current element at the specified location.
- * @param text
- * @param position
- */
- public void addTag(String text, int position){
- onAddTag(text, position);
- postInvalidate();
- }
-
- /**
- * Remove a TagView in specified position.
- * @param position
- */
- public void removeTag(int position){
- onRemoveTag(position);
- postInvalidate();
- }
-
- /**
- * Set OnTagClickListener for TagView.
- * @param listener
- */
- public void setOnTagClickListener(TagView.OnTagClickListener listener){
- mOnTagClickListener = listener;
- }
-
- /**
- * Get TagView text.
- * @param position
- * @return
- */
- public String getTagText(int position){
- return ((TagView)mChildViews.get(position)).getText();
- }
-
- /**
- * Set whether the child view can be dragged.
- * @param enable
- */
- public void setDragEnable(boolean enable){
- this.mDragEnable = enable;
- setDragState();
- }
-
- /**
- * Get current view is drag enable attribute.
- * @return
- */
- public boolean getDragEnable(){
- return mDragEnable;
- }
-
- /**
- * Set vertical interval
- * @param interval
- */
- public void setVerticalInterval(float interval){
- mVerticalInterval = (int) dp2px(getContext(), interval);
- postInvalidate();
- }
-
- /**
- * Get vertical interval in this view.
- * @return
- */
- public int getVerticalInterval(){
- return mVerticalInterval;
- }
-
- /**
- * Set horizontal interval.
- * @param interval
- */
- public void setHorizontalInterval(float interval){
- mHorizontalInterval = (int)dp2px(getContext(), interval);
- postInvalidate();
- }
-
- /**
- * Get horizontal interval in this view.
- * @return
- */
- public int getHorizontalInterval(){
- return mHorizontalInterval;
- }
-
- /**
- * Get TagContainerLayout border width.
- * @return
- */
- public float getBorderWidth() {
- return mBorderWidth;
- }
-
- /**
- * Set TagContainerLayout border width.
- * @param width
- */
- public void setBorderWidth(float width) {
- this.mBorderWidth = width;
- }
-
- /**
- * Get TagContainerLayout border radius.
- * @return
- */
- public float getBorderRadius() {
- return mBorderRadius;
- }
-
- /**
- * Set TagContainerLayout border radius.
- * @param radius
- */
- public void setBorderRadius(float radius) {
- this.mBorderRadius = radius;
- }
-
- /**
- * Get TagContainerLayout border color.
- * @return
- */
- public int getBorderColor() {
- return mBorderColor;
- }
-
- /**
- * Set TagContainerLayout border color.
- * @param color
- */
- public void setBorderColor(int color) {
- this.mBorderColor = color;
- }
-
- /**
- * Get TagContainerLayout background color.
- * @return
- */
- public int getBackgroundColor() {
- return mBackgroundColor;
- }
-
- /**
- * Set TagContainerLayout background color.
- * @param color
- */
- public void setBackgroundColor(int color) {
- this.mBackgroundColor = color;
- }
-
- /**
- * Get TagContainerLayout ViewDragHelper sensitivity.
- * @return
- */
- public float getSensitivity() {
- return mSensitivity;
- }
-
- /**
- * Set TagContainerLayout ViewDragHelper sensitivity.
- * @param sensitivity
- */
- public void setSensitivity(float sensitivity) {
- this.mSensitivity = sensitivity;
- }
-
- /**
- * Set the TagView text max length(must >=3).
- * @param maxLength
- */
- public void setTagMaxLength(int maxLength){
- mTagMaxLength = maxLength < TAG_MIN_LENGTH ? TAG_MIN_LENGTH : maxLength;
- }
-
- /**
- * Get TagView max length.
- * @return
- */
- public int getTagMaxLength(){
- return mTagMaxLength;
- }
-
- /**
- * Set TagView theme.
- * @param theme
- */
- public void setTheme(int theme){
- mTheme = theme;
- }
-
- /**
- * Get TagView theme.
- * @return
- */
- public int getTheme(){
- return mTheme;
- }
-
- /**
- * Get TagView is clickable.
- * @return
- */
- public boolean getIsTagViewClickable() {
- return isTagViewClickable;
- }
-
- /**
- * Set TagView is clickable
- * @param clickable
- */
- public void setIsTagViewClickable(boolean clickable) {
- this.isTagViewClickable = clickable;
- }
-
- /**
- * Get TagView border width.
- * @return
- */
- public float getTagBorderWidth() {
- return mTagBorderWidth;
- }
-
- /**
- * Set TagView border width.
- * @param width
- */
- public void setTagBorderWidth(float width) {
- this.mTagBorderWidth = width;
- }
-
- /**
- * Get TagView border radius.
- * @return
- */
- public float getTagBorderRadius() {
- return mTagBorderRadius;
- }
-
- /**
- * Set TagView border radius.
- * @param radius
- */
- public void setTagBorderRadius(float radius) {
- this.mTagBorderRadius = radius;
- }
-
- /**
- * Get TagView text size.
- * @return
- */
- public float getTagTextSize() {
- return mTagTextSize;
- }
-
- /**
- * Set TagView text size.
- * @param size
- */
- public void setTagTextSize(float size) {
- this.mTagTextSize = size;
- }
-
- /**
- * Get TagView horizontal padding.
- * @return
- */
- public int getTagHorizontalPadding() {
- return mTagHorizontalPadding;
- }
-
- /**
- * Set TagView horizontal padding.
- * @param padding
- */
- public void setTagHorizontalPadding(int padding) {
- int ceilWidth = ceilTagBorderWidth();
- this.mTagHorizontalPadding = padding < ceilWidth ? ceilWidth : padding;
- }
-
- /**
- * Get TagView vertical padding.
- * @return
- */
- public int getTagVerticalPadding() {
- return mTagVerticalPadding;
- }
-
- /**
- * Set TagView vertical padding.
- * @param padding
- */
- public void setTagVerticalPadding(int padding) {
- int ceilWidth = ceilTagBorderWidth();
- this.mTagVerticalPadding = padding < ceilWidth ? ceilWidth : padding;
- }
-
- /**
- * Get TagView border color.
- * @return
- */
- public int getTagBorderColor() {
- return mTagBorderColor;
- }
-
- /**
- * Set TagView border color.
- * @param color
- */
- public void setTagBorderColor(int color) {
- this.mTagBorderColor = color;
- }
-
- /**
- * Get TagView background color.
- * @return
- */
- public int getTagBackgroundColor() {
- return mTagBackgroundColor;
- }
-
- /**
- * Set TagView background color.
- * @param color
- */
- public void setTagBackgroundColor(int color) {
- this.mTagBackgroundColor = color;
- }
-
- /**
- * Get TagView text color.
- * @return
- */
- public int getTagTextColor() {
- return mTagTextColor;
- }
-
- /**
- * Set TagView text color.
- * @param color
- */
- public void setTagTextColor(int color) {
- this.mTagTextColor = color;
- }
-
- public float dp2px(Context context, float dp) {
- final float scale = context.getResources().getDisplayMetrics().density;
- return dp * scale + 0.5f;
- }
-
- public float sp2px(Context context, float sp) {
- final float scale = context.getResources().getDisplayMetrics().scaledDensity;
- return sp * scale;
- }
-}
+/*
+ * Copyright 2015 lujun
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package co.lujun.androidtagview;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import androidx.annotation.DrawableRes;
+import androidx.customview.widget.ViewDragHelper;
+
+import static co.lujun.androidtagview.Utils.dp2px;
+import static co.lujun.androidtagview.Utils.sp2px;
+
+/**
+ * Author: lujun(http://blog.lujun.co)
+ * Date: 2015-12-30 17:14
+ */
+public class TagContainerLayout extends ViewGroup {
+
+ /**
+ * Vertical interval, default 5(dp)
+ */
+ private int mVerticalInterval;
+
+ /**
+ * The list to store the tags color info
+ */
+ private List mColorArrayList;
+
+ /**
+ * Horizontal interval, default 5(dp)
+ */
+ private int mHorizontalInterval;
+
+ /**
+ * TagContainerLayout border width(default 0.5dp)
+ */
+ private float mBorderWidth = 0.5f;
+
+ /**
+ * TagContainerLayout border radius(default 10.0dp)
+ */
+ private float mBorderRadius = 10.0f;
+
+ /**
+ * The sensitive of the ViewDragHelper(default 1.0f, normal)
+ */
+ private float mSensitivity = 1.0f;
+
+ /**
+ * TagView average height
+ */
+ private int mChildHeight;
+
+ /**
+ * TagContainerLayout border color(default #22FF0000)
+ */
+ private int mBorderColor = Color.parseColor("#22FF0000");
+
+ /**
+ * TagContainerLayout background color(default #11FF0000)
+ */
+ private int mBackgroundColor = Color.parseColor("#11FF0000");
+
+ /**
+ * The container layout gravity(default left)
+ */
+ private int mGravity = Gravity.LEFT;
+
+ /**
+ * The max line count of TagContainerLayout
+ */
+ private int mMaxLines = 0;
+
+ /**
+ * The max length for TagView(default max length 23)
+ */
+ private int mTagMaxLength = 23;
+
+ /**
+ * TagView Border width(default 0.5dp)
+ */
+ private float mTagBorderWidth = 0.5f;
+
+ /**
+ * TagView Border radius(default 15.0dp)
+ */
+ private float mTagBorderRadius = 15.0f;
+
+ /**
+ * TagView Text size(default 14sp)
+ */
+ private float mTagTextSize = 14;
+
+ /**
+ * Text direction(support:TEXT_DIRECTION_RTL & TEXT_DIRECTION_LTR, default TEXT_DIRECTION_LTR)
+ */
+ private int mTagTextDirection = View.TEXT_DIRECTION_LTR;
+
+ /**
+ * Horizontal padding for TagView, include left & right padding(left & right padding are equal, default 10dp)
+ */
+ private int mTagHorizontalPadding = 10;
+
+ /**
+ * Vertical padding for TagView, include top & bottom padding(top & bottom padding are equal, default 8dp)
+ */
+ private int mTagVerticalPadding = 8;
+
+ /**
+ * TagView border color(default #88F44336)
+ */
+ private int mTagBorderColor = Color.parseColor("#88F44336");
+
+ /**
+ * TagView background color(default #33F44336)
+ */
+ private int mTagBackgroundColor = Color.parseColor("#33F44336");
+
+ /**
+ * Selected TagView background color(default #33FF7669)
+ */
+ private int mSelectedTagBackgroundColor = Color.parseColor("#33FF7669");
+
+ /**
+ * TagView text color(default #FF666666)
+ */
+ private int mTagTextColor = Color.parseColor("#FF666666");
+
+ /**
+ * TagView typeface
+ */
+ private Typeface mTagTypeface = Typeface.DEFAULT;
+
+ /**
+ * Whether TagView can clickable(default unclickable)
+ */
+ private boolean isTagViewClickable;
+
+ /**
+ * Whether TagView can selectable(default unselectable)
+ */
+ private boolean isTagViewSelectable;
+
+ /**
+ * Tags
+ */
+ private List mTags;
+
+ /**
+ * Default image for new tags
+ */
+ private int mDefaultImageDrawableID = -1;
+
+ /**
+ * Can drag TagView(default false)
+ */
+ private boolean mDragEnable;
+
+ /**
+ * TagView drag state(default STATE_IDLE)
+ */
+ private int mTagViewState = ViewDragHelper.STATE_IDLE;
+
+ /**
+ * The distance between baseline and descent(default 2.75dp)
+ */
+ private float mTagBdDistance = 2.75f;
+
+ /**
+ * OnTagClickListener for TagView
+ */
+ private TagView.OnTagClickListener mOnTagClickListener;
+
+ /**
+ * Whether to support 'letters show with RTL(eg: Android to diordnA)' style(default false)
+ */
+ private boolean mTagSupportLettersRTL = false;
+
+ private Paint mPaint;
+
+ private RectF mRectF;
+
+ private ViewDragHelper mViewDragHelper;
+
+ private List mChildViews;
+
+ private int[] mViewPos;
+
+ /**
+ * View theme(default PURE_CYAN)
+ */
+ private int mTheme = ColorFactory.PURE_CYAN;
+
+ /**
+ * Default interval(dp)
+ */
+ private static final float DEFAULT_INTERVAL = 5;
+
+ /**
+ * Default tag min length
+ */
+ private static final int TAG_MIN_LENGTH = 3;
+
+ /**
+ * The ripple effect duration(In milliseconds, default 1000ms)
+ */
+ private int mRippleDuration = 1000;
+
+ /**
+ * The ripple effect color(default #EEEEEE)
+ */
+ private int mRippleColor;
+
+ /**
+ * The ripple effect color alpha(the value may between 0 - 255, default 128)
+ */
+ private int mRippleAlpha = 128;
+
+ /**
+ * Enable draw cross icon(default false)
+ */
+ private boolean mEnableCross = false;
+
+ /**
+ * The cross area width(your cross click area, default equal to the TagView's height)
+ */
+ private float mCrossAreaWidth = 0.0f;
+
+ /**
+ * The padding of the cross area(default 10dp)
+ */
+ private float mCrossAreaPadding = 10.0f;
+
+ /**
+ * The cross icon color(default Color.BLACK)
+ */
+ private int mCrossColor = Color.BLACK;
+
+ /**
+ * The cross line width(default 1dp)
+ */
+ private float mCrossLineWidth = 1.0f;
+
+ /**
+ * TagView background resource
+ */
+ private int mTagBackgroundResource;
+
+ public TagContainerLayout(Context context) {
+ this(context, null);
+ }
+
+ public TagContainerLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TagContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs, defStyleAttr);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+ TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.AndroidTagView,
+ defStyleAttr, 0);
+ mVerticalInterval = (int) attributes.getDimension(R.styleable.AndroidTagView_vertical_interval,
+ dp2px(context, DEFAULT_INTERVAL));
+ mHorizontalInterval = (int) attributes.getDimension(R.styleable.AndroidTagView_horizontal_interval,
+ dp2px(context, DEFAULT_INTERVAL));
+ mBorderWidth = attributes.getDimension(R.styleable.AndroidTagView_container_border_width,
+ dp2px(context, mBorderWidth));
+ mBorderRadius = attributes.getDimension(R.styleable.AndroidTagView_container_border_radius,
+ dp2px(context, mBorderRadius));
+ mTagBdDistance = attributes.getDimension(R.styleable.AndroidTagView_tag_bd_distance,
+ dp2px(context, mTagBdDistance));
+ mBorderColor = attributes.getColor(R.styleable.AndroidTagView_container_border_color,
+ mBorderColor);
+ mBackgroundColor = attributes.getColor(R.styleable.AndroidTagView_container_background_color,
+ mBackgroundColor);
+ mDragEnable = attributes.getBoolean(R.styleable.AndroidTagView_container_enable_drag, false);
+ mSensitivity = attributes.getFloat(R.styleable.AndroidTagView_container_drag_sensitivity,
+ mSensitivity);
+ mGravity = attributes.getInt(R.styleable.AndroidTagView_container_gravity, mGravity);
+ mMaxLines = attributes.getInt(R.styleable.AndroidTagView_container_max_lines, mMaxLines);
+ mTagMaxLength = attributes.getInt(R.styleable.AndroidTagView_tag_max_length, mTagMaxLength);
+ mTheme = attributes.getInt(R.styleable.AndroidTagView_tag_theme, mTheme);
+ mTagBorderWidth = attributes.getDimension(R.styleable.AndroidTagView_tag_border_width,
+ dp2px(context, mTagBorderWidth));
+ mTagBorderRadius = attributes.getDimension(
+ R.styleable.AndroidTagView_tag_corner_radius, dp2px(context, mTagBorderRadius));
+ mTagHorizontalPadding = (int) attributes.getDimension(
+ R.styleable.AndroidTagView_tag_horizontal_padding,
+ dp2px(context, mTagHorizontalPadding));
+ mTagVerticalPadding = (int) attributes.getDimension(
+ R.styleable.AndroidTagView_tag_vertical_padding, dp2px(context, mTagVerticalPadding));
+ mTagTextSize = attributes.getDimension(R.styleable.AndroidTagView_tag_text_size,
+ sp2px(context, mTagTextSize));
+ mTagBorderColor = attributes.getColor(R.styleable.AndroidTagView_tag_border_color,
+ mTagBorderColor);
+ mTagBackgroundColor = attributes.getColor(R.styleable.AndroidTagView_tag_background_color,
+ mTagBackgroundColor);
+ mTagTextColor = attributes.getColor(R.styleable.AndroidTagView_tag_text_color, mTagTextColor);
+ mTagTextDirection = attributes.getInt(R.styleable.AndroidTagView_tag_text_direction, mTagTextDirection);
+ isTagViewClickable = attributes.getBoolean(R.styleable.AndroidTagView_tag_clickable, false);
+ isTagViewSelectable = attributes.getBoolean(R.styleable.AndroidTagView_tag_selectable, false);
+ mRippleColor = attributes.getColor(R.styleable.AndroidTagView_tag_ripple_color, Color.parseColor("#EEEEEE"));
+ mRippleAlpha = attributes.getInteger(R.styleable.AndroidTagView_tag_ripple_alpha, mRippleAlpha);
+ mRippleDuration = attributes.getInteger(R.styleable.AndroidTagView_tag_ripple_duration, mRippleDuration);
+ mEnableCross = attributes.getBoolean(R.styleable.AndroidTagView_tag_enable_cross, mEnableCross);
+ mCrossAreaWidth = attributes.getDimension(R.styleable.AndroidTagView_tag_cross_width,
+ dp2px(context, mCrossAreaWidth));
+ mCrossAreaPadding = attributes.getDimension(R.styleable.AndroidTagView_tag_cross_area_padding,
+ dp2px(context, mCrossAreaPadding));
+ mCrossColor = attributes.getColor(R.styleable.AndroidTagView_tag_cross_color, mCrossColor);
+ mCrossLineWidth = attributes.getDimension(R.styleable.AndroidTagView_tag_cross_line_width,
+ dp2px(context, mCrossLineWidth));
+ mTagSupportLettersRTL = attributes.getBoolean(R.styleable.AndroidTagView_tag_support_letters_rlt,
+ mTagSupportLettersRTL);
+ mTagBackgroundResource = attributes.getResourceId(R.styleable.AndroidTagView_tag_background,
+ mTagBackgroundResource);
+ attributes.recycle();
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRectF = new RectF();
+ mChildViews = new ArrayList();
+ mViewDragHelper = ViewDragHelper.create(this, mSensitivity, new DragHelperCallBack());
+ setWillNotDraw(false);
+ setTagMaxLength(mTagMaxLength);
+ setTagHorizontalPadding(mTagHorizontalPadding);
+ setTagVerticalPadding(mTagVerticalPadding);
+
+ if (isInEditMode()) {
+ addTag("sample tag");
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ final int childCount = getChildCount();
+ int lines = childCount == 0 ? 0 : getChildLines(childCount);
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+// int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+ int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ if (childCount == 0) {
+ setMeasuredDimension(0, 0);
+ } else if (heightSpecMode == MeasureSpec.AT_MOST
+ || heightSpecMode == MeasureSpec.UNSPECIFIED) {
+ setMeasuredDimension(widthSpecSize, (mVerticalInterval + mChildHeight) * lines
+ - mVerticalInterval + getPaddingTop() + getPaddingBottom());
+ } else {
+ setMeasuredDimension(widthSpecSize, heightSpecSize);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mRectF.set(0, 0, w, h);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int childCount;
+ if ((childCount = getChildCount()) <= 0) {
+ return;
+ }
+ int availableW = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
+ int curRight = getMeasuredWidth() - getPaddingRight();
+ int curTop = getPaddingTop();
+ int curLeft = getPaddingLeft();
+ int sPos = 0;
+ mViewPos = new int[childCount * 2];
+
+ for (int i = 0; i < childCount; i++) {
+ final View childView = getChildAt(i);
+ if (childView.getVisibility() != GONE) {
+ int width = childView.getMeasuredWidth();
+ if (mGravity == Gravity.RIGHT) {
+ if (curRight - width < getPaddingLeft()) {
+ curRight = getMeasuredWidth() - getPaddingRight();
+ curTop += mChildHeight + mVerticalInterval;
+ }
+ mViewPos[i * 2] = curRight - width;
+ mViewPos[i * 2 + 1] = curTop;
+ curRight -= width + mHorizontalInterval;
+ } else if (mGravity == Gravity.CENTER) {
+ if (curLeft + width - getPaddingLeft() > availableW) {
+ int leftW = getMeasuredWidth() - mViewPos[(i - 1) * 2]
+ - getChildAt(i - 1).getMeasuredWidth() - getPaddingRight();
+ for (int j = sPos; j < i; j++) {
+ mViewPos[j * 2] = mViewPos[j * 2] + leftW / 2;
+ }
+ sPos = i;
+ curLeft = getPaddingLeft();
+ curTop += mChildHeight + mVerticalInterval;
+ }
+ mViewPos[i * 2] = curLeft;
+ mViewPos[i * 2 + 1] = curTop;
+ curLeft += width + mHorizontalInterval;
+
+ if (i == childCount - 1) {
+ int leftW = getMeasuredWidth() - mViewPos[i * 2]
+ - childView.getMeasuredWidth() - getPaddingRight();
+ for (int j = sPos; j < childCount; j++) {
+ mViewPos[j * 2] = mViewPos[j * 2] + leftW / 2;
+ }
+ }
+ } else {
+ if (curLeft + width - getPaddingLeft() > availableW) {
+ curLeft = getPaddingLeft();
+ curTop += mChildHeight + mVerticalInterval;
+ }
+ mViewPos[i * 2] = curLeft;
+ mViewPos[i * 2 + 1] = curTop;
+ curLeft += width + mHorizontalInterval;
+ }
+ }
+ }
+
+ // layout all child views
+ for (int i = 0; i < mViewPos.length / 2; i++) {
+ View childView = getChildAt(i);
+ childView.layout(mViewPos[i * 2], mViewPos[i * 2 + 1],
+ mViewPos[i * 2] + childView.getMeasuredWidth(),
+ mViewPos[i * 2 + 1] + mChildHeight);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(mBackgroundColor);
+ canvas.drawRoundRect(mRectF, mBorderRadius, mBorderRadius, mPaint);
+
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(mBorderWidth);
+ mPaint.setColor(mBorderColor);
+ canvas.drawRoundRect(mRectF, mBorderRadius, mBorderRadius, mPaint);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mViewDragHelper.shouldInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mViewDragHelper.processTouchEvent(event);
+ return true;
+ }
+
+ @Override
+ public void computeScroll() {
+ super.computeScroll();
+ if (mViewDragHelper.continueSettling(true)) {
+ requestLayout();
+ }
+ }
+
+ private int getChildLines(int childCount) {
+ int availableW = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
+ int lines = 1;
+ for (int i = 0, curLineW = 0; i < childCount; i++) {
+ View childView = getChildAt(i);
+ int dis = childView.getMeasuredWidth() + mHorizontalInterval;
+ int height = childView.getMeasuredHeight();
+ mChildHeight = i == 0 ? height : Math.min(mChildHeight, height);
+ curLineW += dis;
+ if (curLineW - mHorizontalInterval > availableW) {
+ lines++;
+ curLineW = dis;
+ }
+ }
+
+ return mMaxLines <= 0 ? lines : mMaxLines;
+ }
+
+ private int[] onUpdateColorFactory() {
+ int[] colors;
+ if (mTheme == ColorFactory.RANDOM) {
+ colors = ColorFactory.onRandomBuild();
+ } else if (mTheme == ColorFactory.PURE_TEAL) {
+ colors = ColorFactory.onPureBuild(ColorFactory.PURE_COLOR.TEAL);
+ } else if (mTheme == ColorFactory.PURE_CYAN) {
+ colors = ColorFactory.onPureBuild(ColorFactory.PURE_COLOR.CYAN);
+ } else {
+ colors = new int[]{mTagBackgroundColor, mTagBorderColor, mTagTextColor, mSelectedTagBackgroundColor};
+ }
+ return colors;
+ }
+
+ private void onSetTag() {
+ if (mTags == null) {
+ throw new RuntimeException("NullPointer exception!");
+ }
+ removeAllTags();
+ if (mTags.size() == 0) {
+ return;
+ }
+ for (int i = 0; i < mTags.size(); i++) {
+ onAddTag(mTags.get(i), mChildViews.size());
+ }
+ postInvalidate();
+ }
+
+ private void onAddTag(String text, int position) {
+ if (position < 0 || position > mChildViews.size()) {
+ throw new RuntimeException("Illegal position!");
+ }
+ TagView tagView;
+ if (mDefaultImageDrawableID != -1) {
+ tagView = new TagView(getContext(), text, mDefaultImageDrawableID);
+ } else {
+ tagView = new TagView(getContext(), text);
+ }
+ initTagView(tagView, position);
+ mChildViews.add(position, tagView);
+ if (position < mChildViews.size()) {
+ for (int i = position; i < mChildViews.size(); i++) {
+ mChildViews.get(i).setTag(i);
+ }
+ } else {
+ tagView.setTag(position);
+ }
+ addView(tagView, position);
+ }
+
+ private void initTagView(TagView tagView, int position) {
+ int[] colors;
+ if (mColorArrayList != null && mColorArrayList.size() > 0) {
+ if (mColorArrayList.size() == mTags.size() &&
+ mColorArrayList.get(position).length >= 4) {
+ colors = mColorArrayList.get(position);
+ } else {
+ throw new RuntimeException("Illegal color list!");
+ }
+ } else {
+ colors = onUpdateColorFactory();
+ }
+
+ tagView.setTagBackgroundColor(colors[0]);
+ tagView.setTagBorderColor(colors[1]);
+ tagView.setTagTextColor(colors[2]);
+ tagView.setTagSelectedBackgroundColor(colors[3]);
+ tagView.setTagMaxLength(mTagMaxLength);
+ tagView.setTextDirection(mTagTextDirection);
+ tagView.setTypeface(mTagTypeface);
+ tagView.setBorderWidth(mTagBorderWidth);
+ tagView.setBorderRadius(mTagBorderRadius);
+ tagView.setTextSize(mTagTextSize);
+ tagView.setHorizontalPadding(mTagHorizontalPadding);
+ tagView.setVerticalPadding(mTagVerticalPadding);
+ tagView.setIsViewClickable(isTagViewClickable);
+ tagView.setIsViewSelectable(isTagViewSelectable);
+ tagView.setBdDistance(mTagBdDistance);
+ tagView.setOnTagClickListener(mOnTagClickListener);
+ tagView.setRippleAlpha(mRippleAlpha);
+ tagView.setRippleColor(mRippleColor);
+ tagView.setRippleDuration(mRippleDuration);
+ tagView.setEnableCross(mEnableCross);
+ tagView.setCrossAreaWidth(mCrossAreaWidth);
+ tagView.setCrossAreaPadding(mCrossAreaPadding);
+ tagView.setCrossColor(mCrossColor);
+ tagView.setCrossLineWidth(mCrossLineWidth);
+ tagView.setTagSupportLettersRTL(mTagSupportLettersRTL);
+ tagView.setBackgroundResource(mTagBackgroundResource);
+ }
+
+ private void invalidateTags() {
+ for (View view : mChildViews) {
+ final TagView tagView = (TagView) view;
+ tagView.setOnTagClickListener(mOnTagClickListener);
+ }
+ }
+
+ private void onRemoveTag(int position) {
+ if (position < 0 || position >= mChildViews.size()) {
+ throw new RuntimeException("Illegal position!");
+ }
+ mChildViews.remove(position);
+ removeViewAt(position);
+ for (int i = position; i < mChildViews.size(); i++) {
+ mChildViews.get(i).setTag(i);
+ }
+ // TODO, make removed view null?
+ }
+
+ private void onRemoveConsecutiveTags(List positions) {
+ int smallestPosition = Collections.min(positions);
+ for (int position : positions) {
+ if (position < 0 || position >= mChildViews.size()) {
+ throw new RuntimeException("Illegal position!");
+ }
+ mChildViews.remove(smallestPosition);
+ removeViewAt(smallestPosition);
+ }
+ for (int i = smallestPosition; i < mChildViews.size(); i++) {
+ mChildViews.get(i).setTag(i);
+ }
+ // TODO, make removed view null?
+ }
+
+ private int[] onGetNewPosition(View view) {
+ int left = view.getLeft();
+ int top = view.getTop();
+ int bestMatchLeft = mViewPos[(int) view.getTag() * 2];
+ int bestMatchTop = mViewPos[(int) view.getTag() * 2 + 1];
+ int tmpTopDis = Math.abs(top - bestMatchTop);
+ for (int i = 0; i < mViewPos.length / 2; i++) {
+ if (Math.abs(top - mViewPos[i * 2 + 1]) < tmpTopDis) {
+ bestMatchTop = mViewPos[i * 2 + 1];
+ tmpTopDis = Math.abs(top - mViewPos[i * 2 + 1]);
+ }
+ }
+ int rowChildCount = 0;
+ int tmpLeftDis = 0;
+ for (int i = 0; i < mViewPos.length / 2; i++) {
+ if (mViewPos[i * 2 + 1] == bestMatchTop) {
+ if (rowChildCount == 0) {
+ bestMatchLeft = mViewPos[i * 2];
+ tmpLeftDis = Math.abs(left - bestMatchLeft);
+ } else {
+ if (Math.abs(left - mViewPos[i * 2]) < tmpLeftDis) {
+ bestMatchLeft = mViewPos[i * 2];
+ tmpLeftDis = Math.abs(left - bestMatchLeft);
+ }
+ }
+ rowChildCount++;
+ }
+ }
+ return new int[]{bestMatchLeft, bestMatchTop};
+ }
+
+ private int onGetCoordinateReferPos(int left, int top) {
+ int pos = 0;
+ for (int i = 0; i < mViewPos.length / 2; i++) {
+ if (left == mViewPos[i * 2] && top == mViewPos[i * 2 + 1]) {
+ pos = i;
+ }
+ }
+ return pos;
+ }
+
+ private void onChangeView(View view, int newPos, int originPos) {
+ mChildViews.remove(originPos);
+ mChildViews.add(newPos, view);
+ for (View child : mChildViews) {
+ child.setTag(mChildViews.indexOf(child));
+ }
+
+ removeViewAt(originPos);
+ addView(view, newPos);
+ }
+
+ private int ceilTagBorderWidth() {
+ return (int) Math.ceil(mTagBorderWidth);
+ }
+
+ private class DragHelperCallBack extends ViewDragHelper.Callback {
+
+ @Override
+ public void onViewDragStateChanged(int state) {
+ super.onViewDragStateChanged(state);
+ mTagViewState = state;
+ }
+
+ @Override
+ public boolean tryCaptureView(View child, int pointerId) {
+ requestDisallowInterceptTouchEvent(true);
+ return mDragEnable;
+ }
+
+ @Override
+ public int clampViewPositionHorizontal(View child, int left, int dx) {
+ final int leftX = getPaddingLeft();
+ final int rightX = getWidth() - child.getWidth() - getPaddingRight();
+ return Math.min(Math.max(left, leftX), rightX);
+ }
+
+ @Override
+ public int clampViewPositionVertical(View child, int top, int dy) {
+ final int topY = getPaddingTop();
+ final int bottomY = getHeight() - child.getHeight() - getPaddingBottom();
+ return Math.min(Math.max(top, topY), bottomY);
+ }
+
+ @Override
+ public int getViewHorizontalDragRange(View child) {
+ return getMeasuredWidth() - child.getMeasuredWidth();
+ }
+
+ @Override
+ public int getViewVerticalDragRange(View child) {
+ return getMeasuredHeight() - child.getMeasuredHeight();
+ }
+
+ @Override
+ public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ super.onViewReleased(releasedChild, xvel, yvel);
+ requestDisallowInterceptTouchEvent(false);
+ int[] pos = onGetNewPosition(releasedChild);
+ int posRefer = onGetCoordinateReferPos(pos[0], pos[1]);
+ onChangeView(releasedChild, posRefer, (int) releasedChild.getTag());
+ mViewDragHelper.settleCapturedViewAt(pos[0], pos[1]);
+ invalidate();
+ }
+ }
+
+ /**
+ * Get current drag view state.
+ *
+ * @return
+ */
+ public int getTagViewState() {
+ return mTagViewState;
+ }
+
+ /**
+ * Get TagView text baseline and descent distance.
+ *
+ * @return
+ */
+ public float getTagBdDistance() {
+ return mTagBdDistance;
+ }
+
+ /**
+ * Set TagView text baseline and descent distance.
+ *
+ * @param tagBdDistance
+ */
+ public void setTagBdDistance(float tagBdDistance) {
+ this.mTagBdDistance = dp2px(getContext(), tagBdDistance);
+ }
+
+ /**
+ * Set tags
+ *
+ * @param tags
+ */
+ public void setTags(List tags) {
+ mTags = tags;
+ onSetTag();
+ }
+
+ /**
+ * Set tags with own color
+ *
+ * @param tags
+ * @param colorArrayList
+ */
+ public void setTags(List tags, List colorArrayList) {
+ mTags = tags;
+ mColorArrayList = colorArrayList;
+ onSetTag();
+ }
+
+ /**
+ * Set tags
+ *
+ * @param tags
+ */
+ public void setTags(String... tags) {
+ mTags = Arrays.asList(tags);
+ onSetTag();
+ }
+
+ /**
+ * Inserts the specified TagView into this ContainerLayout at the end.
+ *
+ * @param text
+ */
+ public void addTag(String text) {
+ addTag(text, mChildViews.size());
+ }
+
+ /**
+ * Inserts the specified TagView into this ContainerLayout at the specified location.
+ * The TagView is inserted before the current element at the specified location.
+ *
+ * @param text
+ * @param position
+ */
+ public void addTag(String text, int position) {
+ onAddTag(text, position);
+ postInvalidate();
+ }
+
+ /**
+ * Remove a TagView in specified position.
+ *
+ * @param position
+ */
+ public void removeTag(int position) {
+ onRemoveTag(position);
+ postInvalidate();
+ }
+
+ /**
+ * Remove TagView in multiple consecutive positions.
+ *
+ *
+ */
+ public void removeConsecutiveTags(List positions) {
+ onRemoveConsecutiveTags(positions);
+ postInvalidate();
+ }
+
+ /**
+ * Remove all TagViews.
+ */
+ public void removeAllTags() {
+ mChildViews.clear();
+ removeAllViews();
+ postInvalidate();
+ }
+
+ /**
+ * Set OnTagClickListener for TagView.
+ *
+ * @param listener
+ */
+ public void setOnTagClickListener(TagView.OnTagClickListener listener) {
+ mOnTagClickListener = listener;
+ invalidateTags();
+ }
+
+ /**
+ * Toggle select a tag
+ *
+ * @param position
+ */
+ public void toggleSelectTagView(int position) {
+ if (isTagViewSelectable){
+ TagView tagView = ((TagView)mChildViews.get(position));
+ if (tagView.getIsViewSelected()){
+ tagView.deselectView();
+ } else {
+ tagView.selectView();
+ }
+ }
+ }
+
+ /**
+ * Select a tag
+ *
+ * @param position
+ */
+ public void selectTagView(int position) {
+ if (isTagViewSelectable)
+ ((TagView)mChildViews.get(position)).selectView();
+ }
+
+ /**
+ * Deselect a tag
+ *
+ * @param position
+ */
+ public void deselectTagView(int position) {
+ if (isTagViewSelectable)
+ ((TagView)mChildViews.get(position)).deselectView();
+ }
+
+ /**
+ * Return selected TagView positions
+ *
+ * @return list of selected positions
+ */
+ public List getSelectedTagViewPositions() {
+ List selectedPositions = new ArrayList<>();
+ for (int i = 0; i < mChildViews.size(); i++){
+ if (((TagView)mChildViews.get(i)).getIsViewSelected()){
+ selectedPositions.add(i);
+ }
+ }
+ return selectedPositions;
+ }
+
+ /**
+ * Return selected TagView text
+ *
+ * @return list of selected tag text
+ */
+ public List getSelectedTagViewText() {
+ List selectedTagText = new ArrayList<>();
+ for (int i = 0; i < mChildViews.size(); i++){
+ TagView tagView = (TagView)mChildViews.get(i);
+ if ((tagView.getIsViewSelected())){
+ selectedTagText.add(tagView.getText());
+ }
+ }
+ return selectedTagText;
+ }
+
+ /**
+ * Return number of child tags
+ *
+ * @return size
+ */
+ public int size() {
+ return mChildViews.size();
+ }
+
+ /**
+ * Get TagView text.
+ *
+ * @param position
+ * @return
+ */
+ public String getTagText(int position) {
+ return ((TagView) mChildViews.get(position)).getText();
+ }
+
+ /**
+ * Get a string list for all tags in TagContainerLayout.
+ *
+ * @return
+ */
+ public List getTags() {
+ List tmpList = new ArrayList();
+ for (View view : mChildViews) {
+ if (view instanceof TagView) {
+ tmpList.add(((TagView) view).getText());
+ }
+ }
+ return tmpList;
+ }
+
+ /**
+ * Set whether the child view can be dragged.
+ *
+ * @param enable
+ */
+ public void setDragEnable(boolean enable) {
+ this.mDragEnable = enable;
+ }
+
+ /**
+ * Get current view is drag enable attribute.
+ *
+ * @return
+ */
+ public boolean getDragEnable() {
+ return mDragEnable;
+ }
+
+ /**
+ * Set vertical interval
+ *
+ * @param interval
+ */
+ public void setVerticalInterval(float interval) {
+ mVerticalInterval = (int) dp2px(getContext(), interval);
+ postInvalidate();
+ }
+
+ /**
+ * Get vertical interval in this view.
+ *
+ * @return
+ */
+ public int getVerticalInterval() {
+ return mVerticalInterval;
+ }
+
+ /**
+ * Set horizontal interval.
+ *
+ * @param interval
+ */
+ public void setHorizontalInterval(float interval) {
+ mHorizontalInterval = (int) dp2px(getContext(), interval);
+ postInvalidate();
+ }
+
+ /**
+ * Get horizontal interval in this view.
+ *
+ * @return
+ */
+ public int getHorizontalInterval() {
+ return mHorizontalInterval;
+ }
+
+ /**
+ * Get TagContainerLayout border width.
+ *
+ * @return
+ */
+ public float getBorderWidth() {
+ return mBorderWidth;
+ }
+
+ /**
+ * Set TagContainerLayout border width.
+ *
+ * @param width
+ */
+ public void setBorderWidth(float width) {
+ this.mBorderWidth = width;
+ }
+
+ /**
+ * Get TagContainerLayout border radius.
+ *
+ * @return
+ */
+ public float getBorderRadius() {
+ return mBorderRadius;
+ }
+
+ /**
+ * Set TagContainerLayout border radius.
+ *
+ * @param radius
+ */
+ public void setBorderRadius(float radius) {
+ this.mBorderRadius = radius;
+ }
+
+ /**
+ * Get TagContainerLayout border color.
+ *
+ * @return
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+ /**
+ * Set TagContainerLayout border color.
+ *
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ this.mBorderColor = color;
+ }
+
+ /**
+ * Get TagContainerLayout background color.
+ *
+ * @return
+ */
+ public int getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ /**
+ * Set TagContainerLayout background color.
+ *
+ * @param color
+ */
+ public void setBackgroundColor(int color) {
+ this.mBackgroundColor = color;
+ }
+
+ /**
+ * Get container layout gravity.
+ *
+ * @return
+ */
+ public int getGravity() {
+ return mGravity;
+ }
+
+ /**
+ * Set container layout gravity.
+ *
+ * @param gravity
+ */
+ public void setGravity(int gravity) {
+ this.mGravity = gravity;
+ }
+
+ /**
+ * Get TagContainerLayout ViewDragHelper sensitivity.
+ *
+ * @return
+ */
+ public float getSensitivity() {
+ return mSensitivity;
+ }
+
+ /**
+ * Set TagContainerLayout ViewDragHelper sensitivity.
+ *
+ * @param sensitivity
+ */
+ public void setSensitivity(float sensitivity) {
+ this.mSensitivity = sensitivity;
+ }
+
+ /**
+ * Get default tag image
+ *
+ * @return
+ */
+ public int getDefaultImageDrawableID() {
+ return mDefaultImageDrawableID;
+ }
+
+ /**
+ * Set default image for tags.
+ *
+ * @param imageID
+ */
+ public void setDefaultImageDrawableID(int imageID) {
+ this.mDefaultImageDrawableID = imageID;
+ }
+
+ /**
+ * Set max line count for TagContainerLayout
+ *
+ * @param maxLines max line count
+ */
+ public void setMaxLines(int maxLines) {
+ mMaxLines = maxLines;
+ postInvalidate();
+ }
+
+ /**
+ * Get TagContainerLayout's max lines
+ *
+ * @return maxLines
+ */
+ public int getMaxLines() {
+ return mMaxLines;
+ }
+
+ /**
+ * Set the TagView text max length(must greater or equal to 3).
+ *
+ * @param maxLength
+ */
+ public void setTagMaxLength(int maxLength) {
+ mTagMaxLength = maxLength < TAG_MIN_LENGTH ? TAG_MIN_LENGTH : maxLength;
+ }
+
+ /**
+ * Get TagView max length.
+ *
+ * @return
+ */
+ public int getTagMaxLength() {
+ return mTagMaxLength;
+ }
+
+ /**
+ * Set TagView theme.
+ *
+ * @param theme
+ */
+ public void setTheme(int theme) {
+ mTheme = theme;
+ }
+
+ /**
+ * Get TagView theme.
+ *
+ * @return
+ */
+ public int getTheme() {
+ return mTheme;
+ }
+
+ /**
+ * Get TagView is clickable.
+ *
+ * @return
+ */
+ public boolean getIsTagViewClickable() {
+ return isTagViewClickable;
+ }
+
+ /**
+ * Set TagView is clickable
+ *
+ * @param clickable
+ */
+ public void setIsTagViewClickable(boolean clickable) {
+ this.isTagViewClickable = clickable;
+ }
+
+ /**
+ * Get TagView is selectable.
+ *
+ * @return
+ */
+ public boolean getIsTagViewSelectable() {
+ return isTagViewSelectable;
+ }
+
+ /**
+ * Set TagView is selectable
+ *
+ * @param selectable
+ */
+ public void setIsTagViewSelectable(boolean selectable) {
+ this.isTagViewSelectable= selectable;
+ }
+
+ /**
+ * Get TagView border width.
+ *
+ * @return
+ */
+ public float getTagBorderWidth() {
+ return mTagBorderWidth;
+ }
+
+ /**
+ * Set TagView border width.
+ *
+ * @param width
+ */
+ public void setTagBorderWidth(float width) {
+ this.mTagBorderWidth = width;
+ }
+
+ /**
+ * Get TagView border radius.
+ *
+ * @return
+ */
+ public float getTagBorderRadius() {
+ return mTagBorderRadius;
+ }
+
+ /**
+ * Set TagView border radius.
+ *
+ * @param radius
+ */
+ public void setTagBorderRadius(float radius) {
+ this.mTagBorderRadius = radius;
+ }
+
+ /**
+ * Get TagView text size.
+ *
+ * @return
+ */
+ public float getTagTextSize() {
+ return mTagTextSize;
+ }
+
+ /**
+ * Set TagView text size.
+ *
+ * @param size
+ */
+ public void setTagTextSize(float size) {
+ this.mTagTextSize = size;
+ }
+
+ /**
+ * Get TagView horizontal padding.
+ *
+ * @return
+ */
+ public int getTagHorizontalPadding() {
+ return mTagHorizontalPadding;
+ }
+
+ /**
+ * Set TagView horizontal padding.
+ *
+ * @param padding
+ */
+ public void setTagHorizontalPadding(int padding) {
+ int ceilWidth = ceilTagBorderWidth();
+ this.mTagHorizontalPadding = padding < ceilWidth ? ceilWidth : padding;
+ }
+
+ /**
+ * Get TagView vertical padding.
+ *
+ * @return
+ */
+ public int getTagVerticalPadding() {
+ return mTagVerticalPadding;
+ }
+
+ /**
+ * Set TagView vertical padding.
+ *
+ * @param padding
+ */
+ public void setTagVerticalPadding(int padding) {
+ int ceilWidth = ceilTagBorderWidth();
+ this.mTagVerticalPadding = padding < ceilWidth ? ceilWidth : padding;
+ }
+
+ /**
+ * Get TagView border color.
+ *
+ * @return
+ */
+ public int getTagBorderColor() {
+ return mTagBorderColor;
+ }
+
+ /**
+ * Set TagView border color.
+ *
+ * @param color
+ */
+ public void setTagBorderColor(int color) {
+ this.mTagBorderColor = color;
+ }
+
+ /**
+ * Get TagView background color.
+ *
+ * @return
+ */
+ public int getTagBackgroundColor() {
+ return mTagBackgroundColor;
+ }
+
+ /**
+ * Set TagView background color.
+ *
+ * @param color
+ */
+ public void setTagBackgroundColor(int color) {
+ this.mTagBackgroundColor = color;
+ }
+
+ /**
+ * Get TagView text color.
+ *
+ * @return
+ */
+ public int getTagTextColor() {
+ return mTagTextColor;
+ }
+
+ /**
+ * Set tag text direction, support:View.TEXT_DIRECTION_RTL and View.TEXT_DIRECTION_LTR,
+ * default View.TEXT_DIRECTION_LTR
+ *
+ * @param textDirection
+ */
+ public void setTagTextDirection(int textDirection) {
+ this.mTagTextDirection = textDirection;
+ }
+
+ /**
+ * Get TagView typeface.
+ *
+ * @return
+ */
+ public Typeface getTagTypeface() {
+ return mTagTypeface;
+ }
+
+ /**
+ * Set TagView typeface.
+ *
+ * @param typeface
+ */
+ public void setTagTypeface(Typeface typeface) {
+ this.mTagTypeface = typeface;
+ }
+
+ /**
+ * Get tag text direction
+ *
+ * @return
+ */
+ public int getTagTextDirection() {
+ return mTagTextDirection;
+ }
+
+ /**
+ * Set TagView text color.
+ *
+ * @param color
+ */
+ public void setTagTextColor(int color) {
+ this.mTagTextColor = color;
+ }
+
+ /**
+ * Get the ripple effect color's alpha.
+ *
+ * @return
+ */
+ public int getRippleAlpha() {
+ return mRippleAlpha;
+ }
+
+ /**
+ * Set TagView ripple effect alpha, the value may between 0 to 255, default is 128.
+ *
+ * @param mRippleAlpha
+ */
+ public void setRippleAlpha(int mRippleAlpha) {
+ this.mRippleAlpha = mRippleAlpha;
+ }
+
+ /**
+ * Get the ripple effect color.
+ *
+ * @return
+ */
+ public int getRippleColor() {
+ return mRippleColor;
+ }
+
+ /**
+ * Set TagView ripple effect color.
+ *
+ * @param mRippleColor
+ */
+ public void setRippleColor(int mRippleColor) {
+ this.mRippleColor = mRippleColor;
+ }
+
+ /**
+ * Get the ripple effect duration.
+ *
+ * @return
+ */
+ public int getRippleDuration() {
+ return mRippleDuration;
+ }
+
+ /**
+ * Set TagView ripple effect duration, default is 1000ms.
+ *
+ * @param mRippleDuration
+ */
+ public void setRippleDuration(int mRippleDuration) {
+ this.mRippleDuration = mRippleDuration;
+ }
+
+ /**
+ * Set TagView cross color.
+ *
+ * @return
+ */
+ public int getCrossColor() {
+ return mCrossColor;
+ }
+
+ /**
+ * Set TagView cross color, default Color.BLACK.
+ *
+ * @param mCrossColor
+ */
+ public void setCrossColor(int mCrossColor) {
+ this.mCrossColor = mCrossColor;
+ }
+
+ /**
+ * Get agView cross area's padding.
+ *
+ * @return
+ */
+ public float getCrossAreaPadding() {
+ return mCrossAreaPadding;
+ }
+
+ /**
+ * Set TagView cross area padding, default 10dp.
+ *
+ * @param mCrossAreaPadding
+ */
+ public void setCrossAreaPadding(float mCrossAreaPadding) {
+ this.mCrossAreaPadding = mCrossAreaPadding;
+ }
+
+ /**
+ * Get is the TagView's cross enable, default false.
+ *
+ * @return
+ */
+ public boolean isEnableCross() {
+ return mEnableCross;
+ }
+
+ /**
+ * Enable or disable the TagView's cross.
+ *
+ * @param mEnableCross
+ */
+ public void setEnableCross(boolean mEnableCross) {
+ this.mEnableCross = mEnableCross;
+ }
+
+ /**
+ * Get TagView cross area width.
+ *
+ * @return
+ */
+ public float getCrossAreaWidth() {
+ return mCrossAreaWidth;
+ }
+
+ /**
+ * Set TagView area width.
+ *
+ * @param mCrossAreaWidth
+ */
+ public void setCrossAreaWidth(float mCrossAreaWidth) {
+ this.mCrossAreaWidth = mCrossAreaWidth;
+ }
+
+ /**
+ * Get TagView cross line width.
+ *
+ * @return
+ */
+ public float getCrossLineWidth() {
+ return mCrossLineWidth;
+ }
+
+ /**
+ * Set TagView cross line width, default 1dp.
+ *
+ * @param mCrossLineWidth
+ */
+ public void setCrossLineWidth(float mCrossLineWidth) {
+ this.mCrossLineWidth = mCrossLineWidth;
+ }
+
+ /**
+ * Get the 'letters show with RTL(like: Android to diordnA)' style if it's enabled
+ *
+ * @return
+ */
+ public boolean isTagSupportLettersRTL() {
+ return mTagSupportLettersRTL;
+ }
+
+ /**
+ * Set whether the 'support letters show with RTL(like: Android to diordnA)' style is enabled.
+ *
+ * @param mTagSupportLettersRTL
+ */
+ public void setTagSupportLettersRTL(boolean mTagSupportLettersRTL) {
+ this.mTagSupportLettersRTL = mTagSupportLettersRTL;
+ }
+
+ /**
+ * Get TagView in specified position.
+ *
+ * @param position the position of the TagView
+ * @return
+ */
+ public TagView getTagView(int position){
+ if (position < 0 || position >= mChildViews.size()) {
+ throw new RuntimeException("Illegal position!");
+ }
+ return (TagView) mChildViews.get(position);
+ }
+
+ /**
+ * Get TagView background resource
+ * @return
+ */
+ public int getTagBackgroundResource() {
+ return mTagBackgroundResource;
+ }
+
+ /**
+ * Set TagView background resource
+ * @param tagBackgroundResource
+ */
+ public void setTagBackgroundResource(@DrawableRes int tagBackgroundResource) {
+ this.mTagBackgroundResource = tagBackgroundResource;
+ }
+}
diff --git a/androidtagview/src/main/java/co/lujun/androidtagview/TagView.java b/androidtagview/src/main/java/co/lujun/androidtagview/TagView.java
index 23d7e4d..1a93975 100644
--- a/androidtagview/src/main/java/co/lujun/androidtagview/TagView.java
+++ b/androidtagview/src/main/java/co/lujun/androidtagview/TagView.java
@@ -1,15 +1,43 @@
+/*
+ * Copyright 2015 lujun
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package co.lujun.androidtagview;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Rect;
+import android.graphics.Path;
import android.graphics.RectF;
-import android.support.v4.widget.ViewDragHelper;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Typeface;
+import android.os.Build;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
+import androidx.customview.widget.ViewDragHelper;
+
+import static co.lujun.androidtagview.Utils.dp2px;
+
/**
* Author: lujun(http://blog.lujun.co)
* Date: 2015-12-31 11:47
@@ -37,29 +65,48 @@ public class TagView extends View {
/** TagView background color*/
private int mBackgroundColor;
+ /** TagView background color*/
+ private int mSelectedBackgroundColor;
+
/** TagView text color*/
private int mTextColor;
/** Whether this view clickable*/
private boolean isViewClickable;
+ /** Whether this view selectable*/
+ private boolean isViewSelectable;
+
+ /** Whether this view selected*/
+ private boolean isViewSelected;
+
/** The max length for this tag view*/
private int mTagMaxLength;
/** OnTagClickListener for click action*/
private OnTagClickListener mOnTagClickListener;
- /** Move slop(default 20px)*/
- private int mMoveSlop = 20;
+ /** Move slop(default 5dp)*/
+ private int mMoveSlop = 5;
+
+ /** Scroll slop threshold 4dp*/
+ private int mSlopThreshold = 4;
/** How long trigger long click callback(default 500ms)*/
private int mLongPressTime = 500;
- private Paint mPaint;
+ /** Text direction(support:TEXT_DIRECTION_RTL & TEXT_DIRECTION_LTR, default TEXT_DIRECTION_LTR)*/
+ private int mTextDirection = View.TEXT_DIRECTION_LTR;
- private RectF mRectF;
+ /** The distance between baseline and descent*/
+ private float bdDistance;
+
+ /** Whether to support 'letters show with RTL(eg: Android to diordnA)' style(default false)*/
+ private boolean mTagSupportLettersRTL = false;
- private Rect mTextBound;
+ private Paint mPaint, mRipplePaint;
+
+ private RectF mRectF;
private String mAbstractText, mOriginText;
@@ -67,74 +114,205 @@ public class TagView extends View {
private int mLastX, mLastY;
+ private float fontH, fontW;
+
+ private float mTouchX, mTouchY;
+
+ /** The ripple effect duration(default 1000ms)*/
+ private int mRippleDuration = 1000;
+
+ private float mRippleRadius;
+
+ private int mRippleColor;
+
+ private int mRippleAlpha;
+
+ private Path mPath;
+
+ private Typeface mTypeface;
+
+ private ValueAnimator mRippleValueAnimator;
+
+ private Bitmap mBitmapImage;
+
+ private boolean mEnableCross;
+
+ private float mCrossAreaWidth;
+
+ private float mCrossAreaPadding;
+
+ private int mCrossColor;
+
+ private float mCrossLineWidth;
+
+ private boolean unSupportedClipPath = false;
+
private Runnable mLongClickHandle = new Runnable() {
@Override
public void run() {
- int state = ((TagContainerLayout)getParent()).getTagViewState();
- if (!isMoved && !isUp && state == ViewDragHelper.STATE_IDLE){
- isExecLongClick = true;
- mOnTagClickListener.onTagLongClick((int) getTag(), getText());
+ if (!isMoved && !isUp){
+ int state = ((TagContainerLayout)getParent()).getTagViewState();
+ if (state == ViewDragHelper.STATE_IDLE){
+ isExecLongClick = true;
+ mOnTagClickListener.onTagLongClick((int) getTag(), getText());
+ }
}
}
};
public TagView(Context context, String text){
super(context);
- init(text);
+ init(context, text);
+ }
+
+ public TagView(Context context, String text, int defaultImageID){
+ super(context);
+ init(context, text);
+ mBitmapImage = BitmapFactory.decodeResource(getResources(), defaultImageID);
}
- private void init(String text){
+ private void init(Context context, String text){
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRipplePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRipplePaint.setStyle(Paint.Style.FILL);
mRectF = new RectF();
- mTextBound = new Rect();
- mOriginText = text;
+ mPath = new Path();
+ mOriginText = text == null ? "" : text;
+ mMoveSlop = (int) dp2px(context, mMoveSlop);
+ mSlopThreshold = (int) dp2px(context, mSlopThreshold);
}
private void onDealText(){
if(!TextUtils.isEmpty(mOriginText)) {
mAbstractText = mOriginText.length() <= mTagMaxLength ? mOriginText
: mOriginText.substring(0, mTagMaxLength - 3) + "...";
+ }else {
+ mAbstractText = "";
}
+ mPaint.setTypeface(mTypeface);
mPaint.setTextSize(mTextSize);
- mPaint.getTextBounds(mAbstractText, 0, mAbstractText.length(), mTextBound);
+ final Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
+ fontH = fontMetrics.descent - fontMetrics.ascent;
+ if (mTextDirection == View.TEXT_DIRECTION_RTL){
+ fontW = 0;
+ for (char c : mAbstractText.toCharArray()) {
+ String sc = String.valueOf(c);
+ fontW += mPaint.measureText(sc);
+ }
+ }else {
+ fontW = mPaint.measureText(mAbstractText);
+ }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- setMeasuredDimension(mHorizontalPadding * 2 + mTextBound.width(),
- mVerticalPadding * 2 + mTextBound.height());
+ int height = mVerticalPadding * 2 + (int) fontH;
+ int width = mHorizontalPadding * 2 + (int) fontW + (isEnableCross() ? height : 0) + (isEnableImage() ? height : 0);
+ mCrossAreaWidth = Math.min(Math.max(mCrossAreaWidth, height), width);
+ setMeasuredDimension(width, height);
}
@Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- mRectF.set(canvas.getClipBounds().left + mBorderWidth,
- canvas.getClipBounds().top + mBorderWidth,
- canvas.getClipBounds().right - mBorderWidth,
- canvas.getClipBounds().bottom - mBorderWidth);
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mRectF.set(mBorderWidth, mBorderWidth, w - mBorderWidth, h - mBorderWidth);
+ }
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // draw background
mPaint.setStyle(Paint.Style.FILL);
- mPaint.setColor(mBackgroundColor);
+ mPaint.setColor(getIsViewSelected() ? mSelectedBackgroundColor : mBackgroundColor);
canvas.drawRoundRect(mRectF, mBorderRadius, mBorderRadius, mPaint);
+ // draw border
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setColor(mBorderColor);
canvas.drawRoundRect(mRectF, mBorderRadius, mBorderRadius, mPaint);
+ // draw ripple for TagView
+ drawRipple(canvas);
+
+ // draw text
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mTextColor);
- canvas.drawText(mAbstractText, getWidth() / 2 - mTextBound.width() / 2,
- getHeight() / 2 + mTextBound.height() / 2, mPaint);
+
+ if (mTextDirection == View.TEXT_DIRECTION_RTL) {
+ if (mTagSupportLettersRTL){
+ float tmpX = (isEnableCross() ? getWidth() + getHeight() : getWidth()) / 2
+ + fontW / 2;
+ for (char c : mAbstractText.toCharArray()) {
+ String sc = String.valueOf(c);
+ tmpX -= mPaint.measureText(sc);
+ canvas.drawText(sc, tmpX, getHeight() / 2 + fontH / 2 - bdDistance, mPaint);
+ }
+ }else {
+ canvas.drawText(mAbstractText,
+ (isEnableCross() ? getWidth() + fontW : getWidth()) / 2 - fontW / 2,
+ getHeight() / 2 + fontH / 2 - bdDistance, mPaint);
+ }
+ } else {
+ canvas.drawText(mAbstractText,
+ (isEnableCross() ? getWidth() - getHeight() : getWidth()) / 2 - fontW / 2 + (isEnableImage() ? getHeight() / 2 : 0),
+ getHeight() / 2 + fontH / 2 - bdDistance, mPaint);
+ }
+
+ // draw cross
+ drawCross(canvas);
+
+ // draw image
+ drawImage(canvas);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (isViewClickable){
+ int y = (int) event.getY();
+ int x = (int) event.getX();
+ int action = event.getAction();
+ switch (action){
+ case MotionEvent.ACTION_DOWN:
+ if (getParent() != null) {
+ getParent().requestDisallowInterceptTouchEvent(true);
+ }
+ mLastY = y;
+ mLastX = x;
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (!isViewSelected && (Math.abs(mLastY - y) > mSlopThreshold
+ || Math.abs(mLastX - x) > mSlopThreshold)){
+ if (getParent() != null) {
+ getParent().requestDisallowInterceptTouchEvent(false);
+ }
+ isMoved = true;
+ return false;
+ }
+ break;
+ }
+ }
+ return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (isViewClickable && mOnTagClickListener != null){
+ int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mRippleRadius = 0.0f;
+ mTouchX = event.getX();
+ mTouchY = event.getY();
+ splashRipple();
+ }
+ if (isEnableCross() && isClickCrossArea(event) && mOnTagClickListener != null){
+ if (action == MotionEvent.ACTION_UP) {
+ mOnTagClickListener.onTagCrossClick((int) getTag());
+ }
+ return true;
+ }else if (isViewClickable && mOnTagClickListener != null){
int x = (int) event.getX();
int y = (int) event.getY();
- int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
mLastY = y;
@@ -151,12 +329,15 @@ public boolean onTouchEvent(MotionEvent event) {
}
if (Math.abs(mLastX - x) > mMoveSlop || Math.abs(mLastY - y) > mMoveSlop){
isMoved = true;
+ if (isViewSelected){
+ mOnTagClickListener.onSelectedTagDrag((int) getTag(), getText());
+ }
}
break;
case MotionEvent.ACTION_UP:
isUp = true;
- if (!isExecLongClick) {
+ if (!isExecLongClick && !isMoved) {
mOnTagClickListener.onTagClick((int) getTag(), getText());
}
break;
@@ -166,6 +347,107 @@ public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
+ private boolean isClickCrossArea(MotionEvent event){
+ if (mTextDirection == View.TEXT_DIRECTION_RTL){
+ return event.getX() <= mCrossAreaWidth;
+ }
+ return event.getX() >= getWidth() - mCrossAreaWidth;
+ }
+
+ private void drawImage(Canvas canvas){
+ if (isEnableImage()) {
+ Bitmap scaledImageBitmap = Bitmap.createScaledBitmap(mBitmapImage, Math.round(getHeight() - mBorderWidth), Math.round(getHeight() - mBorderWidth), false);
+
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setShader(new BitmapShader(scaledImageBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ RectF rect = new RectF(mBorderWidth, mBorderWidth, getHeight() - mBorderWidth, getHeight() - mBorderWidth);
+ canvas.drawRoundRect(rect, rect.height()/2, rect.height()/2, paint);
+ }
+ }
+
+ private void drawCross(Canvas canvas){
+ if (isEnableCross()){
+ mCrossAreaPadding = mCrossAreaPadding > getHeight() / 2 ? getHeight() / 2 :
+ mCrossAreaPadding;
+ int ltX, ltY, rbX, rbY, lbX, lbY, rtX, rtY;
+ ltX = mTextDirection == View.TEXT_DIRECTION_RTL ? (int)(mCrossAreaPadding) :
+ (int)(getWidth() - getHeight() + mCrossAreaPadding);
+ ltY = mTextDirection == View.TEXT_DIRECTION_RTL ? (int)(mCrossAreaPadding) :
+ (int)(mCrossAreaPadding);
+ lbX = mTextDirection == View.TEXT_DIRECTION_RTL ? (int)(mCrossAreaPadding) :
+ (int)(getWidth() - getHeight() + mCrossAreaPadding);
+ lbY = mTextDirection == View.TEXT_DIRECTION_RTL ?
+ (int)(getHeight() - mCrossAreaPadding) : (int)(getHeight() - mCrossAreaPadding);
+ rtX = mTextDirection == View.TEXT_DIRECTION_RTL ?
+ (int)(getHeight() - mCrossAreaPadding) : (int)(getWidth() - mCrossAreaPadding);
+ rtY = mTextDirection == View.TEXT_DIRECTION_RTL ? (int)(mCrossAreaPadding) :
+ (int)(mCrossAreaPadding);
+ rbX = mTextDirection == View.TEXT_DIRECTION_RTL ?
+ (int)(getHeight() - mCrossAreaPadding) : (int)(getWidth() - mCrossAreaPadding);
+ rbY = mTextDirection == View.TEXT_DIRECTION_RTL ?
+ (int)(getHeight() - mCrossAreaPadding) : (int)(getHeight() - mCrossAreaPadding);
+
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setColor(mCrossColor);
+ mPaint.setStrokeWidth(mCrossLineWidth);
+ canvas.drawLine(ltX, ltY, rbX, rbY, mPaint);
+ canvas.drawLine(lbX, lbY, rtX, rtY, mPaint);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private void drawRipple(Canvas canvas){
+ if (isViewClickable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB &&
+ canvas != null && !unSupportedClipPath){
+
+ // Disable hardware acceleration for 'Canvas.clipPath()' when running on API from 11 to 17
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
+ }
+ try {
+ canvas.save();
+ mPath.reset();
+
+ canvas.clipPath(mPath);
+ mPath.addRoundRect(mRectF, mBorderRadius, mBorderRadius, Path.Direction.CCW);
+
+// bug: https://github.com/whilu/AndroidTagView/issues/88
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ canvas.clipPath(mPath);
+ } else {
+ canvas.clipPath(mPath, Region.Op.REPLACE);
+ }
+
+ canvas.drawCircle(mTouchX, mTouchY, mRippleRadius, mRipplePaint);
+ canvas.restore();
+ }catch (UnsupportedOperationException e){
+ unSupportedClipPath = true;
+ }
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private void splashRipple(){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && mTouchX > 0 && mTouchY > 0){
+ mRipplePaint.setColor(mRippleColor);
+ mRipplePaint.setAlpha(mRippleAlpha);
+ final float maxDis = Math.max(Math.max(Math.max(mTouchX, mTouchY),
+ Math.abs(getMeasuredWidth() - mTouchX)), Math.abs(getMeasuredHeight() - mTouchY));
+
+ mRippleValueAnimator = ValueAnimator.ofFloat(0.0f, maxDis).setDuration(mRippleDuration);
+ mRippleValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float animValue = (float) animation.getAnimatedValue();
+ mRippleRadius = animValue >= maxDis ? 0 : animValue;
+ postInvalidate();
+ }
+ });
+ mRippleValueAnimator.start();
+ }
+ }
+
public String getText(){
return mOriginText;
}
@@ -174,6 +456,10 @@ public boolean getIsViewClickable(){
return isViewClickable;
}
+ public boolean getIsViewSelected(){
+ return isViewSelected;
+ }
+
public void setTagMaxLength(int maxLength){
this.mTagMaxLength = maxLength;
onDealText();
@@ -183,10 +469,22 @@ public void setOnTagClickListener(OnTagClickListener listener){
this.mOnTagClickListener = listener;
}
+ public int getTagBackgroundColor(){
+ return mBackgroundColor;
+ }
+
+ public int getTagSelectedBackgroundColor(){
+ return mSelectedBackgroundColor;
+ }
+
public void setTagBackgroundColor(int color){
this.mBackgroundColor = color;
}
+ public void setTagSelectedBackgroundColor(int color){
+ this.mSelectedBackgroundColor = color;
+ }
+
public void setTagBorderColor(int color){
this.mBorderColor = color;
}
@@ -220,8 +518,113 @@ public void setIsViewClickable(boolean clickable) {
this.isViewClickable = clickable;
}
+ public void setImage(Bitmap newImage) {
+ this.mBitmapImage = newImage;
+ this.invalidate();
+ }
+
+ public void setIsViewSelectable(boolean viewSelectable) {
+ isViewSelectable = viewSelectable;
+ }
+
+ //TODO change background color
+ public void selectView() {
+ if (isViewSelectable && !getIsViewSelected()) {
+ this.isViewSelected = true;
+ postInvalidate();
+ }
+ }
+
+ public void deselectView() {
+ if (isViewSelectable && getIsViewSelected()) {
+ this.isViewSelected = false;
+ postInvalidate();
+ }
+ }
+
public interface OnTagClickListener{
void onTagClick(int position, String text);
void onTagLongClick(int position, String text);
+ void onSelectedTagDrag(int position, String text);
+ void onTagCrossClick(int position);
+ }
+
+ public int getTextDirection() {
+ return mTextDirection;
+ }
+
+ public void setTextDirection(int textDirection) {
+ this.mTextDirection = textDirection;
+ }
+
+ public void setTypeface(Typeface typeface) {
+ this.mTypeface = typeface;
+ onDealText();
+ }
+
+ public void setRippleAlpha(int mRippleAlpha) {
+ this.mRippleAlpha = mRippleAlpha;
+ }
+
+ public void setRippleColor(int mRippleColor) {
+ this.mRippleColor = mRippleColor;
+ }
+
+ public void setRippleDuration(int mRippleDuration) {
+ this.mRippleDuration = mRippleDuration;
+ }
+
+ public void setBdDistance(float bdDistance) {
+ this.bdDistance = bdDistance;
+ }
+
+ public boolean isEnableImage() { return mBitmapImage != null && mTextDirection != View.TEXT_DIRECTION_RTL; }
+
+ public boolean isEnableCross() {
+ return mEnableCross;
+ }
+
+ public void setEnableCross(boolean mEnableCross) {
+ this.mEnableCross = mEnableCross;
+ }
+
+ public float getCrossAreaWidth() {
+ return mCrossAreaWidth;
+ }
+
+ public void setCrossAreaWidth(float mCrossAreaWidth) {
+ this.mCrossAreaWidth = mCrossAreaWidth;
+ }
+
+ public float getCrossLineWidth() {
+ return mCrossLineWidth;
+ }
+
+ public void setCrossLineWidth(float mCrossLineWidth) {
+ this.mCrossLineWidth = mCrossLineWidth;
+ }
+
+ public float getCrossAreaPadding() {
+ return mCrossAreaPadding;
+ }
+
+ public void setCrossAreaPadding(float mCrossAreaPadding) {
+ this.mCrossAreaPadding = mCrossAreaPadding;
+ }
+
+ public int getCrossColor() {
+ return mCrossColor;
+ }
+
+ public void setCrossColor(int mCrossColor) {
+ this.mCrossColor = mCrossColor;
+ }
+
+ public boolean isTagSupportLettersRTL() {
+ return mTagSupportLettersRTL;
+ }
+
+ public void setTagSupportLettersRTL(boolean mTagSupportLettersRTL) {
+ this.mTagSupportLettersRTL = mTagSupportLettersRTL;
}
}
diff --git a/androidtagview/src/main/java/co/lujun/androidtagview/Utils.java b/androidtagview/src/main/java/co/lujun/androidtagview/Utils.java
new file mode 100644
index 0000000..c2b68de
--- /dev/null
+++ b/androidtagview/src/main/java/co/lujun/androidtagview/Utils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 lujun
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package co.lujun.androidtagview;
+
+import android.content.Context;
+import android.graphics.Color;
+
+/**
+ * Author: lujun(http://blog.lujun.co)
+ * Date: 2016-12-7 21:53
+ */
+
+public class Utils {
+
+ public static float dp2px(Context context, float dp) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return dp * scale + 0.5f;
+ }
+
+ public static float sp2px(Context context, float sp) {
+ final float scale = context.getResources().getDisplayMetrics().scaledDensity;
+ return sp * scale;
+ }
+
+ /**
+ * If the color is Dark, make it lighter and vice versa
+ *
+ * @param color in int,
+ * @param factor The factor greater than 0.0 and smaller than 1.0
+ * @return int
+ */
+ public static int manipulateColorBrightness(int color, float factor) {
+ int a = Color.alpha(color);
+ int r = Color.red(color);
+ int g = Color.green(color);
+ int b = Color.blue(color);
+// if (r + b + g < 128 * 3) factor = 1 / factor;// check if the color is bright or dark
+// r = Math.round(r * factor);
+// b = Math.round(b * factor);
+// g = Math.round(g * factor);
+ if (r > 127) r = 255 - Math.round((255 - r) * factor);
+ if (g > 127) g = 255 - Math.round((255 - g) * factor);
+ if (b > 127) b = 255 - Math.round((255 - b) * factor);
+
+ return Color.argb(a,
+ Math.min(r, 255),
+ Math.min(g, 255),
+ Math.min(b, 255)
+ );
+ }
+}
diff --git a/androidtagview/src/main/res/values/attrs.xml b/androidtagview/src/main/res/values/attrs.xml
index 3004a21..098f9a9 100644
--- a/androidtagview/src/main/res/values/attrs.xml
+++ b/androidtagview/src/main/res/values/attrs.xml
@@ -1,31 +1,55 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/androidtagview/src/main/res/values/strings.xml b/androidtagview/src/main/res/values/strings.xml
index 7231729..1ed655f 100644
--- a/androidtagview/src/main/res/values/strings.xml
+++ b/androidtagview/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
-
- AndroidTagView
-
+
+ AndroidTagView
+
diff --git a/androidtagview/src/test/java/co/lujun/androidtagview/ExampleUnitTest.java b/androidtagview/src/test/java/co/lujun/androidtagview/ExampleUnitTest.java
index 5218bbd..b45bba8 100644
--- a/androidtagview/src/test/java/co/lujun/androidtagview/ExampleUnitTest.java
+++ b/androidtagview/src/test/java/co/lujun/androidtagview/ExampleUnitTest.java
@@ -1,15 +1,15 @@
-package co.lujun.androidtagview;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * To work on unit tests, switch the Test Artifact in the Build Variants view.
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
+package co.lujun.androidtagview;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 51dd7a4..a115e91 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,25 +1,27 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:1.5.0'
- classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
- classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- jcenter()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ google()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ google()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
index 1d3591c..a565221 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,17 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
+## Project-wide Gradle settings.
+#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
-
+#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
+#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# org.gradle.parallel=true
+#Sun Jan 24 18:50:31 CST 2016
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f23df6e..1be1c62 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Oct 21 11:34:03 PDT 2015
+#Thu Jun 14 09:04:10 ICT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282..aec9973 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,90 +1,90 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/sample/.gitignore b/sample/.gitignore
index fbe08b1..6c9b4c3 100755
--- a/sample/.gitignore
+++ b/sample/.gitignore
@@ -1,46 +1,46 @@
-.gradle
-/local.properties
-/.idea/workspace.xml
-.DS_Store
-/build
-# built application files
-*.ap_
-
-# files for the dex VM
-*.dex
-
-# Java class files
-*.class
-.DS_Store
-
-# generated files
-bin/
-gen/
-Wiki/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Eclipse project files
-.classpath
-.project
-.settings/
-
-# Proguard folder generated by Eclipse
-proguard/
-
-#Android Studio
-build/
-src/androidTest/
-
-# Intellij project files
-*.iml
-*.ipr
-*.iws
-.idea/
-
-#gradle
-.gradle/
-.idea
-app/build
+.gradle
+/local.properties
+/.idea/workspace.xml
+.DS_Store
+/build
+# built application files
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+.DS_Store
+
+# generated files
+bin/
+gen/
+Wiki/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Eclipse project files
+.classpath
+.project
+.settings/
+
+# Proguard folder generated by Eclipse
+proguard/
+
+#Android Studio
+build/
+src/androidTest/
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#gradle
+.gradle/
+.idea
+app/build
app/src/androidTest
\ No newline at end of file
diff --git a/sample/build.gradle b/sample/build.gradle
index 2dcb20a..db7ac6a 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,28 +1,33 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 23
- buildToolsVersion "22.0.1"
-
- defaultConfig {
- applicationId "co.lujun.sample"
- minSdkVersion 9
- targetSdkVersion 23
- versionCode 1
- versionName "1.0"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile project(':androidtagview')
- testCompile 'junit:junit:4.12'
- compile 'com.android.support:appcompat-v7:23.1.1'
- compile 'com.android.support:design:23.1.1'
-}
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ applicationId "co.lujun.sample"
+ minSdkVersion 14
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+repositories {
+ mavenCentral()
+ maven { url 'https://maven.google.com' }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation project(':androidtagview')
+ testImplementation 'junit:junit:4.12'
+ implementation 'androidx.appcompat:appcompat:1.0.1'
+ implementation 'com.google.android.material:material:1.0.0'
+ implementation 'com.github.bumptech.glide:glide:4.1.1'
+}
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
index 700c8df..6828981 100644
--- a/sample/proguard-rules.pro
+++ b/sample/proguard-rules.pro
@@ -1,17 +1,17 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in F:\code\android\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in F:\code\android\adt-bundle-windows-x86_64-20140702\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/sample/sample-release.apk b/sample/sample-release.apk
index bb703b2..4676aae 100644
Binary files a/sample/sample-release.apk and b/sample/sample-release.apk differ
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index 9ef8ab9..e56e5f9 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,23 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/main/assets/iran_sans.ttf b/sample/src/main/assets/iran_sans.ttf
new file mode 100644
index 0000000..91885af
Binary files /dev/null and b/sample/src/main/assets/iran_sans.ttf differ
diff --git a/sample/src/main/java/co/lujun/sample/MainActivity.java b/sample/src/main/java/co/lujun/sample/MainActivity.java
index 8388ae3..9399ad4 100644
--- a/sample/src/main/java/co/lujun/sample/MainActivity.java
+++ b/sample/src/main/java/co/lujun/sample/MainActivity.java
@@ -1,24 +1,37 @@
package co.lujun.sample;
import android.app.AlertDialog;
+import android.content.ClipData;
+import android.content.Context;
import android.content.DialogInterface;
+import android.graphics.Bitmap;
+import android.graphics.Color;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.request.target.SimpleTarget;
+import com.bumptech.glide.request.transition.Transition;
+
import java.util.ArrayList;
import java.util.List;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.RecyclerView;
import co.lujun.androidtagview.TagContainerLayout;
import co.lujun.androidtagview.TagView;
public class MainActivity extends AppCompatActivity {
- private TagContainerLayout mTagContainerLayout1, mTagContainerLayout2, mTagContainerLayout3;
+ private TagContainerLayout mTagContainerLayout1, mTagContainerLayout2,
+ mTagContainerLayout3, mTagContainerLayout4, mTagcontainerLayout5;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -29,15 +42,13 @@ protected void onCreate(Bundle savedInstanceState) {
List list1 = new ArrayList();
list1.add("Java");
- list1.add("C/C++");
+ list1.add("C++");
list1.add("Python");
list1.add("Swift");
list1.add("你好,这是一个TAG。你好,这是一个TAG。你好,这是一个TAG。你好,这是一个TAG。");
list1.add("PHP");
- list1.add("Python");
list1.add("JavaScript");
list1.add("Html");
- list1.add("Hello, this is a TAG example.");
list1.add("Welcome to use AndroidTagView!");
List list2 = new ArrayList();
@@ -50,17 +61,26 @@ protected void onCreate(Bundle savedInstanceState) {
list2.add("UK");
list2.add("Germany");
list2.add("Niger");
- list2.add("Singapore");
list2.add("Poland");
list2.add("Norway");
list2.add("Uruguay");
list2.add("Brazil");
- String[] list3 = new String[]{"Adele", "Avril Lavigne", "Taylor Swift", "Rihanna", " Whitney Houston"};
+ String[] list3 = new String[]{"Persian", "波斯语", "فارسی", "Hello", "你好", "سلام"};
+ String[] list4 = new String[]{"Adele", "Whitney Houston"};
+
+ List list5 = new ArrayList();
+ list5.add("Custom Red Color");
+ list5.add("Custom Blue Color");
+
mTagContainerLayout1 = (TagContainerLayout) findViewById(R.id.tagcontainerLayout1);
mTagContainerLayout2 = (TagContainerLayout) findViewById(R.id.tagcontainerLayout2);
mTagContainerLayout3 = (TagContainerLayout) findViewById(R.id.tagcontainerLayout3);
+ mTagContainerLayout4 = (TagContainerLayout) findViewById(R.id.tagcontainerLayout4);
+ mTagcontainerLayout5 = (TagContainerLayout) findViewById(R.id.tagcontainerLayout5);
+
+ mTagContainerLayout1.setDefaultImageDrawableID(R.drawable.yellow_avatar);
// Set custom click listener
mTagContainerLayout1.setOnTagClickListener(new TagView.OnTagClickListener() {
@@ -78,7 +98,9 @@ public void onTagLongClick(final int position, String text) {
.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- mTagContainerLayout1.removeTag(position);
+ if (position < mTagContainerLayout1.getChildCount()) {
+ mTagContainerLayout1.removeTag(position);
+ }
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@@ -90,6 +112,55 @@ public void onClick(DialogInterface dialog, int which) {
.create();
dialog.show();
}
+
+ @Override
+ public void onSelectedTagDrag(int position, String text) {}
+
+ @Override
+ public void onTagCrossClick(int position) {
+// mTagContainerLayout1.removeTag(position);
+ Toast.makeText(MainActivity.this, "Click TagView cross! position = " + position,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ mTagContainerLayout3.setOnTagClickListener(new TagView.OnTagClickListener() {
+ @Override
+ public void onTagClick(int position, String text) {
+ List selectedPositions = mTagContainerLayout3.getSelectedTagViewPositions();
+ //deselect all tags when click on an unselected tag. Otherwise show toast.
+ if (selectedPositions.isEmpty() || selectedPositions.contains(position)) {
+ Toast.makeText(MainActivity.this, "click-position:" + position + ", text:" + text,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ //deselect all tags
+ for (int i : selectedPositions) {
+ mTagContainerLayout3.deselectTagView(i);
+ }
+ }
+
+ }
+
+ @Override
+ public void onTagLongClick(final int position, String text) {
+ mTagContainerLayout3.toggleSelectTagView(position);
+
+ List selectedPositions = mTagContainerLayout3.getSelectedTagViewPositions();
+ Toast.makeText(MainActivity.this, "selected-positions:" + selectedPositions.toString(),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onSelectedTagDrag(int position, String text) {
+ ClipData clip = ClipData.newPlainText("Text", text);
+ View view = mTagContainerLayout3.getTagView(position);
+ View.DragShadowBuilder shadow = new View.DragShadowBuilder(view);
+ view.startDrag(clip, shadow, Boolean.TRUE, 0);
+ }
+
+ @Override
+ public void onTagCrossClick(int position) {
+ }
});
// Custom settings
@@ -101,12 +172,31 @@ public void onClick(DialogInterface dialog, int which) {
// If you want to use your colors for TagView, remember set the theme with ColorFactory.NONE
// mTagContainerLayout1.setTheme(ColorFactory.NONE);
// mTagContainerLayout1.setTagBackgroundColor(Color.TRANSPARENT);
+// mTagContainerLayout1.setTagTextDirection(View.TEXT_DIRECTION_RTL);
+
+ // support typeface
+// Typeface typeface = Typeface.createFromAsset(getAssets(), "iran_sans.ttf");
+// mTagContainerLayout.setTagTypeface(typeface);
+
+ // adjust distance baseline and descent
+// mTagContainerLayout.setTagBdDistance(4.6f);
// After you set your own attributes for TagView, then set tag(s) or add tag(s)
mTagContainerLayout1.setTags(list1);
+ loadImages(list1);
mTagContainerLayout2.setTags(list2);
mTagContainerLayout3.setTags(list3);
+ mTagContainerLayout4.setTags(list4);
+
+ List colors = new ArrayList();
+ //int[]color = {backgroundColor, tagBorderColor, tagTextColor, tagSelectedBackgroundColor}
+ int[] col1 = {Color.parseColor("#ff0000"), Color.parseColor("#000000"), Color.parseColor("#ffffff"), Color.parseColor("#999999")};
+ int[] col2 = {Color.parseColor("#0000ff"), Color.parseColor("#000000"), Color.parseColor("#ffffff"), Color.parseColor("#999999")};
+ colors.add(col1);
+ colors.add(col2);
+
+ mTagcontainerLayout5.setTags(list5, colors);
final EditText text = (EditText) findViewById(R.id.text_tag);
Button btnAddTag = (Button) findViewById(R.id.btn_add_tag);
btnAddTag.setOnClickListener(new View.OnClickListener() {
@@ -117,5 +207,101 @@ public void onClick(View v) {
// mTagContainerLayout1.addTag(text.getText().toString(), 4);
}
});
+
+// mTagContainerLayout1.setMaxLines(1);
+
+
+ // test in RecyclerView
+// RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
+// recyclerView.setVisibility(View.VISIBLE);
+// TagRecyclerViewAdapter adapter = new TagRecyclerViewAdapter(this, list3);
+// adapter.setOnClickListener(new View.OnClickListener() {
+// @Override
+// public void onClick(View v) {
+// Toast.makeText(MainActivity.this, "Click on TagContainerLayout", Toast.LENGTH_SHORT).show();
+// }
+// });
+// recyclerView.setAdapter(adapter);
+ }
+
+ private void loadImages(List list) {
+ String[] avatars = new String[]{"https://forums.oneplus.com/data/avatars/m/231/231279.jpg",
+ "https://d1marr3m5x4iac.cloudfront.net/images/block/movies/17214/17214_aa.jpg",
+ "https://lh3.googleusercontent.com/-KSI1bJ1aVS4/AAAAAAAAAAI/AAAAAAAAB9c/Vrgt6WyS5OU/il/photo.jpg"};
+
+ for (int i=0; i() {
+ @Override
+ public void onResourceReady(Bitmap resource, Transition super Bitmap> transition) {
+ mTagContainerLayout1.getTagView(index).setImage(resource);
+ }
+ });
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ }
+
+ public class TagRecyclerViewAdapter
+ extends RecyclerView.Adapter {
+
+ private Context mContext;
+ private String[] mData;
+ private View.OnClickListener mOnClickListener;
+
+ public TagRecyclerViewAdapter(Context context, String[] data) {
+ this.mContext = context;
+ this.mData = data;
+ }
+
+ @Override
+ public int getItemCount() {
+ return 10;
+ }
+
+ @Override
+ public TagViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new TagViewHolder(LayoutInflater.from(mContext)
+ .inflate(R.layout.view_recyclerview_item, parent, false), mOnClickListener);
+ }
+
+ @Override
+ public void onBindViewHolder(TagViewHolder holder, int position) {
+ holder.tagContainerLayout.setTags(mData);
+ holder.button.setOnClickListener(mOnClickListener);
+ }
+
+ public void setOnClickListener(View.OnClickListener listener) {
+ this.mOnClickListener = listener;
+ }
+
+ class TagViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+ TagContainerLayout tagContainerLayout;
+ View.OnClickListener clickListener;
+ Button button;
+
+ public TagViewHolder(View v, View.OnClickListener listener) {
+ super(v);
+ this.clickListener = listener;
+ tagContainerLayout = (TagContainerLayout) v.findViewById(R.id.tagcontainerLayout);
+ button = (Button) v.findViewById(R.id.button);
+// v.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (clickListener != null) {
+ clickListener.onClick(v);
+ }
+ }
+ }
}
}
diff --git a/sample/src/main/res/drawable/yellow_avatar.png b/sample/src/main/res/drawable/yellow_avatar.png
new file mode 100644
index 0000000..605a8e7
Binary files /dev/null and b/sample/src/main/res/drawable/yellow_avatar.png differ
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
index 3f0ab6f..12311a8 100644
--- a/sample/src/main/res/layout/activity_main.xml
+++ b/sample/src/main/res/layout/activity_main.xml
@@ -1,26 +1,26 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/main/res/layout/content_main.xml b/sample/src/main/res/layout/content_main.xml
index bfd610d..bb89e68 100644
--- a/sample/src/main/res/layout/content_main.xml
+++ b/sample/src/main/res/layout/content_main.xml
@@ -1,86 +1,138 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/main/res/layout/view_recyclerview_item.xml b/sample/src/main/res/layout/view_recyclerview_item.xml
new file mode 100644
index 0000000..b499e4c
--- /dev/null
+++ b/sample/src/main/res/layout/view_recyclerview_item.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/menu/menu_main.xml b/sample/src/main/res/menu/menu_main.xml
index 57ce2bd..cd4df03 100644
--- a/sample/src/main/res/menu/menu_main.xml
+++ b/sample/src/main/res/menu/menu_main.xml
@@ -1,10 +1,10 @@
-
+
diff --git a/sample/src/main/res/values-v21/styles.xml b/sample/src/main/res/values-v21/styles.xml
index 251fb9f..fe2d12a 100644
--- a/sample/src/main/res/values-v21/styles.xml
+++ b/sample/src/main/res/values-v21/styles.xml
@@ -1,9 +1,9 @@
->
-
-
-
+>
+
+
+
diff --git a/sample/src/main/res/values-w820dp/dimens.xml b/sample/src/main/res/values-w820dp/dimens.xml
index 63fc816..62df187 100644
--- a/sample/src/main/res/values-w820dp/dimens.xml
+++ b/sample/src/main/res/values-w820dp/dimens.xml
@@ -1,6 +1,6 @@
-
-
- 64dp
-
+
+
+ 64dp
+
diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml
index 3ab3e9c..2a12c47 100644
--- a/sample/src/main/res/values/colors.xml
+++ b/sample/src/main/res/values/colors.xml
@@ -1,6 +1,6 @@
-
-
- #3F51B5
- #303F9F
- #FF4081
-
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/sample/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml
index 812cb7b..f31eb24 100644
--- a/sample/src/main/res/values/dimens.xml
+++ b/sample/src/main/res/values/dimens.xml
@@ -1,6 +1,6 @@
-
-
- 16dp
- 16dp
- 16dp
-
+
+
+ 16dp
+ 16dp
+ 16dp
+
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
index 893f557..831d98c 100644
--- a/sample/src/main/res/values/strings.xml
+++ b/sample/src/main/res/values/strings.xml
@@ -1,4 +1,4 @@
-
- Sample
- Settings
-
+
+ Sample
+ Settings
+
diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml
index 545b9c6..80f09db 100644
--- a/sample/src/main/res/values/styles.xml
+++ b/sample/src/main/res/values/styles.xml
@@ -1,20 +1,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/test/java/co/lujun/sample/ExampleUnitTest.java b/sample/src/test/java/co/lujun/sample/ExampleUnitTest.java
index 330bb17..ceeb9fd 100644
--- a/sample/src/test/java/co/lujun/sample/ExampleUnitTest.java
+++ b/sample/src/test/java/co/lujun/sample/ExampleUnitTest.java
@@ -1,15 +1,15 @@
-package co.lujun.sample;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * To work on unit tests, switch the Test Artifact in the Build Variants view.
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
+package co.lujun.sample;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
}
\ No newline at end of file
diff --git a/screenshots/device-2016-01-05-184752.png b/screenshots/device-2016-01-05-184752.png
deleted file mode 100644
index 95a4efc..0000000
Binary files a/screenshots/device-2016-01-05-184752.png and /dev/null differ
diff --git a/screenshots/device-2016-01-16-233617.png b/screenshots/device-2016-01-16-233617.png
new file mode 100644
index 0000000..256b63a
Binary files /dev/null and b/screenshots/device-2016-01-16-233617.png differ
diff --git a/screenshots/device-2016-11-09-223523.png b/screenshots/device-2016-11-09-223523.png
new file mode 100644
index 0000000..3108f70
Binary files /dev/null and b/screenshots/device-2016-11-09-223523.png differ
diff --git a/settings.gradle b/settings.gradle
index d789252..7024ddb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':sample', ':androidtagview'
+include ':sample', ':androidtagview'