├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── dimens.xml │ │ │ ├── arrays.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ ├── drawable │ │ │ ├── image.png │ │ │ └── ic_edit_black_24dp.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ └── layout │ │ │ ├── activity_sample.xml │ │ │ └── fragment_bottom_sheet.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── vpaliy │ │ └── chipview │ │ ├── SampleActivity.java │ │ └── BottomSheet.java ├── proguard-rules.pro └── build.gradle ├── chips-lover ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── integers.xml │ │ │ ├── ids.xml │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ └── attrs.xml │ │ └── drawable │ │ │ └── ic_close.xml │ │ ├── java │ │ └── com │ │ │ └── vpaliy │ │ │ └── chips_lover │ │ │ ├── OnEndIconEventClick.java │ │ │ ├── OnFrontIconEventClick.java │ │ │ ├── ChipBuilder.java │ │ │ ├── ChipsLayout.java │ │ │ └── ChipView.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .idea ├── copyright │ └── profiles_settings.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── art ├── ezgif.com-video-to-gif(8).gif └── ezgif.com-video-to-gif(9).gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /chips-lover/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':chips-lover' 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ChipView 3 | 4 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /art/ezgif.com-video-to-gif(8).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/art/ezgif.com-video-to-gif(8).gif -------------------------------------------------------------------------------- /art/ezgif.com-video-to-gif(9).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/art/ezgif.com-video-to-gif(9).gif -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/drawable/image.png -------------------------------------------------------------------------------- /chips-lover/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | chips-lover 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-chip/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /chips-lover/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 300 4 | -------------------------------------------------------------------------------- /chips-lover/src/main/java/com/vpaliy/chips_lover/OnEndIconEventClick.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chips_lover; 2 | 3 | import android.view.View; 4 | 5 | public interface OnEndIconEventClick { 6 | void onClick(View view); 7 | } 8 | -------------------------------------------------------------------------------- /chips-lover/src/main/java/com/vpaliy/chips_lover/OnFrontIconEventClick.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chips_lover; 2 | 3 | 4 | import android.view.View; 5 | 6 | public interface OnFrontIconEventClick { 7 | void onClick(View view); 8 | } 9 | -------------------------------------------------------------------------------- /chips-lover/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jun 17 19:40:08 EDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /chips-lover/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8dp 4 | 5dp 5 | 16dp 6 | 400dp 7 | 8 | 8dp 9 | 16dp 10 | -------------------------------------------------------------------------------- /chips-lover/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32dp 4 | 32dp 5 | 24dp 6 | 12dp 7 | 8dp 8 | 4dp 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Background 5 | SelectedBackground 6 | Text 7 | SelectedText 8 | FrontIconColor 9 | RearIconColor 10 | SelectedRearIconColor 11 | SelectedFrontIconColor 12 | 13 | -------------------------------------------------------------------------------- /chips-lover/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #e0e0e0 4 | #424242 5 | #F5F5F5 6 | #F5F5F5 7 | #424242 8 | #F5F5F5 9 | #757575 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #FFF 8 | #565656 9 | #90A4AE 10 | #455a64 11 | #37474F 12 | #ff5722 13 | 14 | -------------------------------------------------------------------------------- /chips-lover/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/vpaliy/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /chips-lover/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/vpaliy/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.vpaliy.chipview" 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | vectorDrawables.useSupportLibrary = true 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile project(':chips-lover') 25 | compile 'com.github.QuadFlask:colorpicker:0.0.13' 26 | compile 'com.android.support:appcompat-v7:25.3.1' 27 | compile 'com.android.support:design:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | compile 'com.jakewharton:butterknife:8.6.0' 30 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' 31 | } 32 | -------------------------------------------------------------------------------- /chips-lover/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | 8 | 9 | defaultConfig { 10 | minSdkVersion 13 11 | targetSdkVersion 25 12 | versionCode 1 13 | versionName "1.0" 14 | vectorDrawables.useSupportLibrary = true 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 29 | exclude group: 'com.android.support', module: 'support-annotations' 30 | }) 31 | compile 'com.android.support:appcompat-v7:25.3.1' 32 | compile 'com.android.support:design:25.3.1' 33 | testCompile 'junit:junit:4.12' 34 | compile 'de.hdodenhof:circleimageview:2.1.0' 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/chipview/SampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chipview; 2 | 3 | import android.support.design.widget.FloatingActionButton; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import com.vpaliy.chips_lover.ChipBuilder; 10 | import com.vpaliy.chips_lover.ChipsLayout; 11 | import java.util.Arrays; 12 | 13 | import butterknife.BindView; 14 | import butterknife.ButterKnife; 15 | 16 | public class SampleActivity extends AppCompatActivity 17 | implements BottomSheet.OnUpdateChipsListener{ 18 | 19 | @BindView(R.id.chips) 20 | protected ChipsLayout chipsLayout; 21 | 22 | @BindView(R.id.fab) 23 | protected FloatingActionButton fab; 24 | 25 | @BindView(R.id.root) 26 | protected ViewGroup root; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_sample); 32 | ButterKnife.bind(this); 33 | chipsLayout.setTags(Arrays.asList("Ch.Dickson","E.Farmer","M.Phil","J. White","L. Frazier","Iliana Ho", 34 | "Hugo Horne","Cesar Quinn","Seth Pugh","Valentina Green","Ayla Carney","Kyleigh Steele")); 35 | } 36 | 37 | @Override 38 | public void onUpdate(ChipBuilder builder) { 39 | chipsLayout.updateChipColors(builder); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_sample.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 25 | 26 | 27 | 36 | 37 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /chips-lover/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_bottom_sheet.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | 28 | 42 | 43 | 51 | 52 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChipsLover 2 | 3 | [![](https://jitpack.io/v/vpaliyX/ChipsLover.svg)](https://jitpack.io/#vpaliyX/ChipsLover) 4 | 5 | This repository provides an implementation of a material chip. Chips represent complex entities in small blocks, such as a contact. Also it could be used to represent a category or a simple tag. 6 | 7 | I followed the guidelines from the [Material Design](https://material.io/guidelines/components/chips.html#) website during the implementation of this library. 8 | However, I have also added additional functionality such as a `ChipsLayout`, default animations, selections, color customization, text appearance, etc. 9 | 10 | Take a glance at the demo: 11 | 12 | ![](https://github.com/vpaliyX/ChipsLover/blob/master/art/ezgif.com-video-to-gif(8).gif) 13 | 14 | Also I've created the customization sheet in the sample, so go ahead and download the sample, and you'll get a chance to poke around in the library a little bit: 15 | 16 | ![](https://github.com/vpaliyX/ChipsLover/blob/master/art/ezgif.com-video-to-gif(9).gif) 17 | 18 | ## How to use? ## 19 | 20 | Check out all properties you can set to the `ChipView` as well as `ChipsLayout` [here](https://github.com/vpaliyX/ChipsLover/blob/master/chips-lover/src/main/res/values/attrs.xml). They should be intuitive, well, at least I tried to make them so; if it's not the case then I failed miserably. 21 | 22 | ### Note! ### 23 | If you are going to use the `ChipsLayout` view, you may find yourself in a situation where the chips have completely occupied the screen, so in this case you will not be able to scroll your layout. The solution is to wrap the `ChipsLayout` up into a `ScrollView` or `NestedScrollView` (if you're using a `CoordinatorLayout`). 24 | 25 | Check out these XML examples: 26 | 27 | ```XML 28 | 43 | 44 | ``` 45 | 46 | Another one: 47 | 48 | ```XML 49 | 63 | 64 | ``` 65 | 66 | ## How to download? ## 67 | 68 | ### Step 1 ### 69 | 70 | Add it in your root build.gradle at the end of repositories: 71 | 72 | ``` gradle 73 | allprojects { 74 | repositories { 75 | maven { url 'https://jitpack.io' } 76 | } 77 | } 78 | 79 | ``` 80 | ### Step 2 ### 81 | 82 | Add the dependency 83 | 84 | ``` gradle 85 | dependencies { 86 | compile 'com.github.vpaliyX:ChipsLover:v1.2' 87 | } 88 | 89 | ``` 90 | You're good to go! 91 | 92 | 93 | `````` 94 | MIT License 95 | 96 | Copyright (c) 2017 Vasyl Paliy 97 | 98 | Permission is hereby granted, free of charge, to any person obtaining a copy 99 | of this software and associated documentation files (the "Software"), to deal 100 | in the Software without restriction, including without limitation the rights 101 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 102 | copies of the Software, and to permit persons to whom the Software is 103 | furnished to do so, subject to the following conditions: 104 | 105 | The above copyright notice and this permission notice shall be included in all 106 | copies or substantial portions of the Software. 107 | 108 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 109 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 110 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 111 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 112 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 113 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 114 | SOFTWARE. 115 | `````` 116 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/chipview/BottomSheet.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chipview; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.design.widget.BottomSheetDialogFragment; 7 | import android.support.v4.content.ContextCompat; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import com.flask.colorpicker.ColorPickerView; 12 | import com.flask.colorpicker.OnColorChangedListener; 13 | import com.flask.colorpicker.slider.LightnessSlider; 14 | import com.vpaliy.chips_lover.ChipBuilder; 15 | import com.vpaliy.chips_lover.ChipView; 16 | import com.vpaliy.chips_lover.ChipsLayout; 17 | import java.util.List; 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | import butterknife.OnClick; 21 | 22 | public class BottomSheet extends BottomSheetDialogFragment { 23 | 24 | @BindView(R.id.chips_config) 25 | protected ChipsLayout chipsLayout; 26 | 27 | @BindView(R.id.lightness_bar) 28 | protected LightnessSlider lightnessSlider; 29 | 30 | @BindView(R.id.color_picker_view) 31 | protected ColorPickerView pickerView; 32 | 33 | private ChipBuilder builder; 34 | private int selection=-1; 35 | 36 | private OnUpdateChipsListener updateChipsListener; 37 | 38 | public BottomSheet setUpdateListener(OnUpdateChipsListener updateListener){ 39 | this.updateChipsListener=updateListener; 40 | return this; 41 | } 42 | 43 | public static BottomSheet newInstance(){ 44 | return new BottomSheet(); 45 | } 46 | 47 | @Nullable 48 | @Override 49 | public View onCreateView(LayoutInflater inflater, 50 | @Nullable ViewGroup container, 51 | @Nullable Bundle savedInstanceState) { 52 | setRetainInstance(true); 53 | View root=inflater.inflate(R.layout.fragment_bottom_sheet,container,false); 54 | ButterKnife.bind(this,root); 55 | return root; 56 | } 57 | 58 | @Override 59 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 60 | super.onViewCreated(view, savedInstanceState); 61 | if(view!=null){ 62 | lightnessSlider.setColorPicker(pickerView); 63 | final List chips=chipsLayout.getChips(); 64 | builder=ChipBuilder.create(getContext()); 65 | pickerView.addOnColorChangedListener(new OnColorChangedListener() { 66 | @Override 67 | public void onColorChanged(int color) { 68 | ChipView chipView=chips.get(selection); 69 | chipView.setSelectedBackgroundColor(color); 70 | chipView.setBackgroundColor(color); 71 | switch (selection){ 72 | case 0: 73 | builder.setBackgroundColor(color); 74 | break; 75 | case 1: 76 | builder.setSelectedBackgroundColor(color); 77 | break; 78 | case 2: 79 | builder.setTextColor(color); 80 | break; 81 | case 3: 82 | builder.setSelectedTextColor(color); 83 | break; 84 | case 4: 85 | builder.setFrontIconColor(color); 86 | break; 87 | case 5: 88 | builder.setEndIconColor(color); 89 | break; 90 | case 6: 91 | builder.setSelectedEndColor(color); 92 | break; 93 | case 7: 94 | builder.setSelectedFrontColor(color); 95 | break; 96 | } 97 | } 98 | }); 99 | selection=0; 100 | chips.get(selection).select(); 101 | final int color= ContextCompat.getColor(getContext(),R.color.colorSelectedChipBackground); 102 | chipsLayout.setClickListenerToAll(new View.OnClickListener() { 103 | @Override 104 | public void onClick(View view) { 105 | selection=chips.indexOf(view); 106 | ChipView chipView=chips.get(selection); 107 | if(chipView.getSelectedBackgroundColor()!=color) { 108 | pickerView.setColor(chipView.getSelectedBackgroundColor(), false); 109 | }else{ 110 | pickerView.setColor(Color.WHITE,false); 111 | } 112 | lightnessSlider.setColor(Color.WHITE); 113 | 114 | } 115 | }); 116 | } 117 | } 118 | 119 | @OnClick(R.id.submit_chip) 120 | public void submit(){ 121 | updateChipsListener.onUpdate(builder); 122 | } 123 | 124 | @Override 125 | public void onPause() { 126 | super.onPause(); 127 | updateChipsListener.onUpdate(builder); 128 | } 129 | 130 | public interface OnUpdateChipsListener{ 131 | void onUpdate(ChipBuilder builder); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /chips-lover/src/main/java/com/vpaliy/chips_lover/ChipBuilder.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chips_lover; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.drawable.Drawable; 6 | import android.support.v4.content.ContextCompat; 7 | 8 | public class ChipBuilder { 9 | 10 | String text; 11 | Drawable endIconDrawable; 12 | Drawable frontIconDrawable; 13 | int frontIconColor=-1; 14 | int endIconColor=-1; 15 | boolean selectable; 16 | boolean closeable; 17 | boolean isDefaultAnimation; 18 | int textStyle=-1; 19 | int backgroundColor; 20 | int textColor; 21 | int elevation; 22 | int selectedBackgroundColor; 23 | int selectedTextColor; 24 | int selectedEndColor; 25 | int selectedFrontColor; 26 | private Context context; 27 | 28 | private ChipBuilder(Context context, TypedArray array){ 29 | this(context); 30 | if(array!=null){ 31 | text=array.getString(R.styleable.ChipView_chip_text); 32 | backgroundColor=array.getColor(R.styleable.ChipView_chip_backgroundColor,backgroundColor); 33 | textColor=array.getColor(R.styleable.ChipView_chipTextColor,textColor); 34 | textStyle=array.getResourceId(R.styleable.ChipView_chipTextStyle,textStyle); 35 | endIconColor=array.getColor(R.styleable.ChipView_chipEndIconColor,endIconColor); 36 | frontIconColor=array.getColor(R.styleable.ChipView_chipFrontIconColor,frontIconColor); 37 | isDefaultAnimation=array.getBoolean(R.styleable.ChipView_chipDefaultAnimation,false); 38 | selectedEndColor=array.getColor(R.styleable.ChipView_chip_selectedEndColor,selectedEndColor); 39 | closeable=array.getBoolean(R.styleable.ChipView_chipCloseable,false); 40 | selectedFrontColor=array.getColor(R.styleable.ChipView_chip_selectedFrontColor,selectedFrontColor); 41 | selectable=array.getBoolean(R.styleable.ChipView_chipSelectable,false); 42 | elevation=(int)array.getDimension(R.styleable.ChipView_chip_elevation,0); 43 | selectedBackgroundColor=array.getColor(R.styleable.ChipView_chip_selectedBackgroundColor,selectedBackgroundColor); 44 | selectedTextColor=array.getColor(R.styleable.ChipView_chip_selectedTextColor, selectedTextColor); 45 | int icon=array.getResourceId(R.styleable.ChipView_chipFrontIcon,-1); 46 | if(icon!=-1) { 47 | frontIconDrawable = ContextCompat.getDrawable(context, icon); 48 | } 49 | icon=array.getResourceId(R.styleable.ChipView_chipEndIcon,-1); 50 | if(icon!=-1) { 51 | endIconDrawable = ContextCompat.getDrawable(context,icon); 52 | }else if(closeable){ 53 | endIconColor=ContextCompat.getColor(context,R.color.colorChipCloseInactive); 54 | endIconDrawable=ContextCompat.getDrawable(context,R.drawable.ic_close); 55 | } 56 | } 57 | } 58 | 59 | private void init(){ 60 | textColor=ContextCompat.getColor(context,R.color.colorChipText); 61 | textStyle=-1; 62 | backgroundColor=ContextCompat.getColor(context,R.color.colorChipBackground); 63 | endIconColor=-1; 64 | frontIconColor=-1; 65 | selectedEndColor=ContextCompat.getColor(context,R.color.colorSelectedEndIcon); 66 | selectedFrontColor=ContextCompat.getColor(context,R.color.colorSelectedFrontIcon); 67 | selectedBackgroundColor=ContextCompat.getColor(context,R.color.colorSelectedChipBackground); 68 | selectedTextColor=ContextCompat.getColor(context,R.color.colorChipTextSelected); 69 | } 70 | 71 | private ChipBuilder(Context context){ 72 | this.context=context; 73 | init(); 74 | } 75 | 76 | public ChipBuilder setTextStyle(int textStyle) { 77 | this.textStyle = textStyle; 78 | return this; 79 | } 80 | 81 | public ChipBuilder setTextColor(int textColor) { 82 | this.textColor = textColor; 83 | return this; 84 | } 85 | 86 | public ChipBuilder setBackgroundColor(int backgroundColor) { 87 | this.backgroundColor = backgroundColor; 88 | return this; 89 | } 90 | 91 | public ChipBuilder setCloseable(boolean closeable) { 92 | this.closeable = closeable; 93 | return this; 94 | } 95 | 96 | public ChipBuilder setDefaultAnimation(boolean defaultAnimation) { 97 | isDefaultAnimation = defaultAnimation; 98 | return this; 99 | } 100 | 101 | public ChipBuilder setEndIconColor(int endIconColor) { 102 | this.endIconColor = endIconColor; 103 | return this; 104 | } 105 | 106 | public ChipBuilder setEndIconDrawable(Drawable endIconDrawable) { 107 | this.endIconDrawable = endIconDrawable; 108 | return this; 109 | } 110 | 111 | public ChipBuilder setFrontIconColor(int frontIconColor) { 112 | this.frontIconColor = frontIconColor; 113 | return this; 114 | } 115 | 116 | public ChipBuilder setFrontIconDrawable(Drawable frontIconDrawable) { 117 | this.frontIconDrawable = frontIconDrawable; 118 | return this; 119 | } 120 | 121 | public ChipBuilder setSelectable(boolean selectable) { 122 | this.selectable = selectable; 123 | return this; 124 | } 125 | 126 | public ChipBuilder setSelectedBackgroundColor(int selectedBackgroundColor) { 127 | this.selectedBackgroundColor = selectedBackgroundColor; 128 | return this; 129 | } 130 | 131 | public ChipBuilder setSelectedEndColor(int selectedEndColor) { 132 | this.selectedEndColor = selectedEndColor; 133 | return this; 134 | } 135 | 136 | public ChipBuilder setSelectedFrontColor(int selectedFrontColor) { 137 | this.selectedFrontColor = selectedFrontColor; 138 | return this; 139 | } 140 | 141 | public ChipBuilder setSelectedTextColor(int selectedTextColor) { 142 | this.selectedTextColor = selectedTextColor; 143 | return this; 144 | } 145 | 146 | public ChipBuilder setText(String text) { 147 | this.text = text; 148 | return this; 149 | } 150 | 151 | public static ChipBuilder create(Context context,TypedArray array){ 152 | return new ChipBuilder(context,array); 153 | } 154 | 155 | public static ChipBuilder create(Context context){ 156 | return new ChipBuilder(context); 157 | } 158 | 159 | public ChipView build(){ 160 | return new ChipView(context,this ); 161 | } 162 | } -------------------------------------------------------------------------------- /chips-lover/src/main/java/com/vpaliy/chips_lover/ChipsLayout.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chips_lover; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.os.Build; 6 | import android.text.TextUtils; 7 | import android.transition.Transition; 8 | import android.transition.TransitionInflater; 9 | import android.transition.TransitionManager; 10 | import android.util.AttributeSet; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | 18 | public class ChipsLayout extends ViewGroup 19 | implements ChipView.OnChipChangeListener{ 20 | 21 | private List chips; 22 | private int lineHeight; 23 | private int horizontalSpacing; 24 | private int verticalSpacing; 25 | private ChipBuilder chipBuilder; 26 | private boolean deleteAnimationEnabled; 27 | private int removeAnimationRes; 28 | private boolean removeChipTouch; 29 | public ChipsLayout(Context context){ 30 | this(context,null,0); 31 | } 32 | 33 | public ChipsLayout(Context context, AttributeSet attrs){ 34 | this(context,attrs,0); 35 | } 36 | 37 | public ChipsLayout(Context context, AttributeSet attrs, int defStyle){ 38 | super(context,attrs,defStyle); 39 | initAttrs(attrs); 40 | } 41 | 42 | private void initAttrs(AttributeSet attrs){ 43 | if(attrs!=null){ 44 | TypedArray array=getContext().obtainStyledAttributes(attrs,R.styleable.ChipsLayout); 45 | chipBuilder=ChipBuilder.create(getContext(),array); 46 | horizontalSpacing=(int)(array.getDimension(R.styleable.ChipsLayout_chip_layout_horizontal_margin,1)); 47 | verticalSpacing=(int)(array.getDimension(R.styleable.ChipsLayout_chip_layout_vertical_margin,1)); 48 | deleteAnimationEnabled=array.getBoolean(R.styleable.ChipsLayout_remove_anim_enabled,true); 49 | removeAnimationRes=array.getInteger(R.styleable.ChipsLayout_remove_anim,-1); 50 | removeChipTouch=array.getBoolean(R.styleable.ChipsLayout_remove_chips_touch,false); 51 | int arrayRes=array.getResourceId(R.styleable.ChipsLayout_chips_array,-1); 52 | if(arrayRes!=-1){ 53 | String[] textArray=getResources().getStringArray(arrayRes); 54 | setTags(Arrays.asList(textArray)); 55 | } 56 | array.recycle(); 57 | return; 58 | } 59 | chipBuilder=ChipBuilder.create(getContext()); 60 | } 61 | 62 | private static class LayoutParams extends ViewGroup.LayoutParams { 63 | 64 | final int horizontalSpacing; 65 | final int verticalSpacing; 66 | 67 | LayoutParams(int horizontalSpacing, int verticalSpacing) { 68 | super(0, 0); 69 | this.horizontalSpacing = horizontalSpacing; 70 | this.verticalSpacing = verticalSpacing; 71 | } 72 | } 73 | 74 | @Override 75 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 76 | return new LayoutParams(horizontalSpacing, verticalSpacing); 77 | } 78 | 79 | @Override 80 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 81 | return new LayoutParams(horizontalSpacing, verticalSpacing); 82 | } 83 | 84 | @Override 85 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 86 | return p instanceof LayoutParams; 87 | } 88 | 89 | public void updateChipColors(ChipBuilder chipBuilder) { 90 | this.chipBuilder = chipBuilder; 91 | if(chips!=null){ 92 | for(ChipView chipView:chips){ 93 | chipView.setSelectedTextColor(chipBuilder.selectedTextColor); 94 | chipView.setSelectedFrontColor(chipBuilder.selectedFrontColor); 95 | chipView.setSelectedEndColor(chipBuilder.selectedEndColor); 96 | chipView.setSelectedBackgroundColor(chipBuilder.selectedBackgroundColor); 97 | chipView.setFrontIconColor(chipBuilder.frontIconColor); 98 | chipView.setEndIconColor(chipBuilder.endIconColor); 99 | chipView.setTextColor(chipBuilder.textColor); 100 | chipView.setBackgroundColor(chipBuilder.backgroundColor); 101 | } 102 | } 103 | } 104 | 105 | public ChipBuilder getChipBuilder() { 106 | return chipBuilder; 107 | } 108 | 109 | @Override 110 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 111 | final int count = getChildCount(); 112 | final int width = r - l; 113 | int xPos = getPaddingLeft(); 114 | int yPos = getPaddingTop(); 115 | 116 | for (int i = 0; i < count; i++) { 117 | final View child = getChildAt(i); 118 | if (child.getVisibility() != GONE) { 119 | final int childWidth = child.getMeasuredWidth(); 120 | final int childHeight = child.getMeasuredHeight(); 121 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 122 | if (xPos + childWidth > width) { 123 | xPos = getPaddingLeft(); 124 | yPos += lineHeight; 125 | } 126 | child.layout(xPos, yPos, xPos + childWidth, yPos + childHeight); 127 | xPos += childWidth + lp.horizontalSpacing; 128 | } 129 | } 130 | } 131 | 132 | @Override 133 | public void onScaleChanged(ChipView chipView) { 134 | requestLayout(); 135 | } 136 | 137 | @Override 138 | public void onRemove(ChipView chipView) { 139 | if(chips.contains(chipView)) { 140 | if (Build.VERSION.SDK_INT >= 19 && deleteAnimationEnabled) { 141 | if(removeAnimationRes!=-1) { 142 | Transition transition=TransitionInflater.from(getContext()).inflateTransition(removeAnimationRes); 143 | TransitionManager.beginDelayedTransition(this,transition); 144 | }else { 145 | TransitionManager.beginDelayedTransition(this); 146 | } 147 | } 148 | removeView(chipView); 149 | } 150 | } 151 | 152 | public void setHorizontalSpacing(int horizontalSpacing) { 153 | this.horizontalSpacing = horizontalSpacing; 154 | } 155 | 156 | public void setVerticalSpacing(int verticalSpacing) { 157 | this.verticalSpacing = verticalSpacing; 158 | } 159 | 160 | public int getVerticalSpacing() { 161 | return verticalSpacing; 162 | } 163 | 164 | public int getHorizontalSpacing() { 165 | return horizontalSpacing; 166 | } 167 | 168 | public List getChips() { 169 | return chips; 170 | } 171 | 172 | @Override 173 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 174 | int width = View.MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); 175 | int height = View.MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); 176 | int count = getChildCount(); 177 | int lineHeight = 0; 178 | 179 | int xPos = getPaddingLeft(); 180 | int yPos = getPaddingTop(); 181 | 182 | int childHeightMeasureSpec; 183 | if (View.MeasureSpec.getMode(heightMeasureSpec) == View.MeasureSpec.AT_MOST) { 184 | childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST); 185 | } else { 186 | childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 187 | } 188 | 189 | for (int i = 0; i < count; i++) { 190 | final View child = getChildAt(i); 191 | if (child.getVisibility() != GONE) { 192 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 193 | child.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST), childHeightMeasureSpec); 194 | final int childWidth = child.getMeasuredWidth(); 195 | lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.verticalSpacing); 196 | 197 | if (xPos + childWidth> width) { 198 | xPos = getPaddingLeft(); 199 | yPos += lineHeight; 200 | } 201 | xPos += childWidth + lp.horizontalSpacing; 202 | } 203 | } 204 | this.lineHeight = lineHeight; 205 | if (View.MeasureSpec.getMode(heightMeasureSpec) == View.MeasureSpec.UNSPECIFIED) { 206 | height = yPos + lineHeight; 207 | 208 | } else if (View.MeasureSpec.getMode(heightMeasureSpec) == View.MeasureSpec.AT_MOST) { 209 | if (yPos + lineHeight < height) { 210 | height = yPos + lineHeight; 211 | } 212 | } 213 | setMeasuredDimension(width, height); 214 | } 215 | 216 | public void setTags(List tags){ 217 | if(tags==null||tags.isEmpty()) return; 218 | if(chips==null){ 219 | chips=new ArrayList<>(tags.size()); 220 | } 221 | if(tags.size()>chips.size()){ 222 | int diff=tags.size()-chips.size(); 223 | for(int index=0;index chipViews){ 276 | if(chipViews!=null){ 277 | for(ChipView chip:chipViews){ 278 | if(!chips.contains(chip)){ 279 | chips.add(chip); 280 | addView(chip); 281 | } 282 | } 283 | } 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /chips-lover/src/main/java/com/vpaliy/chips_lover/ChipView.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.chips_lover; 2 | 3 | 4 | import android.content.Context; 5 | import android.content.res.TypedArray; 6 | import android.graphics.drawable.Drawable; 7 | import android.graphics.drawable.PaintDrawable; 8 | import android.os.Build; 9 | import android.support.annotation.DimenRes; 10 | import android.support.annotation.Nullable; 11 | import android.support.v4.content.ContextCompat; 12 | import android.support.v4.graphics.drawable.DrawableCompat; 13 | import android.support.v4.view.ViewCompat; 14 | import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; 15 | import android.util.AttributeSet; 16 | import android.view.View; 17 | import android.widget.ImageView; 18 | import android.widget.RelativeLayout; 19 | import android.widget.TextView; 20 | import de.hdodenhof.circleimageview.CircleImageView; 21 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 22 | 23 | public class ChipView extends RelativeLayout{ 24 | 25 | private static final float SCALE_BY=1.03F; 26 | 27 | private TextView chipTextView; 28 | private CircleImageView frontIcon; 29 | private ImageView endIcon; 30 | private String text; 31 | private Drawable endIconDrawable; 32 | private Drawable frontIconDrawable; 33 | private boolean selectable; 34 | private boolean closeable; 35 | private boolean isPressAnimation; 36 | private boolean isSelected; 37 | private int frontIconColor; 38 | private int endIconColor; 39 | private int textStyle; 40 | private int elevation; 41 | private int backgroundColor; 42 | private int textColor; 43 | private int selectedBackgroundColor; 44 | private int selectedTextColor; 45 | private int selectedEndColor; 46 | private int selectedFrontColor; 47 | 48 | private OnChipChangeListener chipChangeListener; 49 | private OnFrontIconEventClick frontIconClickEvent; 50 | private OnEndIconEventClick endIconEventClick; 51 | private OnClickListener selfClickListener; 52 | private OnClickListener externalClickListener; 53 | 54 | public ChipView(Context context) { 55 | this(context, null, 0); 56 | } 57 | 58 | public ChipView(Context context, ChipBuilder builder){ 59 | super(context); 60 | init(builder); 61 | } 62 | 63 | public ChipView(Context context, AttributeSet attrs) { 64 | this(context, attrs, 0); 65 | } 66 | 67 | public ChipView(Context context, AttributeSet attrs, int defStyleAttr) { 68 | super(context, attrs, defStyleAttr); 69 | if(attrs!=null){ 70 | TypedArray array=getContext().obtainStyledAttributes(attrs,R.styleable.ChipView); 71 | init(ChipBuilder.create(getContext(),array)); 72 | array.recycle(); 73 | }else { 74 | init(ChipBuilder.create(getContext())); 75 | } 76 | } 77 | 78 | void init(ChipBuilder builder){ 79 | text=builder.text; 80 | endIconDrawable=builder.endIconDrawable; 81 | frontIconDrawable=builder.frontIconDrawable; 82 | elevation=builder.elevation; 83 | frontIconColor=builder.frontIconColor; 84 | endIconColor=builder.endIconColor; 85 | selectable=builder.selectable; 86 | closeable=builder.closeable; 87 | isPressAnimation =builder.isDefaultAnimation; 88 | textStyle=builder.textStyle; 89 | backgroundColor=builder.backgroundColor; 90 | textColor=builder.textColor; 91 | selectedBackgroundColor=builder.selectedBackgroundColor; 92 | selectedTextColor=builder.selectedTextColor; 93 | selectedEndColor=builder.selectedEndColor; 94 | selectedFrontColor=builder.selectedFrontColor; 95 | setUp(); 96 | } 97 | 98 | private int dimens(@DimenRes int dimen){ 99 | return (int)getResources().getDimension(dimen); 100 | } 101 | 102 | @Override 103 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 104 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 105 | getLayoutParams().height=dimens(R.dimen.chip_height); 106 | getLayoutParams().width=WRAP_CONTENT; 107 | if(elevation>0){ 108 | ViewCompat.setElevation(this,elevation); 109 | } 110 | } 111 | 112 | private void setUp(){ 113 | initBackgroundColor(); 114 | initTextView(); 115 | initEndIcon(); 116 | initFrontIcon(); 117 | selfClickListener=new OnClickListener() { 118 | @Override 119 | public void onClick(View v) { 120 | triggerSelection(); 121 | } 122 | }; 123 | setOnClickListener(selfClickListener); 124 | } 125 | 126 | private void triggerSelection(){ 127 | if(selectable){ 128 | isSelected=!isSelected; 129 | initBackgroundColor(); 130 | setFrontIcon(); 131 | setEndIcon(); 132 | setTextColor(); 133 | if(isPressAnimation) { 134 | ViewCompat.animate(this) 135 | .scaleX(isSelected?SCALE_BY: 1) 136 | .scaleY(isSelected?SCALE_BY: 1) 137 | .setDuration(getResources().getInteger(R.integer.default_anim_duration)) 138 | .setListener(new ViewPropertyAnimatorListenerAdapter(){ 139 | @Override 140 | public void onAnimationEnd(View view) { 141 | super.onAnimationEnd(view); 142 | if(chipChangeListener!=null){ 143 | chipChangeListener.onScaleChanged(ChipView.this); 144 | } 145 | } 146 | }) 147 | .start(); 148 | } 149 | } 150 | if(externalClickListener!=null){ 151 | externalClickListener.onClick(this); 152 | } 153 | } 154 | 155 | private void initTextView() { 156 | chipTextView= new TextView(getContext()); 157 | RelativeLayout.LayoutParams chipTextParams = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); 158 | if(endIconDrawable!=null||frontIconDrawable!=null){ 159 | chipTextParams.addRule(RIGHT_OF,R.id.chip_front_icon); 160 | chipTextParams.addRule(CENTER_VERTICAL); 161 | }else { 162 | chipTextParams.addRule(CENTER_IN_PARENT); 163 | } 164 | int left=frontIconDrawable!=null?dimens(R.dimen.chip_text_icon_margin):dimens(R.dimen.chip_text_margin); 165 | int right=endIconDrawable!=null?0 :dimens(R.dimen.chip_text_margin); 166 | 167 | chipTextParams.setMargins(left,0,right,0); 168 | chipTextView.setLayoutParams(chipTextParams); 169 | chipTextView.setId(R.id.chip_text); 170 | chipTextView.setText(text); 171 | if(textColor!=-1){ 172 | chipTextView.setTextColor(textColor); 173 | } 174 | if(textStyle!=-1){ 175 | if(Build.VERSION.SDK_INT>=23){ 176 | chipTextView.setTextAppearance(textStyle); 177 | } 178 | } 179 | this.addView(chipTextView); 180 | } 181 | 182 | public void setSelectedBackgroundColor(int selectedBackgroundColor) { 183 | this.selectedBackgroundColor = selectedBackgroundColor; 184 | if(isSelected){ 185 | initBackgroundColor(); 186 | } 187 | } 188 | 189 | private Drawable makeCopyIfPossible(Drawable drawable){ 190 | if(drawable.getConstantState()!=null){ 191 | return drawable.getConstantState().newDrawable().mutate(); 192 | } 193 | return drawable; 194 | } 195 | private void initFrontIcon(){ 196 | if(frontIconDrawable!=null) { 197 | frontIconDrawable=makeCopyIfPossible(frontIconDrawable); 198 | frontIcon = new CircleImageView(getContext()); 199 | LayoutParams params = new LayoutParams(dimens(R.dimen.chip_front_icon_size), dimens(R.dimen.chip_front_icon_size)); 200 | params.addRule(ALIGN_PARENT_LEFT); 201 | params.addRule(CENTER_VERTICAL); 202 | frontIcon.setLayoutParams(params); 203 | frontIcon.setId(R.id.chip_front_icon); 204 | setFrontIcon(); 205 | frontIcon.setOnClickListener(new OnClickListener() { 206 | @Override 207 | public void onClick(View v) { 208 | if(frontIconClickEvent!=null){ 209 | frontIconClickEvent.onClick(v); 210 | } 211 | } 212 | }); 213 | addView(frontIcon); 214 | } 215 | } 216 | 217 | public void setChipChangeListener(OnChipChangeListener chipChangeListener) { 218 | this.chipChangeListener = chipChangeListener; 219 | } 220 | 221 | private void initEndIcon(){ 222 | if(closeable||endIconDrawable!=null){ 223 | endIconDrawable=makeCopyIfPossible(endIconDrawable); 224 | endIcon=new CircleImageView(getContext()); 225 | LayoutParams params=new LayoutParams(dimens(R.dimen.chip_end_icon_size), dimens(R.dimen.chip_end_icon_size)); 226 | params.addRule(RIGHT_OF, R.id.chip_text); 227 | params.addRule(CENTER_VERTICAL); 228 | params.setMargins(dimens(R.dimen.chip_close_margin),0, dimens(R.dimen.chip_close_margin),0); 229 | endIcon.setLayoutParams(params); 230 | endIcon.setId(R.id.chip_end_icon); 231 | setEndIcon(); 232 | endIcon.setOnClickListener(new OnClickListener() { 233 | @Override 234 | public void onClick(View v) { 235 | if(endIconEventClick!=null){ 236 | endIconEventClick.onClick(v); 237 | } 238 | 239 | if(closeable){ 240 | chipChangeListener.onRemove(ChipView.this); 241 | } 242 | } 243 | }); 244 | addView(endIcon); 245 | } 246 | } 247 | 248 | @Override 249 | public void setOnClickListener(@Nullable OnClickListener listener) { 250 | if(listener==selfClickListener) { 251 | super.setOnClickListener(listener); 252 | }else{ 253 | this.externalClickListener=listener; 254 | } 255 | } 256 | 257 | public void select(){ 258 | triggerSelection(); 259 | } 260 | 261 | private void setTextColor(){ 262 | if(chipTextView!=null){ 263 | chipTextView.setTextColor(isSelected?selectedTextColor:textColor); 264 | } 265 | } 266 | 267 | private void setFrontIcon(){ 268 | if(frontIcon!=null){ 269 | if(isSelected){ 270 | if(selectedFrontColor!=-1) { 271 | DrawableCompat.setTint(frontIconDrawable, selectedFrontColor); 272 | } 273 | }else if(frontIconColor!=-1){ 274 | DrawableCompat.setTint(frontIconDrawable,frontIconColor); 275 | } 276 | frontIcon.setImageDrawable(frontIconDrawable); 277 | } 278 | } 279 | 280 | private void setEndIcon(){ 281 | if(endIcon!=null){ 282 | if(isSelected) { 283 | DrawableCompat.setTint(endIconDrawable, selectedEndColor); 284 | }else if(endIconColor!=-1){ 285 | DrawableCompat.setTint(endIconDrawable,endIconColor); 286 | } 287 | endIcon.setImageDrawable(endIconDrawable); 288 | } 289 | } 290 | 291 | public void setFrontIconClickEvent(OnFrontIconEventClick frontIconClickEvent) { 292 | this.frontIconClickEvent = frontIconClickEvent; 293 | } 294 | 295 | public void setEndIconEventClick(OnEndIconEventClick endIconEventClick) { 296 | this.endIconEventClick = endIconEventClick; 297 | } 298 | 299 | private void initBackgroundColor() { 300 | PaintDrawable bgDrawable = new PaintDrawable(isSelected?selectedBackgroundColor:backgroundColor); 301 | bgDrawable.setCornerRadius(dimens(R.dimen.chip_height)/2); 302 | setBackgroundDrawable(bgDrawable); 303 | } 304 | 305 | @Override 306 | public void setBackgroundColor(int backgroundColor) { 307 | this.backgroundColor = backgroundColor; 308 | initBackgroundColor(); 309 | } 310 | 311 | public void setSelectedTextColor(int selectedTextColor) { 312 | this.selectedTextColor = selectedTextColor; 313 | if(isSelected){ 314 | setTextColor(); 315 | } 316 | } 317 | 318 | public void setSelectedEndColor(int selectedEndColor) { 319 | this.selectedEndColor = selectedEndColor; 320 | if(isSelected){ 321 | setEndIconColor(selectedEndColor); 322 | } 323 | } 324 | 325 | public void setText(String text) { 326 | this.text = text; 327 | chipTextView.setText(text); 328 | } 329 | 330 | public void setCloseable(boolean closeable) { 331 | this.closeable = closeable; 332 | if(closeable){ 333 | endIconColor=ContextCompat.getColor(getContext(),R.color.colorChipCloseInactive); 334 | endIconDrawable=ContextCompat.getDrawable(getContext(),R.drawable.ic_close); 335 | setEndIconColor(endIconColor); 336 | } 337 | } 338 | 339 | public void setEndIconDrawable(Drawable endIconDrawable) { 340 | this.endIconDrawable = endIconDrawable; 341 | if(endIconDrawable!=null){ 342 | if(endIcon!=null){ 343 | endIcon.setImageDrawable(endIconDrawable); 344 | }else{ 345 | initEndIcon(); 346 | } 347 | } 348 | } 349 | 350 | public void setFrontIconColor(int frontIconColor) { 351 | this.frontIconColor = frontIconColor; 352 | setFrontIcon(); 353 | } 354 | 355 | public void setFrontIconDrawable(Drawable frontIconDrawable) { 356 | this.frontIconDrawable = frontIconDrawable; 357 | if(frontIconDrawable!=null){ 358 | if(frontIcon!=null){ 359 | frontIcon.setImageDrawable(frontIconDrawable); 360 | }else{ 361 | initFrontIcon(); 362 | } 363 | } 364 | } 365 | 366 | public void setPressAnimation(boolean pressAnimation) { 367 | isPressAnimation = pressAnimation; 368 | } 369 | 370 | public void setSelectable(boolean selectable) { 371 | this.selectable = selectable; 372 | } 373 | 374 | public void setEndIconColor(int endIconColor) { 375 | this.endIconColor = endIconColor; 376 | setEndIcon(); 377 | } 378 | 379 | public void setSelectedFrontColor(int selectedFrontColor) { 380 | this.selectedFrontColor = selectedFrontColor; 381 | setFrontIcon(); 382 | } 383 | 384 | public void setTextStyle(int textStyle) { 385 | this.textStyle = textStyle; 386 | if(chipTextView!=null){ 387 | if(Build.VERSION.SDK_INT>=23) { 388 | chipTextView.setTextAppearance(textStyle); 389 | } 390 | } 391 | } 392 | 393 | public void setTextColor(int textColor) { 394 | this.textColor = textColor; 395 | setTextColor(); 396 | } 397 | 398 | public void setChipText(String chipText) { 399 | this.text=chipText; 400 | chipTextView.setText(chipText); 401 | } 402 | 403 | public int getBackgroundColor() { 404 | return backgroundColor; 405 | } 406 | 407 | public int getTextColor() { 408 | return textColor; 409 | } 410 | 411 | public int getSelectedEndColor() { 412 | return selectedEndColor; 413 | } 414 | 415 | public int getEndIconColor() { 416 | return endIconColor; 417 | } 418 | 419 | public int getFrontIconColor() { 420 | return frontIconColor; 421 | } 422 | 423 | public int getSelectedBackgroundColor() { 424 | return selectedBackgroundColor; 425 | } 426 | 427 | public int getSelectedFrontColor() { 428 | return selectedFrontColor; 429 | } 430 | 431 | public int getSelectedTextColor() { 432 | return selectedTextColor; 433 | } 434 | 435 | 436 | public interface OnChipChangeListener{ 437 | void onScaleChanged(ChipView chipView); 438 | void onRemove(ChipView chipView); 439 | } 440 | 441 | public String getChipText() { 442 | return text; 443 | } 444 | } 445 | --------------------------------------------------------------------------------