├── .gitignore ├── .idea ├── caches │ ├── build_file_checksums.ser │ └── gradle_models.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml ├── sonarlint │ └── issuestore │ │ ├── 2 │ │ ├── 4 │ │ │ └── 24ea072072fe4713e94594013cff406f9d0606b3 │ │ └── a │ │ │ └── 2ad9dec1b2f0ae5778e6eee4f0a0690605790a54 │ │ ├── 6 │ │ └── 1 │ │ │ └── 6138426855351adb5486ea848b921869233ec849 │ │ ├── 7 │ │ ├── 6 │ │ │ └── 76b07e40f24cce093536731477540f12e7da3cb6 │ │ └── 8 │ │ │ └── 787447a75214f54bb51d4a80aa664468aa836882 │ │ ├── 8 │ │ ├── 7 │ │ │ └── 87a9921252120e83a84acbdc431d1a251ae6f48b │ │ └── c │ │ │ └── 8c55c3ccc257e5907959013f99656e4c8ec3903e │ │ ├── 9 │ │ ├── 2 │ │ │ └── 9248224cd1511abf72a4b40967b1e73f0fdfb5b4 │ │ ├── 6 │ │ │ └── 969faa2a5d9f5e8390e958d2c07f2cfd54676e6b │ │ ├── b │ │ │ └── 9b6a14db23a6df4cb91effd9044e9417e59254d3 │ │ └── f │ │ │ └── 9f2d7908daa292907c892ba4d78a20d89e1a8bfc │ │ ├── a │ │ ├── 1 │ │ │ └── a1348fc73782a313e847b99ab6dbc5b36348316a │ │ ├── 8 │ │ │ └── a8e804beb3174b2650db161f81257656db32888b │ │ └── f │ │ │ └── af668f32608a082fe07e919115c63958f4a46884 │ │ ├── c │ │ ├── 2 │ │ │ └── c2f17b892b968ced08e39bfe4621772f86e7866b │ │ └── 3 │ │ │ └── c3c1811dec2dbad2deeafb9aad505ca9631da740 │ │ ├── d │ │ ├── 2 │ │ │ └── d2281fbb3027de2722081a53408dd77628bf080e │ │ └── 7 │ │ │ └── d7177de70d933b3f2696068eb33386ce7179262f │ │ ├── e │ │ ├── 0 │ │ │ └── e0b981d6082dcae20a33ba43801b0f244e4ff8af │ │ ├── a │ │ │ └── eaea3b07775b4155814c242382b8a45c406716de │ │ ├── b │ │ │ ├── eba70e44718fdfbb9bda0d313016b28c42f7e174 │ │ │ └── ebcd3036ba4cd1868c91b2e70a00ed08034014b6 │ │ └── c │ │ │ └── ec94823674c3743e2b3ba0615782ab34f523c992 │ │ ├── f │ │ ├── 0 │ │ │ └── f07866736216be0ee2aba49e392191aeae700a35 │ │ ├── 1 │ │ │ └── f1a1251d2235e5a153c4e7546efbe72ef053d961 │ │ ├── 4 │ │ │ └── f4a01d6a4fcb971362ec00a83903fd3902f52164 │ │ └── 7 │ │ │ └── f79cc2688e29fcbc286fda14ae07b27c9e83690f │ │ └── index.pb └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sha │ │ └── kamel │ │ └── togglesample │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sha │ │ │ └── kamel │ │ │ └── togglesample │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sha │ └── kamel │ └── togglesample │ └── ExampleUnitTest.java ├── blob └── master │ └── raw │ └── mtb_example.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mtb ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sha │ │ └── kamel │ │ └── multitogglebutton │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sha │ │ │ └── kamel │ │ │ └── multitogglebutton │ │ │ ├── Defaults.java │ │ │ ├── MultiToggleButton.java │ │ │ ├── Selected.java │ │ │ └── ToggleButton.java │ └── res │ │ ├── drawable-v21 │ │ ├── button_pressed.xml │ │ ├── button_section_shape.xml │ │ └── button_unpressed.xml │ │ ├── drawable │ │ ├── button_animator.xml │ │ ├── button_pressed.xml │ │ ├── button_section_shape.xml │ │ ├── button_states.xml │ │ ├── button_unpressed.xml │ │ ├── rect.xml │ │ ├── root_view_rounded.xml │ │ ├── rounded_left.xml │ │ ├── rounded_left_right.xml │ │ └── rounded_right.xml │ │ ├── layout │ │ ├── view_root.xml │ │ ├── view_root_rounded.xml │ │ ├── view_root_rounded_scrollable.xml │ │ └── view_root_srcollable.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── integers.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sha │ └── kamel │ └── multitogglebutton │ └── ExampleUnitTest.java ├── settings.gradle └── versions.gradle /.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 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/2/4/24ea072072fe4713e94594013cff406f9d0606b3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/2/4/24ea072072fe4713e94594013cff406f9d0606b3 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/2/a/2ad9dec1b2f0ae5778e6eee4f0a0690605790a54: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/2/a/2ad9dec1b2f0ae5778e6eee4f0a0690605790a54 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/6/1/6138426855351adb5486ea848b921869233ec849: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/6/1/6138426855351adb5486ea848b921869233ec849 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/7/6/76b07e40f24cce093536731477540f12e7da3cb6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/7/6/76b07e40f24cce093536731477540f12e7da3cb6 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/7/8/787447a75214f54bb51d4a80aa664468aa836882: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/7/8/787447a75214f54bb51d4a80aa664468aa836882 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/8/7/87a9921252120e83a84acbdc431d1a251ae6f48b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/8/7/87a9921252120e83a84acbdc431d1a251ae6f48b -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/9/2/9248224cd1511abf72a4b40967b1e73f0fdfb5b4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/9/2/9248224cd1511abf72a4b40967b1e73f0fdfb5b4 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/9/6/969faa2a5d9f5e8390e958d2c07f2cfd54676e6b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/9/6/969faa2a5d9f5e8390e958d2c07f2cfd54676e6b -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/9/b/9b6a14db23a6df4cb91effd9044e9417e59254d3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/9/b/9b6a14db23a6df4cb91effd9044e9417e59254d3 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/9/f/9f2d7908daa292907c892ba4d78a20d89e1a8bfc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/9/f/9f2d7908daa292907c892ba4d78a20d89e1a8bfc -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/a/1/a1348fc73782a313e847b99ab6dbc5b36348316a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/a/1/a1348fc73782a313e847b99ab6dbc5b36348316a -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/a/8/a8e804beb3174b2650db161f81257656db32888b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/a/8/a8e804beb3174b2650db161f81257656db32888b -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/a/f/af668f32608a082fe07e919115c63958f4a46884: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/a/f/af668f32608a082fe07e919115c63958f4a46884 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/c/2/c2f17b892b968ced08e39bfe4621772f86e7866b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/c/2/c2f17b892b968ced08e39bfe4621772f86e7866b -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/c/3/c3c1811dec2dbad2deeafb9aad505ca9631da740: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/c/3/c3c1811dec2dbad2deeafb9aad505ca9631da740 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/d/2/d2281fbb3027de2722081a53408dd77628bf080e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/d/2/d2281fbb3027de2722081a53408dd77628bf080e -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/d/7/d7177de70d933b3f2696068eb33386ce7179262f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/d/7/d7177de70d933b3f2696068eb33386ce7179262f -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/e/0/e0b981d6082dcae20a33ba43801b0f244e4ff8af: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/e/0/e0b981d6082dcae20a33ba43801b0f244e4ff8af -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/e/a/eaea3b07775b4155814c242382b8a45c406716de: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/e/a/eaea3b07775b4155814c242382b8a45c406716de -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/e/b/eba70e44718fdfbb9bda0d313016b28c42f7e174: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/e/b/eba70e44718fdfbb9bda0d313016b28c42f7e174 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/e/b/ebcd3036ba4cd1868c91b2e70a00ed08034014b6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/e/b/ebcd3036ba4cd1868c91b2e70a00ed08034014b6 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/e/c/ec94823674c3743e2b3ba0615782ab34f523c992: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/e/c/ec94823674c3743e2b3ba0615782ab34f523c992 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/1/f1a1251d2235e5a153c4e7546efbe72ef053d961: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/f/1/f1a1251d2235e5a153c4e7546efbe72ef053d961 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164 -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/f/7/f79cc2688e29fcbc286fda14ae07b27c9e83690f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/f/7/f79cc2688e29fcbc286fda14ae07b27c9e83690f -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/index.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/.idea/sonarlint/issuestore/index.pb -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # MultiToggleButton 5 | ### A simple Android multi toggle button 6 | 7 | ![alt text](https://github.com/ShabanKamell/android-multitoggle/blob/master/blob/master/raw/mtb_example.png "Sample App") 8 | 9 | # Installation 10 | [ ![Download](https://api.bintray.com/packages/shabankamel/android/multitogglebutton/images/download.svg) ](https://bintray.com/shabankamel/android/multitogglebutton/_latestVersion) 11 | ```groovy 12 | dependencies { 13 | implementation 'com.sha.kamel:multi-toggle-button:1.8.3@aar' 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | maven { url "https://dl.bintray.com/shabankamel/android" } 19 | } 20 | } 21 | ``` 22 | # Usage 23 | 24 | Add a button to layout: 25 | ```xml 26 | 40 | ``` 41 | ## Listen to item selection 42 | ```java 43 | mtb.setOnItemSelectedListener((toggleButton, item, index, label, selected) -> { 44 | toast(selected ? "selected" : "deselected")); 45 | }); 46 | ``` 47 | ## Get selected items 48 | You can get selected items. 49 | ```java 50 | Selected selected = toggleButton.getSelected(); 51 | 52 | boolean isAnySelected = selected.isAnySelected(); 53 | boolean isAllSelected = selected.isAllSelected(); 54 | boolean isSingleItem = selected.isSingleItem(); 55 | 56 | int singleItemPosition = selected.getSingleItemPosition(); 57 | TextView singleItem = selected.getSingleItem(); 58 | 59 | List selectedItems = selected.getSelectedItems(); 60 | List selectedPositions = selected.getSelectedPositions(); 61 | ``` 62 | ## Colors 63 | You can select any desired color for different states. 64 | ```java 65 | mtb.setColorRes(R.color.mtb_green, R.color.mtb_gray); 66 | ``` 67 | There're many methods to set colors. Take a look at `ToggleButton` 68 | 69 | ## Rounded corners 70 | you can set corners rounded: 71 | ```java 72 | mtb.setRoundedCorners(); 73 | ``` 74 | 75 | ## Corners Radius 76 | You can set a default radius of `18dp` : 77 | ```java 78 | mtb.setCornerRadius(20); 79 | ``` 80 | 81 | 82 | ##### Note: 83 | if you set corner radius with `setCornerRadius`, no need to call `setRoundedCorners`. 84 | 85 | ## Multiple Choice 86 | ```java 87 | mtb.multipleChoice(true) 88 | ``` 89 | 90 | ## Max items to select 91 | you can set the maximum items allowed to be selected 92 | ```java 93 | mtb.maxSelectedItems(2, max -> toast("Can't select more than " + max + " items.")); 94 | ``` 95 | #### Note 96 | if you call `maxSelectedItems`, no need to set `multipleChoice(true)`. 97 | 98 | ## Scroll 99 | You can scroll `MultiToggleButton` if the views are out of bounds 100 | ```java 101 | mtb.setScrollable(true); 102 | ``` 103 | #### Note 104 | The scroll is disabled by default. 105 | 106 | 107 | # Attributes: 108 | | Attribute name | Description | 109 | | ----------------|------------------------| 110 | | labels | Labels of each items in button 111 | | mtbPressedColor | Color of pressed button 112 | | mtbUnpressedColor | Color of unpressed button 113 | | mtbColorPressedText | Color of text for pressed button 114 | | mtbColorUnpressedText | Color of text for un pressed button 115 | | mtbCornerRadius | Corner radius 116 | | mtbRoundedCorners | If true, the corners will be rounded. If corner radius is not set a default radius 18 will be set. 117 | | mtbMultipleChoice | multiple items choice. The default is false. 118 | | mtbScrollable | If true, items will be scrollable if it's out of screen bounds. The default is false 119 | | mtbSelectFirstItem | If true, first item will be selected. The default is true. 120 | | mtbTextAllCaps | All text caps.The default is true. 121 | 122 | 123 | ### See 'app' module for the full code. 124 | 125 | # License 126 | 127 | ## Apache license 2.0 128 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion versions.compileSdk 5 | defaultConfig { 6 | applicationId "com.sha.kamel.togglesample.sample" 7 | minSdkVersion versions.minSdk 8 | targetSdkVersion versions.targetSdk 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | compileOptions { 21 | targetCompatibility 1.8 22 | sourceCompatibility 1.8 23 | } 24 | 25 | // annotationProcessor 26 | android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true 27 | 28 | compileOptions { 29 | targetCompatibility 1.8 30 | sourceCompatibility 1.8 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation fileTree(dir: 'libs', include: ['*.jar']) 36 | implementation deps.support.appCompat 37 | implementation deps.support.design 38 | implementation deps.constraintLayout 39 | testImplementation 'junit:junit:4.12' 40 | androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', { 41 | exclude group: 'com.android.support', module: 'support-annotations' 42 | }) 43 | 44 | implementation project(':mtb') 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sha/kamel/togglesample/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.togglesample; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sha.kamel.formvalidator", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/sha/kamel/togglesample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.togglesample; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | import android.widget.Toast; 8 | 9 | import com.annimon.stream.Stream; 10 | import com.sha.kamel.multitogglebutton.MultiToggleButton; 11 | import com.sha.kamel.multitogglebutton.Selected; 12 | import com.sha.kamel.multitogglebutton.ToggleButton; 13 | 14 | import java.util.Arrays; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | private MultiToggleButton 19 | mtb1, 20 | mtb2, 21 | mtb3, 22 | mtb4, 23 | mtb5; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | 30 | setupDynamicButton(); 31 | } 32 | 33 | @Override 34 | public boolean onCreateOptionsMenu(Menu menu) { 35 | // Inflate the menu; this adds items to the action bar if it is present. 36 | getMenuInflater().inflate(R.menu.menu_main, menu); 37 | return true; 38 | } 39 | 40 | @Override 41 | public boolean onOptionsItemSelected(MenuItem item) { 42 | // Handle action bar item clicks here. The action bar will 43 | // automatically handle clicks on the Home/Up createTextView, so long 44 | // as you specify a parent activity in AndroidManifest.xml. 45 | int id = item.getItemId(); 46 | 47 | //noinspection SimplifiableIfStatement 48 | if (id == R.id.action_settings) { 49 | return true; 50 | } 51 | 52 | return super.onOptionsItemSelected(item); 53 | } 54 | 55 | public void setupDynamicButton() { 56 | mtb1 = findViewById(R.id.mtb1); 57 | mtb2 = findViewById(R.id.mtb2); 58 | mtb3 = findViewById(R.id.mtb3); 59 | mtb4 = findViewById(R.id.mtb4); 60 | mtb5 = findViewById(R.id.mtb5); 61 | 62 | mtb1.setOnItemSelectedListener(listener()) 63 | .setLabel("Yes", 0); 64 | 65 | mtb2.setOnItemSelectedListener(listener()) 66 | .setLabelsRes(Arrays.asList(R.string.left, R.string.right)) 67 | .setColorRes(R.color.mtb_green, R.color.mtb_gray) 68 | .setCornerRadius(40); 69 | 70 | mtb3.setOnItemSelectedListener(listener()); 71 | mtb4.setOnItemSelectedListener(listener()) 72 | .maxSelectedItems(2, max -> toast("Can't select more than " + max + " items.")); 73 | 74 | mtb5.setOnItemSelectedListener(listener()); 75 | 76 | String[] dogs = getResources().getStringArray(R.array.dogs_array); 77 | 78 | mtb5.setItems(dogs, null, new boolean[dogs.length]) 79 | .setOnItemSelectedListener(listener()) 80 | .setPressedColorTextRes(R.color.white) 81 | .setUnpressedColorTextRes(R.color.white); 82 | } 83 | 84 | private ToggleButton.OnItemSelectedListener listener(){ 85 | return (toggleButton, item, index, label, selected) -> { 86 | String msg = "Number " + 87 | index + 88 | " is " + 89 | (selected ? "selected" : "deselected") + 90 | ", Label: " + label + 91 | ", Selected items: " + 92 | selectedItemsMsg(toggleButton); 93 | toast(msg); 94 | }; 95 | } 96 | 97 | private void toast(String s) { 98 | Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); 99 | } 100 | 101 | private String selectedItemsMsg(ToggleButton toggleButton) { 102 | Selected selected = toggleButton.getSelected(); 103 | String msg; 104 | if (selected.isAnySelected()){ 105 | if (selected.isSingleItem()) 106 | msg = "One item selected: " + selected.getSingleItemPosition(); 107 | else 108 | msg = Stream.of(selected.getSelectedPositions()) 109 | .map(String::valueOf) 110 | .reduce((p1, p2) -> p1 + ", " + p2) 111 | .get(); 112 | 113 | } 114 | else msg = "No items selected"; 115 | return msg; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | 32 | 33 | 41 | 42 | 55 | 56 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #FFFFFF 5 | #4fbe79 6 | #d3d3d3 7 | #de8a40 8 | #86470f 9 | #ffffff 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MTB Example 3 | 4 | Hello world! 5 | Settings 6 | 7 | 8 | Mer 9 | Venus 10 | Earth 11 | Mars 12 | 13 | 14 | 15 | Yes 16 | No 17 | 18 | 19 | 20 | Pug 21 | Cavalier King Charles Spaniel 22 | Doge 23 | Pup 24 | Golden 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/test/java/com/sha/kamel/togglesample/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.togglesample; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /blob/master/raw/mtb_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/blob/master/raw/mtb_example.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | apply from : 'versions.gradle' 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.3.0-alpha08' 11 | 12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 13 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 14 | 15 | // NOTE: Do not place your application versions here; they belong 16 | // in the individual module build.gradle files 17 | } 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | jcenter() 24 | maven { 25 | url "https://dl.bintray.com/shabankamel/android" 26 | } 27 | } 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MalekKamel/android-multitoggle/3ac8e4c311a158315527685addacad2b2b3cc821/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 20 12:32:49 EET 2018 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-4.9-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /mtb/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mtb/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | // ./gradlew bintrayUpload 4 | 5 | ext { 6 | bintrayRepo = 'android' 7 | bintrayName = 'multitogglebutton' 8 | 9 | publishedGroupId = 'com.sha.kamel' 10 | libraryName = 'MultiToggleButton' 11 | artifact = 'multi-toggle-button' 12 | 13 | libraryDescription = 'A simple Android multi toggle button.' 14 | 15 | siteUrl = 'https://github.com/ShabanKamell/android-multitoggle' 16 | gitUrl = 'https://github.com/ShabanKamell/android-multitoggle.git' 17 | 18 | libraryVersion = '1.8.3' 19 | 20 | developerId = 'shaban' 21 | developerName = 'shaban kamel' 22 | developerEmail = 'sh3ban.kamel@gmail.com' 23 | 24 | licenseName = 'The Apache Software License, Version 2.0' 25 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 26 | allLicenses = ["Apache-2.0"] 27 | } 28 | 29 | android { 30 | compileSdkVersion versions.compileSdk 31 | 32 | defaultConfig { 33 | minSdkVersion versions.minvSdk 34 | targetSdkVersion versions.targetSdk 35 | versionCode 1 36 | versionName "1.0" 37 | 38 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 39 | } 40 | 41 | buildTypes { 42 | release { 43 | minifyEnabled false 44 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 45 | } 46 | } 47 | packagingOptions { 48 | exclude 'META-INF/rxjava.properties' 49 | exclude 'META-INF/DEPENDENCIES.txt' 50 | exclude 'META-INF/LICENSE.txt' 51 | exclude 'META-INF/NOTICE.txt' 52 | exclude 'META-INF/NOTICE' 53 | exclude 'META-INF/LICENSE' 54 | exclude 'META-INF/DEPENDENCIES' 55 | exclude 'META-INF/notice.txt' 56 | exclude 'META-INF/license.txt' 57 | exclude 'META-INF/dependencies.txt' 58 | exclude 'META-INF/LGPL2.1' 59 | exclude 'META-INF/MANIFEST.MF' 60 | } 61 | 62 | compileOptions { 63 | targetCompatibility 1.8 64 | sourceCompatibility 1.8 65 | } 66 | 67 | // annotationProcessor 68 | android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true 69 | } 70 | 71 | configurations { 72 | javadocDeps 73 | } 74 | 75 | dependencies { 76 | implementation fileTree(dir: 'libs', include: ['*.jar']) 77 | testImplementation 'junit:junit:4.12' 78 | androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', { 79 | exclude group: 'com.android.support', module: 'support-annotations' 80 | }) 81 | 82 | api deps.support.design 83 | javadocDeps deps.support.design 84 | javadocDeps deps.support.compat 85 | 86 | api deps.java8_reactive_streams 87 | javadocDeps deps.java8_reactive_streams 88 | 89 | } 90 | 91 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 92 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' 93 | 94 | task docs(type: Javadoc) { 95 | failOnError false 96 | source = android.sourceSets.main.java.sourceFiles 97 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 98 | classpath += configurations.compile 99 | } 100 | 101 | task androidJavadocs(type: Javadoc) { 102 | source = android.sourceSets.main.java.srcDirs 103 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + configurations.compile 104 | } 105 | 106 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 107 | classifier = 'javadoc' 108 | //basename = artifact_id 109 | from androidJavadocs.destinationDir 110 | } 111 | 112 | javadoc { 113 | classpath += configurations.javadocDeps 114 | options.addStringOption("sourcepath", "") 115 | } 116 | -------------------------------------------------------------------------------- /mtb/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /mtb/src/androidTest/java/com/sha/kamel/multitogglebutton/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.multitogglebutton; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sha.kamel.formvalidator.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /mtb/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mtb/src/main/java/com/sha/kamel/multitogglebutton/Defaults.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.multitogglebutton; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.support.annotation.ColorInt; 8 | import android.support.v4.content.ContextCompat; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.widget.TextView; 12 | 13 | import java.util.List; 14 | 15 | public interface Defaults { 16 | float DEFAULT_CORNER_RADIUS = 18; 17 | 18 | default int color(TypedArray a, int res) { 19 | return a.getColor(res, 0); 20 | } 21 | 22 | default int color(TypedArray a, int res, int defValue) { 23 | return a.getColor(res, color(defValue)); 24 | } 25 | 26 | default int color(int res) { 27 | return ContextCompat.getColor(getContext(), res); 28 | } 29 | 30 | default Context getContext(){ 31 | throw new UnsupportedOperationException(); 32 | } 33 | 34 | default LayoutInflater layoutInflater(){ 35 | return (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 36 | } 37 | 38 | default boolean isValidColor(int color){ 39 | return color != 0; 40 | } 41 | 42 | default void setBackground(View v, @ColorInt int color) { 43 | if (!(v.getBackground() instanceof GradientDrawable)){ 44 | v.setBackgroundColor(color); 45 | return; 46 | } 47 | GradientDrawable drawable = (GradientDrawable) v.getBackground(); 48 | drawable.setColor(color); 49 | } 50 | 51 | default GradientDrawable gradientDrawable(View v){ 52 | return v.getBackground() instanceof GradientDrawable ? 53 | (GradientDrawable) v.getBackground() : 54 | new GradientDrawable(); 55 | } 56 | 57 | default void radius(View v, float radius) { 58 | if (radius == -1) radius = DEFAULT_CORNER_RADIUS; 59 | GradientDrawable drawable = gradientDrawable(v); 60 | drawable.setStroke(0, Color.TRANSPARENT); 61 | drawable.setCornerRadius(radius); 62 | v.setBackground(drawable); 63 | } 64 | 65 | default void radius(View v, float[] radius) { 66 | GradientDrawable drawable = gradientDrawable(v); 67 | drawable.setCornerRadii(radius); 68 | drawable.setStroke(0, Color.TRANSPARENT); 69 | v.setBackground(drawable); 70 | } 71 | 72 | default void leftRadius(View v, float radius) { 73 | if (radius == -1) radius = DEFAULT_CORNER_RADIUS; 74 | radius(v, new float[] { radius, radius, 0, 0, 0, 0, radius, radius }); 75 | } 76 | 77 | default void rightRadius(View v, float radius) { 78 | if (radius == -1) radius = DEFAULT_CORNER_RADIUS; 79 | radius(v, new float[] { 0, 0, radius, radius, radius, radius, 0, 0 }); 80 | } 81 | 82 | 83 | default int count(T[] array){ 84 | return array == null ? 0 : array.length; 85 | } 86 | 87 | default int count(int[] array){ 88 | return array == null ? 0 : array.length; 89 | } 90 | 91 | default boolean isEmpty(int[] array){ 92 | return array == null || array.length == 0; 93 | } 94 | 95 | default boolean isNotEmpty(int[] array){ 96 | return array != null && array.length > 0; 97 | } 98 | 99 | default boolean isEmpty(boolean[] array){ 100 | return array == null || array.length == 0; 101 | } 102 | 103 | default boolean isNotEmpty(boolean[] array){ 104 | return array != null && array.length > 0; 105 | } 106 | 107 | default boolean isEmpty(List array){ 108 | return array == null || array.isEmpty(); 109 | } 110 | 111 | default boolean isNotEmpty(List array){ 112 | return array != null && !array.isEmpty(); 113 | } 114 | 115 | default boolean isEmpty(T[] array){ 116 | return array == null || array.length == 0; 117 | } 118 | 119 | default boolean isNotEmpty(T[] array){ 120 | return array != null && array.length > 0; 121 | } 122 | 123 | default int count(List list){ 124 | return list == null ? 0 : list.size(); 125 | } 126 | 127 | default String text(TextView tv){ 128 | return tv.getText().toString(); 129 | } 130 | 131 | 132 | } 133 | -------------------------------------------------------------------------------- /mtb/src/main/java/com/sha/kamel/multitogglebutton/MultiToggleButton.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.multitogglebutton; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.util.AttributeSet; 7 | import android.view.LayoutInflater; 8 | import android.widget.TextView; 9 | 10 | import com.annimon.stream.Stream; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class MultiToggleButton extends ToggleButton { 16 | 17 | public MultiToggleButton(Context context) { 18 | super(context, null); 19 | } 20 | 21 | public MultiToggleButton(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | initItems(); 24 | } 25 | 26 | private void initItems() { 27 | setItems(labels); 28 | } 29 | 30 | /** 31 | * Set the enabled state of this MultiToggleButton, 32 | * including all of its child items. 33 | * 34 | * @param enabled True if this view is enabled, false otherwise. 35 | */ 36 | @Override 37 | public void setEnabled(boolean enabled) { 38 | Stream.of(items).forEach(item -> item.setEnabled(enabled)); 39 | } 40 | 41 | /** 42 | * Set multiple items with the specified labels 43 | * 44 | * @param labels An array of CharSequences for the items 45 | * @return this 46 | */ 47 | public ToggleButton setItems(@NonNull CharSequence[] labels) { 48 | return setItems(labels, null, null); 49 | } 50 | 51 | /** 52 | * Set multiple items with the specified labels 53 | * 54 | * @param labels An array of CharSequences for the items 55 | * @return this 56 | */ 57 | public ToggleButton setItems(@NonNull List labels) { 58 | return setItems(labels.toArray(new CharSequence[0]), null, null); 59 | } 60 | 61 | /** 62 | * Set multiple items with the specified labels and default 63 | * initial values. Initial states are allowed, but both 64 | * arrays must be of the same size. 65 | * 66 | * @param labels An array of CharSequences for the items 67 | * @param selected The default value for the items 68 | * @return this 69 | */ 70 | public ToggleButton setItems(@Nullable CharSequence[] labels, @Nullable boolean[] selected) { 71 | return setItems(labels, null, selected); 72 | } 73 | 74 | /** 75 | * Set multiple items with the specified labels and default 76 | * initial values. Initial states are allowed, but both 77 | * arrays must be of the same size. 78 | * 79 | * @param labels An array of CharSequences for the items 80 | * @param imageResourceIds an optional icon to show, either text, icon or both needs to be set. 81 | * @param selected The default value for the items 82 | * @return this 83 | */ 84 | public ToggleButton setItems(@Nullable CharSequence[] labels, @Nullable int[] imageResourceIds, @Nullable boolean[] selected) { 85 | boolean[] selection = selected == null ? new boolean[count(labels)] : selected; 86 | if (!isEmpty(items) && selectFirstItem && selection.length > 0){ 87 | selection[0] = true; 88 | notifyItemSelected( 89 | items.get(0), 90 | true, 91 | 0, 92 | isEmpty(labels) ? items.get(0).getText().toString() : labels[0].toString() 93 | ); 94 | } 95 | 96 | this.labels = labels; 97 | final int itemsCount = Math.max(count(labels), count(imageResourceIds)); 98 | 99 | if (itemsCount == 0) return this; 100 | 101 | prepare(); 102 | 103 | addItems(itemsCount, labels, imageResourceIds, selection); 104 | if (hasRoundedCorners()) 105 | radius(rootView, cornerRadius); 106 | return this; 107 | } 108 | 109 | private void addItems( 110 | int itemsCount, 111 | CharSequence[] labels, 112 | int[] imageResourceIds, 113 | boolean[] selected 114 | ) { 115 | rootView.removeAllViews(); 116 | items = new ArrayList<>(itemsCount); 117 | 118 | if (labels == null) return; 119 | 120 | Stream.of(labels) 121 | .forEachIndexed((i, label) -> { 122 | TextView tv = createTextView(i, itemsCount); 123 | 124 | tv.setText(label); 125 | if (imageResourceIds != null && imageResourceIds[i] != 0) 126 | tv.setCompoundDrawablesWithIntrinsicBounds(imageResourceIds[i], 0, 0, 0); 127 | 128 | tv.setOnClickListener(v -> { 129 | if (!v.isSelected() && maxItemsToSelect > 0 && getSelectedItemsSize() == maxItemsToSelect){ 130 | maxCallback.accept(maxItemsToSelect); 131 | return; 132 | } 133 | toggleItemSelection(i); 134 | }); 135 | rootView.addView(tv); 136 | 137 | boolean defaultSelected = true; 138 | if (selected == null || itemsCount != selected.length) 139 | defaultSelected = false; 140 | if (defaultSelected) setItemSelected(tv, selected[i]); 141 | 142 | this.items.add(tv); 143 | }); 144 | } 145 | 146 | @Override 147 | public ToggleButton setLabelsRes(List labels) { 148 | List l = Stream.of(labels) 149 | .map(label -> getContext().getString(label)) 150 | .map(item -> (CharSequence) item) 151 | .toList(); 152 | setItems(l); 153 | return super .setLabelsRes(labels); 154 | } 155 | 156 | @Override 157 | public ToggleButton setLabels(List labels) { 158 | 159 | return super.setLabels(labels); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /mtb/src/main/java/com/sha/kamel/multitogglebutton/Selected.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.multitogglebutton; 2 | 3 | import android.widget.TextView; 4 | 5 | import java.util.List; 6 | 7 | public class Selected { 8 | private List items; 9 | private List positions; 10 | private int allItemsSize; 11 | 12 | Selected(List items, List positions, int allItemsSize) { 13 | this.items = items; 14 | this.positions = positions; 15 | this.allItemsSize = allItemsSize; 16 | } 17 | 18 | public boolean isSingleItem() { 19 | return items.size() == 1; 20 | } 21 | 22 | public TextView getSingleItem() { 23 | return items.get(0); 24 | } 25 | 26 | public int getSingleItemPosition() { 27 | return positions.get(0); 28 | } 29 | 30 | public List getSelectedItems() { 31 | return items; 32 | } 33 | 34 | public List getSelectedPositions() { 35 | return positions; 36 | } 37 | 38 | public boolean isAnySelected() { 39 | return !items.isEmpty(); 40 | } 41 | 42 | public boolean isAllSelected() { 43 | return items.size() == allItemsSize; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mtb/src/main/java/com/sha/kamel/multitogglebutton/ToggleButton.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.multitogglebutton; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.drawable.GradientDrawable; 6 | import android.os.Bundle; 7 | import android.os.Parcelable; 8 | import android.support.annotation.ColorInt; 9 | import android.support.annotation.ColorRes; 10 | import android.support.annotation.StringRes; 11 | import android.util.AttributeSet; 12 | import android.view.Gravity; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.Button; 17 | import android.widget.LinearLayout; 18 | import android.widget.TextView; 19 | 20 | import com.annimon.stream.Stream; 21 | import com.annimon.stream.function.IntConsumer; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | public abstract class ToggleButton extends LinearLayout 27 | implements Defaults{ 28 | 29 | private static final String KEY_BUTTON_STATES = "button_states"; 30 | private static final String KEY_INSTANCE_STATE = "instance_state"; 31 | 32 | /** 33 | * A list of rendered items. Used to get state, among others 34 | */ 35 | protected List items; 36 | 37 | /** 38 | * The specified labels 39 | */ 40 | protected CharSequence[] labels; 41 | /** 42 | * If true, multiple items can be pressed at the same time 43 | */ 44 | protected boolean multipleChoice = false; 45 | 46 | /** 47 | * The layout containing all items 48 | */ 49 | protected ViewGroup rootView; 50 | 51 | protected IntConsumer maxCallback; 52 | protected int maxItemsToSelect; 53 | 54 | public interface OnItemSelectedListener { 55 | void onSelected(ToggleButton toggleButton, View item, int position, String label, boolean selected); 56 | } 57 | 58 | private OnItemSelectedListener listener; 59 | 60 | protected float cornerRadius; 61 | protected int rootViewBackgroundColor; 62 | protected boolean isCornersRounded; 63 | protected boolean scrollable; 64 | protected boolean selectFirstItem; 65 | protected boolean textAllCaps; 66 | 67 | @ColorInt 68 | int colorPressed; 69 | 70 | @ColorInt 71 | int colorUnpressed; 72 | 73 | @ColorInt 74 | int colorPressedText; 75 | 76 | @ColorInt 77 | int colorUnpressedText; 78 | 79 | public ToggleButton(Context context) { 80 | super(context, null); 81 | } 82 | 83 | public ToggleButton(Context context, AttributeSet attrs) { 84 | super(context, attrs); 85 | resolveAttrs(attrs); 86 | } 87 | 88 | private void resolveAttrs(AttributeSet attrs) { 89 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MultiToggleButton, 0, 0); 90 | try { 91 | colorPressed = color(a, R.styleable.MultiToggleButton_mtbPressedColor, R.color.blue); 92 | colorUnpressed = color(a, R.styleable.MultiToggleButton_mtbUnpressedColor, R.color.white_unpressed); 93 | rootViewBackgroundColor = colorUnpressed; 94 | 95 | colorPressedText = color(a, R.styleable.MultiToggleButton_mtbColorPressedText); 96 | colorUnpressedText = color(a, R.styleable.MultiToggleButton_mtbColorUnpressedText); 97 | 98 | isCornersRounded = a.getBoolean(R.styleable.MultiToggleButton_mtbRoundedCorners, false); 99 | multipleChoice = a.getBoolean(R.styleable.MultiToggleButton_mtbMultipleChoice, false); 100 | scrollable = a.getBoolean(R.styleable.MultiToggleButton_mtbScrollable, false); 101 | textAllCaps = a.getBoolean(R.styleable.MultiToggleButton_mtbTextAllCaps, true); 102 | selectFirstItem = a.getBoolean(R.styleable.MultiToggleButton_mtbSelectFirstItem, true); 103 | cornerRadius = a.getDimension(R.styleable.MultiToggleButton_mtbCornerRadius, -1); 104 | 105 | labels = a.getTextArray(R.styleable.MultiToggleButton_labels); 106 | 107 | } finally { 108 | a.recycle(); 109 | } 110 | } 111 | 112 | protected void prepare() { 113 | setOrientation(LinearLayout.HORIZONTAL); 114 | setGravity(Gravity.CENTER_VERTICAL); 115 | 116 | LayoutInflater inflater = layoutInflater(); 117 | if (rootView == null) { 118 | int res; 119 | if (scrollable) 120 | res = !hasRoundedCorners() ? R.layout.view_root_srcollable : R.layout.view_root_rounded_scrollable; 121 | else 122 | res = !hasRoundedCorners() ? R.layout.view_root : R.layout.view_root_rounded; 123 | 124 | ViewGroup v = (ViewGroup) inflater.inflate(res, this, true); 125 | rootView = scrollable ? v.findViewById(R.id.rootView) : v; 126 | 127 | rootView.setBackground(new GradientDrawable()); 128 | 129 | if (hasRoundedCorners()){ 130 | radius(rootView, cornerRadius); 131 | } 132 | setBackground(rootView, colorUnpressed); 133 | } 134 | } 135 | 136 | private enum ItemPosition{ 137 | LEFT, 138 | RIGHT, 139 | INNER, 140 | SINGLE 141 | } 142 | 143 | protected TextView createTextView(int i, int itemsCount) { 144 | TextView tv = new Button(getContext()); 145 | tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT, 1f)); 146 | tv.setMinHeight(getResources().getDimensionPixelSize(R.dimen.button_min_height)); 147 | tv.setAllCaps(textAllCaps); 148 | 149 | if (i == 0 && itemsCount != 2) { 150 | if (itemsCount == 1) { 151 | tv.setTag(ItemPosition.SINGLE); 152 | if (hasRoundedCorners()) 153 | radius(tv, cornerRadius); 154 | } 155 | else { 156 | tv.setTag(ItemPosition.LEFT); 157 | if (hasRoundedCorners()) 158 | leftRadius(tv, cornerRadius); 159 | } 160 | } 161 | 162 | else if (itemsCount == 2) { 163 | tv.setTag(ItemPosition.INNER); 164 | if (hasRoundedCorners()) 165 | radius(tv, cornerRadius); 166 | } 167 | else if (i == itemsCount - 1 && hasRoundedCorners()) { 168 | tv.setTag(ItemPosition.RIGHT); 169 | rightRadius(tv, cornerRadius); 170 | } 171 | 172 | return tv; 173 | } 174 | 175 | /** 176 | * Listen to selection states of items 177 | * @param listener called if state changed 178 | * @return this 179 | */ 180 | public ToggleButton setOnItemSelectedListener(OnItemSelectedListener listener) { 181 | this.listener = listener; 182 | return this; 183 | } 184 | 185 | @Override 186 | public Parcelable onSaveInstanceState() { 187 | Bundle bundle = new Bundle(); 188 | bundle.putParcelable(KEY_INSTANCE_STATE, super.onSaveInstanceState()); 189 | boolean[] selection = getSelectionArray(); 190 | if (!isEmpty(selection)) 191 | bundle.putBooleanArray(KEY_BUTTON_STATES, selection); 192 | return bundle; 193 | } 194 | 195 | @Override 196 | public void onRestoreInstanceState(Parcelable state) { 197 | if (state instanceof Bundle) { 198 | Bundle bundle = (Bundle) state; 199 | setItemsSelection(bundle.getBooleanArray(KEY_BUTTON_STATES)); 200 | state = bundle.getParcelable(KEY_INSTANCE_STATE); 201 | } 202 | super.onRestoreInstanceState(state); 203 | } 204 | 205 | /** 206 | * @return an array of items selection 207 | */ 208 | public boolean[] getSelectionArray() { 209 | if (isEmpty(items)) return new boolean[]{}; 210 | boolean[] list = new boolean[items.size()]; 211 | for (int i = 0 ; i < items.size() ; i++) 212 | list[i] = items.get(i).isSelected(); 213 | return list; 214 | } 215 | 216 | /** 217 | * @return list of items selection 218 | */ 219 | public List getSelectionList() { 220 | if (isEmpty(items)) return new ArrayList<>(); 221 | return Stream.of(items).map(View::isSelected).toList(); 222 | } 223 | 224 | /** 225 | * @return list of items selection 226 | */ 227 | public int getSelectedItemsSize() { 228 | return Stream.of(items).filter(View::isSelected).toList().size(); 229 | } 230 | 231 | /** 232 | * Set items selection. selection array must be equal to items 233 | * size 234 | * @param selected selection array 235 | * @return this 236 | */ 237 | public ToggleButton setItemsSelection(boolean[] selected) { 238 | if (isEmpty(items) || isEmpty(selected) || items.size() != selected.length) return this; 239 | Stream.of(items).forEachIndexed((i, v) -> setItemSelected(v, selected[i])); 240 | return this; 241 | } 242 | 243 | /** 244 | * Set an item selected 245 | * @param v item view 246 | * @param selected true for selected 247 | * @return this 248 | */ 249 | public ToggleButton setItemSelected(View v, boolean selected) { 250 | if (v == null) return this; 251 | 252 | v.setSelected(selected); 253 | 254 | setBackground(v, selected ? colorPressed : colorUnpressed); 255 | 256 | textStyle(v, selected); 257 | return this; 258 | } 259 | 260 | private void textStyle(View v, boolean selected) { 261 | 262 | int style = selected ? R.style.WhiteBoldText : R.style.PrimaryNormalText; 263 | TextView tv = (TextView) v; 264 | tv.setTextAppearance(getContext(), style); 265 | 266 | 267 | if (isValidColor(colorPressedText) || isValidColor(colorUnpressedText)) 268 | tv.setTextColor(selected ? colorPressedText : colorUnpressedText); 269 | else 270 | tv.setTextColor(!selected ? colorPressed : colorUnpressed); 271 | } 272 | 273 | /** 274 | * @return a {@link Selected} object with zero or 275 | * more selected items 276 | */ 277 | public Selected getSelected() { 278 | List selected = Stream.of(items) 279 | .filterIndexed((i, item) -> item.isSelected()) 280 | .toList(); 281 | 282 | List selectedPositions = Stream.of(items) 283 | .filterIndexed((i, item) -> item.isSelected()) 284 | .map(items::indexOf) 285 | .toList(); 286 | return new Selected(selected, selectedPositions, items.size()); 287 | } 288 | 289 | /** 290 | * Reverse selection of an item 291 | * @param position index of item 292 | * @return this 293 | */ 294 | public ToggleButton toggleItemSelection(int position) { 295 | TextView item = items.get(position); 296 | boolean currentState = item.isSelected(); 297 | Stream.of(items) 298 | .forEachIndexed((i, v) -> { 299 | // Update selected item only in multiple choice 300 | if (multipleChoice) { 301 | if (i == position && v != null) 302 | setItemSelected(v, !v.isSelected()); 303 | return; 304 | } 305 | // 306 | setItemSelected(items.get(i), i == position); 307 | }); 308 | 309 | String label = item.getText().toString(); 310 | 311 | notifyItemSelected(item, currentState, position, label); 312 | 313 | return this; 314 | } 315 | 316 | protected void notifyItemSelected(TextView item, boolean oldState, int position, String label) { 317 | if (listener != null && oldState != item.isSelected()) 318 | listener.onSelected(this, item, position, label, item.isSelected()); 319 | } 320 | 321 | /** 322 | * update items 323 | */ 324 | public void refresh() { 325 | Stream.of(getSelectionList()) 326 | .forEachIndexed((i, selected) -> { 327 | View item = items.get(i); 328 | refreshView(item); 329 | setItemSelected(item, selected); 330 | }); 331 | if (hasRoundedCorners()) 332 | radius(rootView, cornerRadius); 333 | } 334 | 335 | private void refreshView(View item){ 336 | Object tag = item.getTag(); 337 | if (!hasRoundedCorners() || !(tag instanceof ItemPosition)) return; 338 | ItemPosition position = (ItemPosition) tag; 339 | switch (position){ 340 | case LEFT: 341 | leftRadius(item, cornerRadius); 342 | break; 343 | 344 | case RIGHT: 345 | rightRadius(item, cornerRadius); 346 | break; 347 | 348 | case SINGLE: 349 | case INNER: 350 | radius(item, cornerRadius); 351 | break; 352 | } 353 | } 354 | 355 | /** 356 | * The desired color resource identifier generated by the aapt tool 357 | * 358 | * @param colorPressed color resource ID for the pressed item 359 | * @param colorUnpressed color resource ID for the released item 360 | * @return this 361 | */ 362 | public ToggleButton setColorRes(@ColorRes int colorPressed, @ColorRes int colorUnpressed) { 363 | setColors( 364 | color(colorPressed), 365 | color(colorUnpressed) 366 | ); 367 | return this; 368 | } 369 | 370 | /** 371 | * Color values are in the form 0xAARRGGBB 372 | * 373 | * @param colorPressed resolved color for the pressed item 374 | * @param colorUnpressed resolved color for the released item 375 | * @return this 376 | */ 377 | public ToggleButton setColors(@ColorInt int colorPressed, @ColorInt int colorUnpressed) { 378 | try { 379 | this.colorPressed = colorPressed; 380 | this.colorUnpressed = colorUnpressed; 381 | rootViewBackgroundColor = colorUnpressed; 382 | return this; 383 | }finally { 384 | refresh(); 385 | } 386 | } 387 | 388 | /** 389 | * The desired color resource identifier generated by the aapt tool 390 | * 391 | * @param colorPressedText color resource ID for the pressed createTextView's text 392 | * @param colorPressedBackground color resource ID for the pressed createTextView's background 393 | * @return this 394 | */ 395 | public ToggleButton setPressedColorsRes(@ColorRes int colorPressedText, @ColorRes int colorPressedBackground) { 396 | setPressedColors( 397 | color(colorPressedText), 398 | color(colorPressedBackground) 399 | ); 400 | return this; 401 | } 402 | 403 | /** 404 | * The desired color resource for pressed color text 405 | * @param color resource 406 | * @return this 407 | */ 408 | public ToggleButton setPressedColorTextRes(@ColorRes int color) { 409 | this.colorPressedText = color(color); 410 | return this; 411 | } 412 | 413 | /** 414 | * The desired color for pressed color text 415 | * @param color resource 416 | * @return this 417 | */ 418 | public ToggleButton setPressedColorText(@ColorInt int color) { 419 | this.colorPressedText = color; 420 | refresh(); 421 | return this; 422 | } 423 | 424 | /** 425 | * The desired color for unpressed color text 426 | * @param color resource 427 | * @return this 428 | */ 429 | public ToggleButton setUnpressedColorTextRes(@ColorRes int color) { 430 | this.colorUnpressedText = color(color); 431 | refresh(); 432 | return this; 433 | } 434 | 435 | /** 436 | * The desired color for pressed color text 437 | * @param color resource 438 | * @return this 439 | */ 440 | public ToggleButton setUnpressedColorText(@ColorInt int color) { 441 | this.colorUnpressedText = color; 442 | refresh(); 443 | return this; 444 | } 445 | 446 | /** 447 | * Color values are in the form 0xAARRGGBB 448 | * 449 | * @param colorPressedText resolved color for the pressed createTextView's text 450 | * @param colorPressedBackground resolved color for the pressed createTextView's background 451 | * @return this 452 | */ 453 | public ToggleButton setPressedColors(@ColorInt int colorPressedText, @ColorInt int colorPressedBackground) { 454 | this.colorPressedText = colorPressedText; 455 | this.colorPressed = colorPressedBackground; 456 | refresh(); 457 | return this; 458 | } 459 | 460 | /** 461 | * The desired color resource identifier generated by the aapt tool 462 | * 463 | * @param colorUnpressedText color resource ID for the released createTextView's text 464 | * @param colorUnpressedBackground color resource ID for the released createTextView's background 465 | * @return this 466 | */ 467 | public ToggleButton setUnpressedColorRes(@ColorRes int colorUnpressedText, @ColorRes int colorUnpressedBackground) { 468 | setUnpressedColors( 469 | color(colorUnpressedText), 470 | color(colorUnpressedBackground) 471 | ); 472 | return this; 473 | } 474 | 475 | /** 476 | * Color values are in the form 0xAARRGGBB 477 | * 478 | * @param colorNotPressedText resolved color for the released createTextView's text 479 | * @param colorUnpressedBackground resolved color for the released createTextView's background 480 | * @return this 481 | */ 482 | public ToggleButton setUnpressedColors(@ColorInt int colorNotPressedText, @ColorInt int colorUnpressedBackground) { 483 | this.colorUnpressedText = colorNotPressedText; 484 | this.colorUnpressed = colorUnpressedBackground; 485 | rootViewBackgroundColor = colorUnpressed; 486 | refresh(); 487 | return this; 488 | } 489 | 490 | /** 491 | * The desired color resource identifier generated by the aapt tool 492 | * 493 | * @param colorPressedText drawable resource ID for the pressed createTextView's background 494 | * @param colorUnpressedText drawable resource ID for the released createTextView's background 495 | * @return this 496 | */ 497 | public ToggleButton setForegroundColorsRes(@ColorRes int colorPressedText, @ColorRes int colorUnpressedText) { 498 | setForegroundColors( 499 | color(colorPressedText), 500 | color(colorUnpressedText) 501 | ); 502 | return this; 503 | } 504 | 505 | /** 506 | * Color values are in the form 0xAARRGGBB 507 | * 508 | * @param colorPressedText resolved color for the pressed createTextView's text 509 | * @param colorUnpressedText resolved color for the released createTextView's text 510 | * @return this 511 | */ 512 | public ToggleButton setForegroundColors(@ColorInt int colorPressedText, @ColorInt int colorUnpressedText) { 513 | this.colorPressedText = colorPressedText; 514 | this.colorUnpressedText = colorUnpressedText; 515 | refresh(); 516 | return this; 517 | } 518 | 519 | /** 520 | * If multiple choice is enabled, the user can select multiple 521 | * values simultaneously. 522 | * 523 | * @param enable true to be multiple selected 524 | * @return this 525 | */ 526 | public ToggleButton multipleChoice(boolean enable) { 527 | this.multipleChoice = enable; 528 | return this; 529 | } 530 | 531 | /** 532 | * Select first item. The first item is selected by default 533 | * @param selected false to deselect 534 | * @return this 535 | */ 536 | public ToggleButton selectFirstItem(boolean selected) { 537 | this.selectFirstItem = selected; 538 | 539 | if (isEmpty(items)) return this; 540 | 541 | List states = getSelectionList(); 542 | states.set(0, true); 543 | Stream.of(states).forEachIndexed((i, s) -> setItemSelected(items.get(i), s)); 544 | return this; 545 | } 546 | 547 | /** 548 | * @return An array of the items' labels 549 | */ 550 | public CharSequence[] getLabels() { 551 | return this.labels; 552 | } 553 | 554 | /** 555 | * Set item label for item by position 556 | * @param label text 557 | * @param position index of item 558 | * @return this 559 | */ 560 | public ToggleButton setLabel(CharSequence label, int position){ 561 | items.get(position).setText(label); 562 | return this; 563 | } 564 | 565 | /** 566 | * Set item label for item by position 567 | * @param label text resource 568 | * @param position index of item 569 | * @return this 570 | */ 571 | public ToggleButton setLabel(@StringRes int label, int position){ 572 | TextView item = items.get(position); 573 | item.setText(label); 574 | return setLabel(getContext().getString(label), position); 575 | } 576 | 577 | /** 578 | * Set item label for item by position 579 | * @param labels texts resources 580 | * @return this 581 | */ 582 | public ToggleButton setLabelsRes(List labels){ 583 | List l = Stream.of(labels).map(label -> getContext().getString(label)).toList(); 584 | return setLabels(l); 585 | } 586 | 587 | /** 588 | * Set item label for item by position 589 | * @param labels texts strings 590 | * @return this 591 | */ 592 | public ToggleButton setLabels(List labels){ 593 | if (isEmpty(items)) return this; 594 | if (count(labels) != count(items)) throw new IllegalArgumentException("Labels size may not equal to items size."); 595 | 596 | Stream.of(items).forEachIndexed((i, item) -> item.setText(labels.get(i))); 597 | return this; 598 | } 599 | 600 | /** 601 | * Specify the radius of corners 602 | * @param cornerRadius radius size 603 | * @return this 604 | */ 605 | public ToggleButton setCornerRadius(float cornerRadius) { 606 | this.cornerRadius = cornerRadius; 607 | refresh(); 608 | return this; 609 | } 610 | 611 | /** 612 | * @return true if mtbRoundedCorners is set to true 613 | * or mtbCornerRadius value is greater than zero 614 | */ 615 | public boolean hasRoundedCorners(){ 616 | return cornerRadius != -1 || isCornersRounded; 617 | } 618 | 619 | /** 620 | * Return item by position 621 | * @param position index of item 622 | * @return item with the specified position 623 | */ 624 | public TextView itemAt(int position){ 625 | return items.get(position); 626 | } 627 | 628 | /** 629 | * @return list of all items. If no items, an empty array is returned. 630 | * Never return null 631 | */ 632 | public List items() { 633 | return items == null ? new ArrayList<>() : items; 634 | } 635 | 636 | /** 637 | * Specify maximum number of items to be selected 638 | * @param max number of items 639 | * @param callbackIfExceeded will be called if the selected items exceeds max 640 | * @return this 641 | */ 642 | public ToggleButton maxSelectedItems(int max, IntConsumer callbackIfExceeded){ 643 | if (max > items.size()) 644 | throw new IllegalArgumentException("max may not be greater than added items"); 645 | maxItemsToSelect = max; 646 | maxCallback = callbackIfExceeded; 647 | multipleChoice = true; 648 | return this; 649 | } 650 | 651 | /** 652 | * @return true if multiple choice enabled 653 | */ 654 | public boolean isMultipleChoice() { 655 | return multipleChoice; 656 | } 657 | 658 | /** 659 | * @return root view of all items 660 | */ 661 | @Override 662 | public ViewGroup getRootView() { 663 | return rootView; 664 | } 665 | 666 | /** 667 | * @return max items to select 668 | */ 669 | public int getMaxItemsToSelect() { 670 | return maxItemsToSelect; 671 | } 672 | 673 | public float getCornerRadius() { 674 | return cornerRadius; 675 | } 676 | 677 | /** 678 | * @return true if can scroll 679 | */ 680 | public boolean isScrollable() { 681 | return scrollable; 682 | } 683 | 684 | /** 685 | * @return true if first item is selected by default 686 | */ 687 | public boolean isSelectFirstItem() { 688 | return selectFirstItem; 689 | } 690 | 691 | /** 692 | * @return true if all caps enabled 693 | */ 694 | public boolean isTextAllCaps() { 695 | return textAllCaps; 696 | } 697 | 698 | /** 699 | * @return color of pressed item 700 | */ 701 | public int getColorPressed() { 702 | return colorPressed; 703 | } 704 | 705 | /** 706 | * @return color of unpressed item 707 | */ 708 | public int getColorUnpressed() { 709 | return colorUnpressed; 710 | } 711 | 712 | /** 713 | * @return text color of pressed text 714 | */ 715 | public int getColorPressedText() { 716 | return colorPressedText; 717 | } 718 | 719 | /** 720 | * @return text color of unpressed text 721 | */ 722 | public int getColorUnpressedText() { 723 | return colorUnpressedText; 724 | } 725 | 726 | /** 727 | * Set set rounded corners radius of 18dp 728 | * by passing true. The default is false 729 | * @param rounded true if enabled 730 | * @return this 731 | */ 732 | public ToggleButton setRoundedCorners(boolean rounded) { 733 | isCornersRounded = rounded; 734 | refresh(); 735 | return this; 736 | } 737 | } -------------------------------------------------------------------------------- /mtb/src/main/res/drawable-v21/button_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable-v21/button_section_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable-v21/button_unpressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/button_animator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 14 | 15 | 16 | 17 | 18 | 23 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/button_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/button_section_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/button_states.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/button_unpressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/rect.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/root_view_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/rounded_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/rounded_left_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /mtb/src/main/res/drawable/rounded_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /mtb/src/main/res/layout/view_root.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /mtb/src/main/res/layout/view_root_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /mtb/src/main/res/layout/view_root_rounded_scrollable.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /mtb/src/main/res/layout/view_root_srcollable.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /mtb/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /mtb/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #f5f4f4 4 | #e4e3e3 5 | #3F51B5 6 | #00000000 7 | #b53f5e 8 | #d9d7d7 9 | -------------------------------------------------------------------------------- /mtb/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4dp 3 | 2dp 4 | 1dp 5 | 7dp 6 | 2dp 7 | .07dp 8 | 9 | -------------------------------------------------------------------------------- /mtb/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 100 4 | 100 5 | -------------------------------------------------------------------------------- /mtb/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Left 4 | Center 5 | Right 6 | -------------------------------------------------------------------------------- /mtb/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 24 | 25 | 29 | 30 | 35 | 36 | -------------------------------------------------------------------------------- /mtb/src/test/java/com/sha/kamel/multitogglebutton/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sha.kamel.multitogglebutton; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':mtb' 2 | -------------------------------------------------------------------------------- /versions.gradle: -------------------------------------------------------------------------------- 1 | ext.versions = [ 2 | code : 16, 3 | name : '0.0.1', 4 | 5 | minSdk : 16, 6 | targetSdk : 28, 7 | compileSdk : 28, 8 | 9 | rxLifeCycle : "2.2.0", 10 | rxandroid : "2.0.1", 11 | rxjava : "2.1.3", 12 | rxbinding: "2.0.0", 13 | butterknife : "8.5.1", 14 | constraintlayout : "2.0.0-alpha2", 15 | supportLibs : '27.1.1', 16 | java8_reactive_streams: "1.2.0" 17 | ] 18 | 19 | ext.deps = [ 20 | support : [ 21 | recyclerView: "com.android.support:recyclerview-v7:$versions.supportLibs", 22 | appCompat : "com.android.support:appcompat-v7:$versions.supportLibs", 23 | compat : "com.android.support:support-compat:$versions.supportLibs", 24 | design : "com.android.support:design:$versions.supportLibs", 25 | preference : "com.android.support:preference-v7:$versions.supportLibs", 26 | percent : "com.android.support:percent:$versions.supportLibs", 27 | gridlayout : "com.android.support:gridlayout-v7:$versions.supportLibs", 28 | cardView : "com.android.support:cardview-v7:$versions.supportLibs", 29 | ], 30 | rx : [ 31 | lifeCycle : "com.trello.rxlifecycle2:rxlifecycle-android:${versions.rxLifeCycle}", 32 | android : "io.reactivex.rxjava2:rxandroid:${versions.rxandroid}", 33 | java : "io.reactivex.rxjava2:rxjava:${versions.rxjava}", 34 | binding : "com.jakewharton.rxbinding2:rxbinding:${versions.rxbinding}", 35 | binding_support_4 : "com.jakewharton.rxbinding2:rxbinding-support-v4:${versions.rxbinding}", 36 | binding_appcompat_7 : "com.jakewharton.rxbinding2:rxbinding-appcompat-v7:${versions.rxbinding}", 37 | binding_design : "com.jakewharton.rxbinding2:rxbinding-design:${versions.rxbinding}", 38 | binding_recyclerview_7 : "com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:${versions.rxbinding}", 39 | ], 40 | 41 | butterknife : [ 42 | runtime : "com.jakewharton:butterknife:${versions.butterknife}", 43 | compiler: "com.jakewharton:butterknife-compiler:${versions.butterknife}", 44 | ], 45 | constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintlayout}", 46 | java8_reactive_streams : "com.annimon:stream:${versions.java8_reactive_streams}" 47 | 48 | ] --------------------------------------------------------------------------------