├── .gitignore ├── .idea ├── caches │ ├── build_file_checksums.ser │ └── gradle_models.ser ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── segmentedcontrolmodule ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── segmented_control │ │ └── widget │ │ └── custom │ │ └── android │ │ └── com │ │ └── segmentedcontrol │ │ ├── Assert.java │ │ ├── Configs.java │ │ ├── Consumer.java │ │ ├── Notifier.java │ │ ├── SegmentConsumer.java │ │ ├── SegmentedControl.java │ │ ├── SegmentedControlControllerComponent.java │ │ ├── SegmentedControlViewComponent.java │ │ ├── custom_segment │ │ ├── SegmentAdapterImpl.java │ │ └── SegmentViewHolderImpl.java │ │ ├── item_row │ │ ├── SegmentRowAdapter.java │ │ └── SegmentRowViewHolder.java │ │ ├── item_row_column │ │ ├── SegmentAdapter.java │ │ ├── SegmentBackgroundType.java │ │ ├── SegmentData.java │ │ ├── SegmentDecoration.java │ │ └── SegmentViewHolder.java │ │ ├── listeners │ │ ├── OnSegmentClickListener.java │ │ ├── OnSegmentSelectRequestListener.java │ │ └── OnSegmentSelectedListener.java │ │ └── utils │ │ ├── ArgbEvaluator.java │ │ └── Utils.java │ └── res │ ├── layout │ └── item_segment_impl.xml │ └── values │ ├── attr.xml │ ├── colors.xml │ └── strings.xml └── settings.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 | /app 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertApikyan/SegmentedControl/574281e85338086221ffafd4652f7b41ea4ff15b/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertApikyan/SegmentedControl/574281e85338086221ffafd4652f7b41ea4ff15b/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android SegmentedControl + multi row support + multi selection 2 | ### minSdk API 14+ 3 | 4 | ![N|Solid](https://raw.githubusercontent.com/RobertApikyan/SegmentedControl/release_v0.1/app/src/main/res/mipmap-hdpi/ic_launcher.png) 5 | 6 | [Demo App, Play store link](https://play.google.com/store/apps/details?id=segmented_control.widget.custom.android.com.segmentedcontrolexample&hl=en) 7 | 8 | [Or try demo App online !](https://appetize.io/app/y4e91xhxgp47956bf73da4z4yg) 9 | 10 | [![](https://jitpack.io/v/RobertApikyan/SegmentedControl.svg)](https://jitpack.io/#RobertApikyan/SegmentedControl) 11 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 12 | 13 | ## Segmented control for Android, with a lot of customization properties 14 | 15 | 16 | 17 | ## ScreenShots 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ## Download 29 | ### Gradle 30 | Add to project level build.gradle 31 | ```groovy 32 | allprojects { 33 | repositories { 34 | ... 35 | maven { url 'https://jitpack.io' } 36 | } 37 | } 38 | ``` 39 | Add dependency to app module level build.gradle 40 | ```groovy 41 | dependencies { 42 | implementation 'com.github.RobertApikyan:SegmentedControl:1.2.0' 43 | } 44 | ``` 45 | ### Maven 46 | ```xml 47 | 48 | 49 | jitpack.io 50 | https://jitpack.io 51 | 52 | 53 | ``` 54 | Add dependency 55 | ```xml 56 | 57 | com.github.RobertApikyan 58 | SegmentedControl 59 | 1.1.3 60 | 61 | ``` 62 | 63 | 64 | ### Done. 65 | 66 | ## Simple usage in XML 67 | ```xml 68 | 78 | ``` 79 | 80 | Attributes 81 | ```xml 82 | setSupportedSelectionsCount(int) 83 | setDistributeEvenly(boolean) 84 | setDistributeEvenly(boolean) 85 | setColumnCount(int) 86 | addSegments(Object[]), addSegments(List) 87 | setSelectedStrokeColor(int) 88 | setUnSelectedStrokeColor(int) 89 | setSelectedBackgroundColor(int) 91 | setUnSelectedBackgroundColor(int) 92 | setSelectedTextColor(int) 93 | setUnSelectedTextColor(int) 94 | setTextSize(int) 95 | 96 | 97 | setTextHorizontalPadding(int) 98 | setTextVerticalPadding(int) 99 | setSegmentVerticalMargin(int) 100 | setSegmentHorizontalMargin(int) 101 | setRadius(int) 102 | setTopLeftRadius(int) 103 | setTopRightRadius(int) 104 | setBottomRightRadius(int) 105 | setBottomLeftRadius(int) 106 | setRadiusForEverySegment(boolean) 107 | setTypeFace(TypeFace) 108 | ``` 109 | 110 | **Note:** After every configuration change call segmentedControl.notifyConfigIsChanged() method 111 | 112 | # Example 113 | ```java 114 | segmentedControl.setStrokeWidth(width.intValue()); 115 | segmentedControl.setColumnCount(columnCount); 116 | segmentedControl.notifyConfigIsChanged(); 117 | ``` 118 | 119 | > SegmentedControl uses SegmentAdapter and SegmentViewHolder for displaying segments. There are allready exist a default implementations for SegmentAdapter (SegmentAdapterImpl) and SegmentViewHolder (SegmentViewHolderImpl), but if you want to make your custom implementation... well here is the steps 120 | 121 | 1. Define `segment_item.xml` inside `layouts` folder 122 | ```xml 123 | 127 | 128 | 138 | 139 | ``` 140 | 141 | 2. Create a `SegmentViewHolder` instance (`AppSegmentViewHolder`) (here I define the segment generic data type as a String) 142 | ```java 143 | public class AppSegmentViewHolder extends SegmentViewHolder { 144 | TextView textView; 145 | 146 | public AppSegmentViewHolder(@NonNull View sectionView) { 147 | super(sectionView); 148 | textView = (TextView) sectionView.findViewById(R.id.text_view); 149 | } 150 | 151 | @Override 152 | protected void onSegmentBind(String segmentData) { 153 | textView.setText(segmentData); 154 | } 155 | } 156 | ``` 157 | 158 | 3. Create a `SegmentAdapter` instance 159 | ```java 160 | public class AppSegmentAdapter extends SegmentAdapter { 161 | 162 | @NonNull 163 | @Override 164 | protected AppSegmentViewHolder onCreateViewHolder(@NonNull LayoutInflater layoutInflater, ViewGroup viewGroup, int i) { 165 | return new AppSegmentViewHolder(layoutInflater.inflate(R.layout.item_segment, null)); 166 | } 167 | } 168 | ``` 169 | 170 | 4. Pass the adapter to the segmentedControl 171 | ```java 172 | segmentedControl = (SegmentedControl) findViewById(R.id.segmented_control); 173 | segmentedControl.setAdapter(new AppSegmentAdapter()); 174 | ``` 175 | 176 | 5. Finally add segments data. 177 | ```java 178 | segmentedControl.addSegments(getResources().getStringArray(R.array.segments)); 179 | ``` 180 | That's it :) 181 | 182 | For custom implementation use `SegmentedControlUtils` helper class in order to define segment background type and background radius. 183 | 184 | [![View Robert Apikyan profile on LinkedIn](https://www.linkedin.com/img/webpromo/btn_viewmy_160x33.png)](https://www.linkedin.com/in/robert-apikyan-24b915130/) 185 | 186 | License 187 | ------- 188 | 189 | Copyright 2017 Robert Apikyan 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.4.1' 11 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | google() 22 | maven { url 'https://jitpack.io' } 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /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/RobertApikyan/SegmentedControl/574281e85338086221ffafd4652f7b41ea4ff15b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 19 11:27:29 AMT 2019 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-5.1.1-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 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | defaultConfig { 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.2.0" 11 | 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'com.android.support:appcompat-v7:28.0.0' 26 | api 'com.github.RobertApikyan:ViewComponent:1.0.3' 27 | api 'com.github.RobertApikyan:SecLayout:1.0.3' 28 | } 29 | 30 | task sourcesJar(type: Jar) { 31 | from android.sourceSets.main.java.srcDirs 32 | classifier = 'sources' 33 | } 34 | 35 | artifacts { 36 | archives sourcesJar 37 | } 38 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/Assert.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import section_layout.widget.custom.android.com.sectionlayout.SectionLayout; 4 | 5 | /** 6 | * Created by Robert Apikyan on 9/7/2017. 7 | */ 8 | 9 | class Assert { 10 | 11 | static void adapter(SectionLayout.Adapter adapter) { 12 | throwIf(adapter == null, new IllegalArgumentException("SegmentedControl#setAdapter -> adapter can't be null")); 13 | } 14 | 15 | static void columnCount(int columnCount) { 16 | throwIf(columnCount < Configs.DEFAULT_COLUMN_COUNT, 17 | new IllegalArgumentException("SegmentedControl#setColumnCount -> columnCounts value is invalid:" + " " + "columnCount = " + columnCount)); 18 | } 19 | 20 | static void outOfBounds(int position, int size, String methodName) { 21 | throwIf(position > size, new IndexOutOfBoundsException(methodName + " -> " + "position = " + position + " " + "size = " + size)); 22 | } 23 | 24 | private static void throwIf(boolean willThrow, Exception exception) { 25 | if (willThrow) { 26 | try { 27 | throw exception; 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } 33 | 34 | public static void supportedSelectionsCount(int supportedSelectionsCount) { 35 | throwIf(supportedSelectionsCount < Configs.DEFAULT_SUPPORTED_SELECTIONS_COUNT, 36 | new IllegalStateException("SegmentedControl#setSupportedSelectionsCount -> supportedSelectionsCount value is invalid: " + 37 | " " + "supportedSelectionsCount= "+supportedSelectionsCount)); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/Configs.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentDecoration; 4 | 5 | /** 6 | * Created by Robert Apikyan on 9/7/2017. 7 | */ 8 | 9 | class Configs { 10 | static final int DEFAULT_COLUMN_COUNT = 2; 11 | static final int DEFAULT_SUPPORTED_SELECTIONS_COUNT = 1; 12 | 13 | boolean willDistributeEvenly; 14 | boolean reselectionEnabled; 15 | 16 | int columnCount; 17 | int supportedSelectionsCount; 18 | 19 | SegmentDecoration segmentDecoration = new SegmentDecoration(); 20 | 21 | static Configs getDefault() { 22 | Configs configs = new Configs(); 23 | configs.reselectionEnabled = true; 24 | configs.willDistributeEvenly = false; 25 | configs.columnCount = DEFAULT_COLUMN_COUNT; 26 | configs.supportedSelectionsCount = DEFAULT_SUPPORTED_SELECTIONS_COUNT; 27 | return configs; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/Consumer.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | /** 4 | * Created by Robert Apikyan on 9/8/2017. 5 | */ 6 | 7 | interface Consumer { 8 | void apply(T t); 9 | } 10 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/Notifier.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 7 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentClickListener; 8 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentSelectRequestListener; 9 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentSelectedListener; 10 | 11 | import static segmented_control.widget.custom.android.com.segmentedcontrol.utils.Utils.lazy; 12 | 13 | 14 | /** 15 | * Created by Robert Apikyan on 9/8/2017. 16 | */ 17 | 18 | class Notifier implements OnSegmentClickListener, OnSegmentSelectedListener, OnSegmentSelectRequestListener { 19 | private List> onSegmentClickListeners; 20 | private List> onSegmentSelectedListeners; 21 | @SuppressWarnings("unchecked") 22 | private OnSegmentSelectRequestListener onSegmentSelectRequestListener; 23 | 24 | @Override 25 | public void onSegmentClick(final SegmentViewHolder segmentViewHolder) { 26 | onEvent(onSegmentClickListeners, new Consumer>() { 27 | @Override 28 | public void apply(OnSegmentClickListener onSegmentClickListener) { 29 | onSegmentClickListener.onSegmentClick(segmentViewHolder); 30 | } 31 | }); 32 | } 33 | 34 | @Override 35 | public void onSegmentSelected(final SegmentViewHolder segmentViewHolder, final boolean isSelected, final boolean isReselected) { 36 | onEvent(onSegmentSelectedListeners, new Consumer>() { 37 | @Override 38 | public void apply(OnSegmentSelectedListener onSegmentSelectedListener) { 39 | onSegmentSelectedListener.onSegmentSelected(segmentViewHolder, isSelected, isReselected); 40 | } 41 | }); 42 | } 43 | 44 | @Override 45 | public boolean onSegmentSelectRequest(final SegmentViewHolder segmentViewHolder) { 46 | return onSegmentSelectRequestListener == null || onSegmentSelectRequestListener.onSegmentSelectRequest(segmentViewHolder); 47 | } 48 | 49 | void addOnSegmentClickListener(OnSegmentClickListener onSegmentClickListener) { 50 | onSegmentClickListeners = lazy(onSegmentClickListeners, new ArrayList>()); 51 | onSegmentClickListeners.add(onSegmentClickListener); 52 | } 53 | 54 | void removeOnSegmentClickListener(OnSegmentClickListener onSegmentClickListener) { 55 | onSegmentClickListeners = lazy(onSegmentClickListeners, new ArrayList>()); 56 | onSegmentClickListeners.remove(onSegmentClickListener); 57 | } 58 | 59 | void addOnSegmentSelectListener(OnSegmentSelectedListener onSegmentSelectedListener) { 60 | onSegmentSelectedListeners = lazy(onSegmentSelectedListeners, new ArrayList>()); 61 | onSegmentSelectedListeners.add(onSegmentSelectedListener); 62 | } 63 | 64 | void removeOnSegmentSelectListener(OnSegmentSelectedListener onSegmentSelectedListener) { 65 | onSegmentSelectedListeners = lazy(onSegmentSelectedListeners, new ArrayList>()); 66 | onSegmentSelectedListeners.remove(onSegmentSelectedListener); 67 | } 68 | 69 | void setOnSegmentSelectRequestListener(OnSegmentSelectRequestListener onSegmentSelectRequestListener) { 70 | this.onSegmentSelectRequestListener = onSegmentSelectRequestListener; 71 | } 72 | 73 | 74 | private void onEvent(List eventListeners, Consumer listenerConsumer) { 75 | if (eventListeners != null && eventListeners.size() != 0) { 76 | for (T t : eventListeners) { 77 | listenerConsumer.apply(t); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/SegmentConsumer.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 4 | 5 | public interface SegmentConsumer { 6 | void apply(SegmentViewHolder segmentViewHolder); 7 | } -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/SegmentedControl.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Typeface; 6 | import android.support.annotation.NonNull; 7 | import android.util.AttributeSet; 8 | import android.util.TypedValue; 9 | import android.view.LayoutInflater; 10 | 11 | import java.util.List; 12 | 13 | import section_layout.widget.custom.android.com.sectionlayout.SectionLayout; 14 | import segmented_control.widget.custom.android.com.segmented_control.R; 15 | import segmented_control.widget.custom.android.com.segmentedcontrol.custom_segment.SegmentAdapterImpl; 16 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentAdapter; 17 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 18 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentClickListener; 19 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentSelectRequestListener; 20 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentSelectedListener; 21 | import view_component.lib_android.com.view_component.base_view.layouts.ComponentFrameLayout; 22 | 23 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentDecoration.DEFAULT_SELECTION_ANIMATION_DURATION; 24 | 25 | /** 26 | * Created by Robert Apikyan on 8/18/2017. 27 | * /* Attributes 28 | * {@link #setDistributeEvenly(boolean)} 29 | * {@link #setReselectionEnabled(boolean)} 30 | * {@link #setColumnCount(int)} 31 | * {@link #addSegments(Object[])} {@link #addSegments(List)} 32 | * {@link #setSupportedSelectionsCount(int)} 33 | * {@link #setSelectedStrokeColor(int)} 34 | * {@link #setUnSelectedStrokeColor(int)} 35 | * {@link #setStrokeWidth(int)} 36 | * {@link #setSelectedBackgroundColor(int)} 37 | * {@link #setUnSelectedBackgroundColor(int)} 38 | * {@link #setSelectedTextColor(int)} 39 | * {@link #setUnSelectedTextColor(int)} 40 | * {@link #setTextSize(int)} 41 | * {@link #setTextHorizontalPadding(int)} 42 | * {@link #setTextVerticalPadding(int)} 43 | * {@link #setSegmentVerticalMargin(int)} 44 | * {@link #setSegmentHorizontalMargin(int)} 45 | * {@link #setRadius(int)} 46 | * {@link #setTopLeftRadius(int)} 47 | * {@link #setTopRightRadius(int)} 48 | * {@link #setBottomRightRadius(int)} 49 | * {@link #setBottomLeftRadius(int)} 50 | * {@link #setRadiusForEverySegment(boolean)} 51 | * 52 | * @param 53 | */ 54 | public class SegmentedControl extends ComponentFrameLayout, 55 | SegmentedControlControllerComponent> { 56 | public SegmentedControl(Context context) { 57 | this(context, null); 58 | } 59 | 60 | public SegmentedControl(Context context, AttributeSet attrs) { 61 | this(context, attrs, 0); 62 | } 63 | 64 | public SegmentedControl(Context context, AttributeSet attrs, int defStyle) { 65 | super(context, attrs, defStyle); 66 | initAttr(attrs, defStyle); 67 | } 68 | 69 | private void initAttr(AttributeSet attrs, int defStyle) { 70 | TypedArray typedArray = getContext().obtainStyledAttributes(attrs, 71 | R.styleable.SegmentedControl, 72 | defStyle, 0); 73 | fetchAccentColor(); 74 | try { 75 | attrDistributeEvenly(typedArray); 76 | attrReselectionEnabled(typedArray); 77 | attrColumnCount(typedArray); 78 | attrSupportedSelectionsCount(typedArray); 79 | attrSelectedStrokeColor(typedArray); 80 | attrUnSelectedStrokeColor(typedArray); 81 | attrStrokeWidth(typedArray); 82 | attrSelectedBackgroundColor(typedArray); 83 | attrFocusedBackgroundColor(typedArray); 84 | attrSelectionAnimationDuration(typedArray); 85 | attrUnSelectedBackgroundColor(typedArray); 86 | attrSelectedTextColor(typedArray); 87 | attrUnSelectedTextColor(typedArray); 88 | attrTextSize(typedArray); 89 | attrFontAssetPath(typedArray); 90 | attrTextVerticalPadding(typedArray); 91 | attrTextHorizontalPadding(typedArray); 92 | attrSegmentHorizontalMargin(typedArray); 93 | attrSegmentVerticalMargin(typedArray); 94 | attrTopLeftRadius(typedArray); 95 | attrTopRightRadius(typedArray); 96 | attrBottomRightRadius(typedArray); 97 | attrBottomLeftRadius(typedArray); 98 | attrRadius(typedArray); 99 | attrRadiusForEverySegment(typedArray); 100 | attrSegments(typedArray); 101 | } finally { 102 | typedArray.recycle(); 103 | } 104 | } 105 | 106 | private void fetchAccentColor() { 107 | TypedValue tv = new TypedValue(); 108 | TypedArray typedArray = getContext().obtainStyledAttributes(tv.data, new int[]{R.attr.colorAccent}); 109 | try { 110 | getControllerComponent().setAccentColor(typedArray.getColor(0, 0)); 111 | } finally { 112 | typedArray.recycle(); 113 | } 114 | } 115 | 116 | private void attrSelectedStrokeColor(TypedArray typedArray) { 117 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_selectedStrokeColor, new Consumer() { 118 | @Override 119 | public void apply(Integer color) { 120 | getControllerComponent().setSelectedStrokeColor(color); 121 | } 122 | }); 123 | } 124 | 125 | private void attrUnSelectedStrokeColor(TypedArray typedArray) { 126 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_unSelectedStrokeColor, new Consumer() { 127 | @Override 128 | public void apply(Integer color) { 129 | getControllerComponent().setUnSelectedStrokeColor(color); 130 | } 131 | }); 132 | } 133 | 134 | private void attrStrokeWidth(TypedArray typedArray) { 135 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_strokeWidth, new Consumer() { 136 | @Override 137 | public void apply(Integer width) { 138 | getControllerComponent().setStrokeWidth(width); 139 | } 140 | }); 141 | } 142 | 143 | private void attrSelectedBackgroundColor(TypedArray typedArray) { 144 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_selectedBackgroundColor, new Consumer() { 145 | @Override 146 | public void apply(Integer color) { 147 | getControllerComponent().setSelectedBackgroundColor(color); 148 | } 149 | }); 150 | } 151 | 152 | private void attrUnSelectedBackgroundColor(TypedArray typedArray) { 153 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_unSelectedBackgroundColor, new Consumer() { 154 | @Override 155 | public void apply(Integer color) { 156 | getControllerComponent().setUnSelectedBackgroundColor(color); 157 | } 158 | }); 159 | } 160 | 161 | private void attrFocusedBackgroundColor(TypedArray typedArray) { 162 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_focusedBackgroundColor, new Consumer() { 163 | @Override 164 | public void apply(Integer color) { 165 | getControllerComponent().setFocusedBackgroundColor(color); 166 | } 167 | }); 168 | } 169 | 170 | private void attrSelectionAnimationDuration(TypedArray typedArray) { 171 | getControllerComponent().setSelectionAnimationDuration( 172 | typedArray.getInt(R.styleable.SegmentedControl_selectionAnimationDuration, 173 | DEFAULT_SELECTION_ANIMATION_DURATION)); 174 | } 175 | 176 | private void attrSelectedTextColor(TypedArray typedArray) { 177 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_selectedTextColor, new Consumer() { 178 | @Override 179 | public void apply(Integer color) { 180 | getControllerComponent().setSelectedTextColor(color); 181 | } 182 | }); 183 | } 184 | 185 | private void attrUnSelectedTextColor(TypedArray typedArray) { 186 | obtainColorAttr(typedArray, R.styleable.SegmentedControl_unSelectedTextColor, new Consumer() { 187 | @Override 188 | public void apply(Integer color) { 189 | getControllerComponent().setUnSelectedTextColor(color); 190 | } 191 | }); 192 | } 193 | 194 | private void attrTextSize(TypedArray typedArray) { 195 | int textSize = typedArray.getDimensionPixelSize(R.styleable.SegmentedControl_textSize, 196 | (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, 197 | getResources().getDisplayMetrics())); 198 | if (textSize > 0) { 199 | getControllerComponent().setTextSize(textSize); 200 | } 201 | } 202 | 203 | private void attrFontAssetPath(TypedArray typedArray) { 204 | String fontPath = typedArray.getString(R.styleable.SegmentedControl_fontAssetPath); 205 | if (fontPath != null && !fontPath.isEmpty()) { 206 | getControllerComponent().setTypeFace(Typeface.createFromAsset(getContext().getAssets(), fontPath)); 207 | } 208 | } 209 | 210 | private void attrTextVerticalPadding(TypedArray typedArray) { 211 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_textVerticalPadding, new Consumer() { 212 | @Override 213 | public void apply(Integer dimen) { 214 | getControllerComponent().setTextVerticalPadding(dimen); 215 | } 216 | }); 217 | } 218 | 219 | private void attrTextHorizontalPadding(TypedArray typedArray) { 220 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_textHorizontalPadding, new Consumer() { 221 | @Override 222 | public void apply(Integer dimen) { 223 | getControllerComponent().setTextHorizontalPadding(dimen); 224 | } 225 | }); 226 | } 227 | 228 | private void attrSegmentVerticalMargin(TypedArray typedArray) { 229 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_segmentVerticalMargin, new Consumer() { 230 | @Override 231 | public void apply(Integer dimen) { 232 | getControllerComponent().setSegmentVerticalMargin(dimen); 233 | } 234 | }); 235 | } 236 | 237 | private void attrSegmentHorizontalMargin(TypedArray typedArray) { 238 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_segmentHorizontalMargin, new Consumer() { 239 | @Override 240 | public void apply(Integer dimen) { 241 | getControllerComponent().setSegmentHorizontalMargin(dimen); 242 | } 243 | }); 244 | } 245 | 246 | private void attrRadius(TypedArray typedArray) { 247 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_radius, new Consumer() { 248 | @Override 249 | public void apply(Integer dimen) { 250 | getControllerComponent().setRadius(dimen); 251 | } 252 | }); 253 | } 254 | 255 | private void attrTopLeftRadius(TypedArray typedArray) { 256 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_topLeftRadius, new Consumer() { 257 | @Override 258 | public void apply(Integer dimen) { 259 | getControllerComponent().setTopLeftRadius(dimen); 260 | } 261 | }); 262 | } 263 | 264 | private void attrTopRightRadius(TypedArray typedArray) { 265 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_topRightRadius, new Consumer() { 266 | @Override 267 | public void apply(Integer dimen) { 268 | getControllerComponent().setTopRightRadius(dimen); 269 | } 270 | }); 271 | } 272 | 273 | private void attrBottomRightRadius(TypedArray typedArray) { 274 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_bottomRightRadius, new Consumer() { 275 | @Override 276 | public void apply(Integer dimen) { 277 | getControllerComponent().setBottomRightRadius(dimen); 278 | } 279 | }); 280 | } 281 | 282 | private void attrBottomLeftRadius(TypedArray typedArray) { 283 | obtainDimensionAttr(typedArray, R.styleable.SegmentedControl_bottomLeftRadius, new Consumer() { 284 | @Override 285 | public void apply(Integer dimen) { 286 | getControllerComponent().setBottomLeftRadius(dimen); 287 | } 288 | }); 289 | } 290 | 291 | private void attrRadiusForEverySegment(TypedArray typedArray) { 292 | boolean radiusForEverySegment = typedArray.getBoolean(R.styleable.SegmentedControl_radiusForEverySegment, false); 293 | getControllerComponent().setRadiusForEverySegment(radiusForEverySegment); 294 | } 295 | 296 | private void attrSegments(TypedArray typedArray) { 297 | CharSequence[] items = typedArray.getTextArray(R.styleable.SegmentedControl_segments); 298 | useDefaultAdapter(); 299 | //noinspection unchecked 300 | getControllerComponent().addSegments((D[]) items); 301 | } 302 | 303 | private void attrColumnCount(TypedArray typedArray) { 304 | int columnCount = typedArray.getInteger(R.styleable.SegmentedControl_columnCount, Configs.DEFAULT_COLUMN_COUNT); 305 | getControllerComponent().setColumnCount(columnCount); 306 | notifyConfigIsChanged(); 307 | } 308 | 309 | private void attrSupportedSelectionsCount(TypedArray typedArray) { 310 | int supportedSelectionsCount = typedArray.getInteger(R.styleable.SegmentedControl_supportedSelectionsCount, Configs.DEFAULT_SUPPORTED_SELECTIONS_COUNT); 311 | if (supportedSelectionsCount > 0) { 312 | getControllerComponent().setSupportedSelectionsCount(supportedSelectionsCount); 313 | } 314 | } 315 | 316 | private void attrDistributeEvenly(TypedArray typedArray) { 317 | boolean willDistribute = typedArray.getBoolean(R.styleable.SegmentedControl_distributeEvenly, false); 318 | getControllerComponent().setDistributeEvenly(willDistribute); 319 | getControllerComponent().notifyConfigIsChanged(); 320 | } 321 | 322 | private void attrReselectionEnabled(TypedArray typedArray) { 323 | boolean reselectionEnabled = typedArray.getBoolean(R.styleable.SegmentedControl_reselectionEnabled, true); 324 | getControllerComponent().setReselectionEnabled(reselectionEnabled); 325 | } 326 | 327 | private void obtainColorAttr(TypedArray typedArray, int attr, Consumer colorConsumer) { 328 | int color = typedArray.getColor(attr, -2); // -1 is white 329 | if (color != -2) { 330 | colorConsumer.apply(color); 331 | } 332 | } 333 | 334 | private void obtainDimensionAttr(TypedArray typedArray, int attr, Consumer dimensionConsumer) { 335 | int dimensionPixelSize = typedArray.getDimensionPixelSize(attr, -1); 336 | if (dimensionPixelSize != -1) { 337 | dimensionConsumer.apply(dimensionPixelSize); 338 | } 339 | } 340 | 341 | @NonNull 342 | @Override 343 | public SegmentedControlViewComponent createViewComponent(@NonNull LayoutInflater layoutInflater) { 344 | addView(new SectionLayout(getContext()), 0); 345 | return new SegmentedControlViewComponent<>(this); 346 | } 347 | 348 | @NonNull 349 | @Override 350 | public SegmentedControlControllerComponent createControllerComponent() { 351 | return new SegmentedControlControllerComponent<>(); 352 | } 353 | 354 | /** 355 | * Will enabled multy selection, default value is 1, only one item can be selected at the time 356 | * @param supportedSelectionsCount specifies the count of selected segments at the same time, 357 | * if the selection count is reached the elder selection 358 | * will be unselected 359 | */ 360 | public void setSupportedSelectionsCount(int supportedSelectionsCount) { 361 | Assert.supportedSelectionsCount(supportedSelectionsCount); 362 | getControllerComponent().setSupportedSelectionsCount(supportedSelectionsCount); 363 | } 364 | 365 | /** 366 | * @param columnCount The column count for SegmentedControl 367 | */ 368 | public void setColumnCount(int columnCount) { 369 | Assert.columnCount(columnCount); 370 | getControllerComponent().setColumnCount(columnCount); 371 | } 372 | 373 | /** 374 | * @param adapter, NonNull adapter (extended from {@link SegmentAdapter}) 375 | */ 376 | public void setAdapter(@NonNull SegmentAdapter adapter) { 377 | Assert.adapter(adapter); 378 | getControllerComponent().setAdapter(adapter); 379 | } 380 | 381 | /** 382 | * set the adapter {@link SegmentAdapter} with {@link segmented_control.widget.custom.android.com.segmentedcontrol.custom_segment.SegmentViewHolderImpl} view holder 383 | * where data D data type is String. use segmentedControl.addSegments(String[] segmentDataArray) or segmentedControl.addSegments(List segmentDataList) methods 384 | */ 385 | public void useDefaultAdapter() { 386 | setAdapter(new SegmentAdapterImpl()); 387 | } 388 | 389 | /** 390 | * Add segments to segmentController 391 | * 392 | * @param segmentData, specified array Data type 393 | */ 394 | public void addSegments(D[] segmentData) { 395 | getControllerComponent().addSegments(segmentData); 396 | } 397 | 398 | /** 399 | * Add segments to segmentController 400 | * 401 | * @param segmentData, specified list Data type 402 | */ 403 | public void addSegments(List segmentData) { 404 | getControllerComponent().addSegments(segmentData); 405 | } 406 | 407 | /** 408 | * @param willDistributeEvenly, true each section width with will be equal to each other, 409 | * false each section width will be measured depending on its content width; 410 | */ 411 | public void setDistributeEvenly(boolean willDistributeEvenly) { 412 | getControllerComponent().setDistributeEvenly(willDistributeEvenly); 413 | } 414 | 415 | /** 416 | * @param isEnabled if true second click to segment will not turn it from selected to unselected state, 417 | * if false the second click will turn it from selected to un selected state, 418 | * default value is true 419 | */ 420 | public void setReselectionEnabled(boolean isEnabled) { 421 | getControllerComponent().setReselectionEnabled(isEnabled); 422 | } 423 | 424 | /** 425 | * @param onSegmentClickListener, every time click event will be notified, 426 | * even if the segment is already selected 427 | */ 428 | public void addOnSegmentClickListener(OnSegmentClickListener onSegmentClickListener) { 429 | getControllerComponent().addOnSegmentClickListener(onSegmentClickListener); 430 | } 431 | 432 | /** 433 | * @param onSegmentClickListener, every time click event will be notified, 434 | * even if the segment is already selected 435 | *

436 | * Remove listener 437 | */ 438 | public void removeOnSegmentClickListener(OnSegmentClickListener onSegmentClickListener) { 439 | getControllerComponent().removeOnSegmentClickListener(onSegmentClickListener); 440 | } 441 | 442 | /** 443 | * @param onSegmentSelectedListener, event will be notified, when segment is selected and unSelected and reselected, 444 | * for more info check out {@link OnSegmentSelectedListener} class 445 | */ 446 | public void addOnSegmentSelectListener(OnSegmentSelectedListener onSegmentSelectedListener) { 447 | getControllerComponent().addOnSegmentSelectListener(onSegmentSelectedListener); 448 | } 449 | 450 | /** 451 | * @param onSegmentSelectedListener, event will be notified, when segment is selected and unSelected and reselected, 452 | * for more info check out {@link OnSegmentSelectedListener} class 453 | *

454 | * Remove listener 455 | */ 456 | public void removeOnSegmentSelectListener(OnSegmentSelectedListener onSegmentSelectedListener) { 457 | getControllerComponent().removeOnSegmentSelectListener(onSegmentSelectedListener); 458 | } 459 | 460 | /** 461 | * @param onSegmentSelectRequestListener, event will be triggered after click event and before selection event 462 | * for more info click out {@link OnSegmentSelectRequestListener} class 463 | */ 464 | public void setOnSegmentSelectRequestListener(OnSegmentSelectRequestListener onSegmentSelectRequestListener) { 465 | getControllerComponent().setOnSegmentSelectRequestListener(onSegmentSelectRequestListener); 466 | } 467 | 468 | /** 469 | * Removes all segments 470 | */ 471 | public void removeAllSegments() { 472 | getControllerComponent().removeAllSegments(); 473 | } 474 | 475 | /** 476 | * find the segment view holder with the segment absolute position 477 | * 478 | * @param position, the segment position 479 | * @return SegmentViewHolder instance for specified position 480 | */ 481 | public SegmentViewHolder findSegmentByAbsolutePosition(int position) { 482 | Assert.outOfBounds(position, size(), "SegmentedControl#findSegmentByAbsolutePosition"); 483 | return getControllerComponent().findSegmentByAbsolutePosition(position); 484 | } 485 | 486 | /** 487 | * find the segment with column number and row position 488 | * 489 | * @param column, Segment column number 490 | * @param row, Segment row position 491 | * @return SegmentViewHolder instance for specified column and row numbers 492 | */ 493 | public SegmentViewHolder findSegmentByColumnAndRow(int column, int row) { 494 | Assert.outOfBounds(getControllerComponent().getAbsolutePosition(column, row), size(), "SegmentedControl#setSelectedSegment"); 495 | return getControllerComponent().findSegmentByColumnAndRow(column, row); 496 | } 497 | 498 | /** 499 | * Select the selected position 500 | * 501 | * @param position, Segment position 502 | */ 503 | public void setSelectedSegment(int position) { 504 | Assert.outOfBounds(position, size(), "SegmentedControl#setSelectedSegment"); 505 | getControllerComponent().setSelectedSegment(position); 506 | } 507 | 508 | /** 509 | * Select the segment with specified column number and row position 510 | * 511 | * @param column, Segment column number 512 | * @param row, Segment row position 513 | */ 514 | public void setSelectedSegment(int column, int row) { 515 | Assert.outOfBounds(getControllerComponent().getAbsolutePosition(column, row), size(), "SegmentedControl#setSelectedSegment"); 516 | getControllerComponent().setSelectedSegment(column, row); 517 | } 518 | 519 | /** 520 | * Iterate on segments, and pass the view holders as an argument for {@link SegmentConsumer} 521 | * 522 | * @param segmentConsumer, Segment consumer 523 | */ 524 | public void forEachSegment(SegmentConsumer segmentConsumer) { 525 | getControllerComponent().forEachSegment(segmentConsumer); 526 | } 527 | 528 | public void setSelectedStrokeColor(int color) { 529 | getControllerComponent().setSelectedStrokeColor(color); 530 | } 531 | 532 | public void setUnSelectedStrokeColor(int color) { 533 | getControllerComponent().setUnSelectedStrokeColor(color); 534 | } 535 | 536 | public void setFocusedBackgroundColor(int color) { 537 | getControllerComponent().setFocusedBackgroundColor(color); 538 | } 539 | 540 | /** 541 | * Set segments selection animation duration value 542 | * 543 | * @param duration in milliseconds 544 | */ 545 | public void setSelectionAnimationDuration(int duration) { 546 | getControllerComponent().setSelectionAnimationDuration(duration); 547 | } 548 | 549 | public void setStrokeWidth(int width) { 550 | getControllerComponent().setStrokeWidth(width); 551 | } 552 | 553 | public void setSelectedBackgroundColor(int color) { 554 | getControllerComponent().setSelectedBackgroundColor(color); 555 | } 556 | 557 | public void setUnSelectedBackgroundColor(int color) { 558 | getControllerComponent().setUnSelectedBackgroundColor(color); 559 | } 560 | 561 | public void setSelectedTextColor(int color) { 562 | getControllerComponent().setSelectedTextColor(color); 563 | } 564 | 565 | public void setUnSelectedTextColor(int color) { 566 | getControllerComponent().setUnSelectedTextColor(color); 567 | } 568 | 569 | public void setTextSize(int textSize) { 570 | getControllerComponent().setTextSize(textSize); 571 | } 572 | 573 | public void setTypeFace(Typeface typeFace) { 574 | getControllerComponent().setTypeFace(typeFace); 575 | } 576 | 577 | public void setTextVerticalPadding(int padding) { 578 | getControllerComponent().setTextVerticalPadding(padding); 579 | } 580 | 581 | public void setTextHorizontalPadding(int padding) { 582 | getControllerComponent().setTextHorizontalPadding(padding); 583 | } 584 | 585 | public void setSegmentVerticalMargin(int margin) { 586 | getControllerComponent().setSegmentVerticalMargin(margin); 587 | } 588 | 589 | public void setSegmentHorizontalMargin(int margin) { 590 | getControllerComponent().setSegmentHorizontalMargin(margin); 591 | } 592 | 593 | public void setRadius(int radius) { 594 | getControllerComponent().setRadius(radius); 595 | } 596 | 597 | public void setTopLeftRadius(int radius) { 598 | getControllerComponent().setTopLeftRadius(radius); 599 | } 600 | 601 | public void setTopRightRadius(int radius) { 602 | getControllerComponent().setTopRightRadius(radius); 603 | } 604 | 605 | public void setBottomRightRadius(int radius) { 606 | getControllerComponent().setBottomRightRadius(radius); 607 | } 608 | 609 | public void setBottomLeftRadius(int radius) { 610 | getControllerComponent().setBottomLeftRadius(radius); 611 | } 612 | 613 | /** 614 | * @param radiusForEverySegment, true every segment corners will be rounded, false only top left,top right, bottom right and bottom left corners will be rounded 615 | */ 616 | public void setRadiusForEverySegment(boolean radiusForEverySegment) { 617 | getControllerComponent().setRadiusForEverySegment(radiusForEverySegment); 618 | } 619 | 620 | /** 621 | * Removes the last selected segment selection, SegmentViewHolder's onSegmentSelected method will be called 622 | * with isSelected = false, isReselected = false 623 | */ 624 | public void clearSelection() { 625 | getControllerComponent().clearSelection(false); 626 | } 627 | 628 | /** 629 | * Removes the last selected segment selection, SegmentViewHolder's onSegmentSelected method will be called 630 | * with isSelected = false, isReselected = false. 631 | * 632 | * @param notifySegmentSelectedListener if true SegmentSelectedListeners will be notified. 633 | */ 634 | public void clearSelection(boolean notifySegmentSelectedListener) { 635 | getControllerComponent().clearSelection(notifySegmentSelectedListener); 636 | } 637 | 638 | /** 639 | * Call this method after every configuration change 640 | * setColumnCount, setRadius et... 641 | */ 642 | public void notifyConfigIsChanged() { 643 | getControllerComponent().notifyConfigIsChanged(); 644 | } 645 | 646 | public int size() { 647 | return getControllerComponent().size(); 648 | } 649 | 650 | // public SegmentViewHolder getSelectedViewHolder() { 651 | // return getControllerComponent().getSelectedViewHolder(); 652 | // } 653 | 654 | /** 655 | * @return int[]{column,row} Section column and row numbers 656 | */ 657 | public int[] getLastSelectedColumnAndRow() { 658 | return getControllerComponent().getLastSelectedColumnAndRow(); 659 | } 660 | 661 | public int getLastSelectedAbsolutePosition() { 662 | return getControllerComponent().getLastSelectedAbsolutePosition(); 663 | } 664 | 665 | public boolean hasSelectedSegment() { 666 | return getControllerComponent().isSelected(); 667 | } 668 | } 669 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/SegmentedControlControllerComponent.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import android.graphics.Typeface; 4 | import android.support.annotation.Nullable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Queue; 11 | 12 | import section_layout.widget.custom.android.com.sectionlayout.SectionLayout; 13 | import section_layout.widget.custom.android.com.sectionlayout.distributive_section_layout.DistributiveSectionLayout; 14 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row.SegmentRowAdapter; 15 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row.SegmentRowViewHolder; 16 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentAdapter; 17 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentData; 18 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentDecoration; 19 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 20 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentClickListener; 21 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentSelectRequestListener; 22 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentSelectedListener; 23 | import view_component.lib_android.com.view_component.base_view.ControllerComponent; 24 | 25 | /** 26 | * Created by Robert Apikyan on 9/5/2017. 27 | */ 28 | 29 | class SegmentedControlControllerComponent extends ControllerComponent> { 30 | private final Configs configs = Configs.getDefault(); 31 | // private SegmentViewHolder lastClickedSegmentViewHolder; 32 | private LinkedList> selectedSegments = new LinkedList<>(); 33 | private final Notifier notifier = new Notifier<>(); 34 | private final List dataList = new ArrayList<>(); 35 | 36 | // private final OnSegmentClickListener _onSegmentClickListener = new OnSegmentClickListener() { 37 | // @Override 38 | // public void onSegmentClick(SegmentViewHolder segmentViewHolder) { 39 | // notifier.onSegmentClick(segmentViewHolder); 40 | // 41 | // if (segmentViewHolder.equals(lastClickedSegmentViewHolder)) { 42 | // // on section reselected 43 | // segmentViewHolder.setSelected(true); 44 | // notifier.onSegmentSelected(segmentViewHolder, true, true); 45 | // } else if (notifier.onSegmentSelectRequest(segmentViewHolder)) { 46 | // // on section selected 47 | // // unSelect the last one 48 | // if (lastClickedSegmentViewHolder != null) { 49 | // lastClickedSegmentViewHolder.setSelected(false); 50 | // notifier.onSegmentSelected(lastClickedSegmentViewHolder, false, false); 51 | // } 52 | // // select the current 53 | // lastClickedSegmentViewHolder = segmentViewHolder; 54 | // segmentViewHolder.setSelected(true); 55 | // notifier.onSegmentSelected(segmentViewHolder, true, false); 56 | // } 57 | // } 58 | // }; 59 | 60 | private final OnSegmentClickListener onSegmentClickListener = new OnSegmentClickListener() { 61 | @Override 62 | public void onSegmentClick(SegmentViewHolder segmentViewHolder) { 63 | notifier.onSegmentClick(segmentViewHolder); 64 | 65 | int index = selectedSegments.indexOf(segmentViewHolder); 66 | boolean contains = index != -1; 67 | if (contains) { 68 | // on section reselected 69 | SegmentViewHolder viewHolder = selectedSegments.get(index); 70 | if (configs.reselectionEnabled){ 71 | viewHolder.setSelected(true); 72 | notifier.onSegmentSelected(segmentViewHolder, true, true); 73 | }else{ 74 | selectedSegments.remove(index); 75 | viewHolder.setSelected(false); 76 | notifier.onSegmentSelected(segmentViewHolder, false, false); 77 | } 78 | 79 | } else if (notifier.onSegmentSelectRequest(segmentViewHolder)) { 80 | // on section selected 81 | // unSelect the last one 82 | SegmentViewHolder lastSelected = addSelectedSegmentViewHolder(segmentViewHolder); 83 | if (lastSelected != null) { 84 | lastSelected.setSelected(false); 85 | notifier.onSegmentSelected(lastSelected, false, false); 86 | } 87 | // select the current 88 | segmentViewHolder.setSelected(true); 89 | notifier.onSegmentSelected(segmentViewHolder, true, false); 90 | } 91 | } 92 | }; 93 | 94 | /** 95 | * Watch the supported selections count, if limit is reached the oldest segment will be removed from 96 | * selections list and returned back to method caller 97 | * 98 | * @param segmentViewHolder new selected segment; 99 | * @return oldest selection 100 | */ 101 | @Nullable 102 | private SegmentViewHolder addSelectedSegmentViewHolder(SegmentViewHolder segmentViewHolder) { 103 | selectedSegments.add(segmentViewHolder); 104 | if (selectedSegments.size() > configs.supportedSelectionsCount) { 105 | return selectedSegments.remove(0); 106 | } 107 | return null; 108 | } 109 | 110 | private SegmentViewHolder getLastSelectedViewHolder() { 111 | return selectedSegments.size() > 0 ? selectedSegments.getLast() : null; 112 | } 113 | 114 | private void addSegment(D segmentData) { 115 | if (getVerticalSectionLayout().size() == 0 || !canAddToLastRow()) { 116 | addNewRow(); 117 | } 118 | 119 | addSegmentToLastRow(segmentData); 120 | } 121 | 122 | // removeLastSelected = true 123 | // private void removeAllSegments(boolean removeLastSelected) { 124 | // for (int row = 0; row < getVerticalSectionLayout().size(); row++) { 125 | // getHorizontalSectionLayout(row).removeAllSections(); 126 | // } 127 | // getVerticalSectionLayout().removeAllSections(); 128 | // dataList.clear(); 129 | // 130 | // if (removeLastSelected) { 131 | // lastClickedSegmentViewHolder = null; 132 | // } 133 | // } 134 | 135 | private void removeAllSegments(boolean removeLastSelected) { 136 | for (int row = 0; row < getVerticalSectionLayout().size(); row++) { 137 | getHorizontalSectionLayout(row).removeAllSections(); 138 | } 139 | getVerticalSectionLayout().removeAllSections(); 140 | dataList.clear(); 141 | 142 | if (removeLastSelected) { 143 | selectedSegments.clear(); 144 | } 145 | } 146 | 147 | private void addSegmentToLastRow(D segmentData) { 148 | DistributiveSectionLayout> horizontalSectionLayout = getHorizontalSectionLayout(getLastRowIndex()); 149 | horizontalSectionLayout 150 | .addSection(SegmentData.create(segmentData, onSegmentClickListener, getAbsolutePosition(getLastHorizontalSectionLayout().size(), 151 | getVerticalSectionLayout().size() - 1), getLastRowIndex(), horizontalSectionLayout.size(), size(), configs.columnCount, configs.segmentDecoration)); 152 | horizontalSectionLayout.requestLayout(); 153 | } 154 | 155 | private int[] getRowAndColumnWithAbsolutePosition(int position) { 156 | int smallDiff = position % configs.columnCount; 157 | int diff = position - smallDiff; 158 | int row = (diff / configs.columnCount) + (smallDiff == configs.columnCount ? 1 : 0); 159 | int column = position % configs.columnCount; 160 | return new int[]{row, column}; 161 | } 162 | 163 | void notifyConfigIsChanged() { 164 | recreate(false); 165 | } 166 | 167 | // public void clearSelection(boolean notifySegmentSelectedListener) { 168 | // if (lastClickedSegmentViewHolder != null) { 169 | // lastClickedSegmentViewHolder.setSelected(false); 170 | // 171 | // if (notifySegmentSelectedListener) { 172 | // notifier.onSegmentSelected(lastClickedSegmentViewHolder, false, false); 173 | // } 174 | // 175 | // lastClickedSegmentViewHolder = null; 176 | // } 177 | // } 178 | 179 | public void clearSelection(boolean notifySegmentSelectedListener) { 180 | if (!selectedSegments.isEmpty()) { 181 | for (SegmentViewHolder selectedSegment : selectedSegments) { 182 | selectedSegment.setSelected(false); 183 | 184 | if (notifySegmentSelectedListener) { 185 | notifier.onSegmentSelected(selectedSegment, false, false); 186 | } 187 | } 188 | 189 | selectedSegments.clear(); 190 | } 191 | } 192 | 193 | // private void recreate(boolean removeLastSelected) { 194 | // if (dataList.size() == 0) return; 195 | // List itemsData = new ArrayList<>(dataList); 196 | // removeAllSegments(removeLastSelected); 197 | // addSegments(itemsData); 198 | // if (lastClickedSegmentViewHolder != null) { 199 | // setSelectedSegment(lastClickedSegmentViewHolder.getAbsolutePosition()); 200 | // } 201 | // } 202 | 203 | private void recreate(boolean removeLastSelected) { 204 | if (dataList.size() == 0) return; 205 | List itemsData = new ArrayList<>(dataList); 206 | removeAllSegments(removeLastSelected); 207 | addSegments(itemsData); 208 | 209 | SegmentViewHolder lastSelection = getLastSelectedViewHolder(); 210 | if (lastSelection != null) { 211 | setSelectedSegment(lastSelection.getAbsolutePosition()); 212 | } 213 | } 214 | 215 | private boolean canAddToLastRow() { 216 | return getHorizontalSectionLayout(getLastRowIndex()).size() < configs.columnCount; 217 | } 218 | 219 | private void addNewRow() { 220 | //noinspection unchecked 221 | getVerticalSectionLayout().addSection(configs.willDistributeEvenly); 222 | } 223 | 224 | private SectionLayout getVerticalSectionLayout() { 225 | //noinspection ConstantConditions 226 | return getViewComponent().verticalSectionLayout; 227 | } 228 | 229 | private DistributiveSectionLayout> getHorizontalSectionLayout(int row) { 230 | //noinspection unchecked 231 | SegmentRowViewHolder segmentedViewHolder = (SegmentRowViewHolder) getVerticalSectionLayout().getViewHolderForAdapterPosition(row); 232 | return segmentedViewHolder.getDistributiveSectionLayout(); 233 | } 234 | 235 | private DistributiveSectionLayout> getLastHorizontalSectionLayout() { 236 | return getHorizontalSectionLayout(getLastRowIndex()); 237 | } 238 | 239 | private int getLastRowIndex() { 240 | return getVerticalSectionLayout().size() - 1; 241 | } 242 | 243 | SegmentViewHolder findSegmentByAbsolutePosition(int position) { 244 | int[] point = getRowAndColumnWithAbsolutePosition(position); 245 | return findSegmentByColumnAndRow(point[0], point[1]); 246 | } 247 | 248 | SegmentViewHolder findSegmentByColumnAndRow(int column, int row) { 249 | return (SegmentViewHolder) getHorizontalSectionLayout(column).getViewHolderForAdapterPosition(row); 250 | } 251 | 252 | void forEachSegment(SegmentConsumer segmentConsumer) { 253 | for (int row = 0; row < getVerticalSectionLayout().size(); row++) { 254 | DistributiveSectionLayout> horizontalSectionLayout = getHorizontalSectionLayout(row); 255 | for (int column = 0; column < horizontalSectionLayout.size(); column++) { 256 | segmentConsumer.apply(findSegmentByColumnAndRow(row, column)); 257 | } 258 | } 259 | } 260 | 261 | void setAccentColor(int color) { 262 | configs.segmentDecoration = SegmentDecoration.createDefault(getContext(), color); 263 | } 264 | 265 | void setSelectedStrokeColor(int color) { 266 | configs.segmentDecoration.selectedStrokeColor = color; 267 | } 268 | 269 | void setUnSelectedStrokeColor(int color) { 270 | configs.segmentDecoration.unSelectedStrokeColor = color; 271 | } 272 | 273 | void setStrokeWidth(int width) { 274 | configs.segmentDecoration.strokeWidth = width; 275 | } 276 | 277 | void setSelectedBackgroundColor(int color) { 278 | configs.segmentDecoration.selectBackgroundColor = color; 279 | } 280 | 281 | void setUnSelectedBackgroundColor(int color) { 282 | configs.segmentDecoration.unSelectedBackgroundColor = color; 283 | } 284 | 285 | void setFocusedBackgroundColor(int color) { 286 | configs.segmentDecoration.focusedBackgroundColor = color; 287 | } 288 | 289 | void setReselectionEnabled(boolean isEnabled){ 290 | configs.reselectionEnabled = isEnabled; 291 | } 292 | 293 | void setSelectionAnimationDuration(int duration) { 294 | configs.segmentDecoration.selectionAnimationDuration = duration; 295 | } 296 | 297 | void setSelectedTextColor(int color) { 298 | configs.segmentDecoration.selectedTextColor = color; 299 | } 300 | 301 | void setUnSelectedTextColor(int color) { 302 | configs.segmentDecoration.unSelectedTextColor = color; 303 | } 304 | 305 | void setTextSize(int textSize) { 306 | configs.segmentDecoration.textSize = textSize; 307 | } 308 | 309 | void setTypeFace(Typeface typeFace) { 310 | configs.segmentDecoration.typeface = typeFace; 311 | } 312 | 313 | void setTextVerticalPadding(int padding) { 314 | configs.segmentDecoration.textVerticalPadding = padding; 315 | } 316 | 317 | void setTextHorizontalPadding(int padding) { 318 | configs.segmentDecoration.textHorizontalPadding = padding; 319 | } 320 | 321 | void setSegmentVerticalMargin(int margin) { 322 | configs.segmentDecoration.segmentVerticalMargin = margin; 323 | } 324 | 325 | void setSegmentHorizontalMargin(int margin) { 326 | configs.segmentDecoration.segmentHorizontalMargin = margin; 327 | } 328 | 329 | void setRadius(int radius) { 330 | setTopLeftRadius(radius); 331 | setTopRightRadius(radius); 332 | setBottomRightRadius(radius); 333 | setBottomLeftRadius(radius); 334 | } 335 | 336 | void setTopLeftRadius(int radius) { 337 | configs.segmentDecoration.topLeftRadius = radius; 338 | } 339 | 340 | void setTopRightRadius(int radius) { 341 | configs.segmentDecoration.topRightRadius = radius; 342 | } 343 | 344 | void setBottomRightRadius(int radius) { 345 | configs.segmentDecoration.bottomRightRadius = radius; 346 | } 347 | 348 | void setBottomLeftRadius(int radius) { 349 | configs.segmentDecoration.bottomLeftRadius = radius; 350 | } 351 | 352 | void setRadiusForEverySegment(boolean radiusForEverySegment) { 353 | configs.segmentDecoration.radiusForEverySegment = radiusForEverySegment; 354 | } 355 | 356 | void setAdapter(SegmentAdapter adapter) { 357 | //noinspection ConstantConditions, setAdapter will be called from SegmentedControl 358 | getViewComponent().verticalSectionLayout.withAdapter(new SegmentRowAdapter(adapter)); 359 | } 360 | 361 | void addSegments(D[] segmentDataArray) { 362 | if (segmentDataArray == null || segmentDataArray.length == 0) return; 363 | addSegments(new ArrayList<>(Arrays.asList(segmentDataArray))); 364 | } 365 | 366 | void addSegments(List segmentDataList) { 367 | if (segmentDataList == null || segmentDataList.size() == 0) return; 368 | 369 | dataList.addAll(new ArrayList<>(segmentDataList)); 370 | 371 | for (D segmentData : dataList) { 372 | addSegment(segmentData); 373 | } 374 | } 375 | 376 | void removeAllSegments() { 377 | removeAllSegments(true); 378 | } 379 | 380 | void setDistributeEvenly(boolean willDistributeEvenly) { 381 | configs.willDistributeEvenly = willDistributeEvenly; 382 | } 383 | 384 | void setSupportedSelectionsCount(int supportedSelectionsCount) { 385 | configs.supportedSelectionsCount = supportedSelectionsCount; 386 | } 387 | 388 | void setColumnCount(int columnCount) { 389 | configs.columnCount = columnCount; 390 | } 391 | 392 | void addOnSegmentClickListener(OnSegmentClickListener onSegmentClickListener) { 393 | notifier.addOnSegmentClickListener(onSegmentClickListener); 394 | } 395 | 396 | void removeOnSegmentClickListener(OnSegmentClickListener onSegmentClickListener) { 397 | notifier.removeOnSegmentClickListener(onSegmentClickListener); 398 | } 399 | 400 | void addOnSegmentSelectListener(OnSegmentSelectedListener onSegmentSelectedListener) { 401 | notifier.addOnSegmentSelectListener(onSegmentSelectedListener); 402 | } 403 | 404 | void removeOnSegmentSelectListener(OnSegmentSelectedListener onSegmentSelectedListener) { 405 | notifier.removeOnSegmentSelectListener(onSegmentSelectedListener); 406 | } 407 | 408 | void setOnSegmentSelectRequestListener(OnSegmentSelectRequestListener onSegmentSelectRequestListener) { 409 | notifier.setOnSegmentSelectRequestListener(onSegmentSelectRequestListener); 410 | } 411 | 412 | void setSelectedSegment(int absolutePosition) { 413 | int[] point = getRowAndColumnWithAbsolutePosition(absolutePosition); 414 | setSelectedSegment(point[0], point[1]); 415 | } 416 | 417 | void setSelectedSegment(int column, int row) { 418 | onSegmentClickListener.onSegmentClick(findSegmentByColumnAndRow(column, row)); 419 | } 420 | 421 | // SegmentViewHolder getSelectedViewHolder() { 422 | // return lastClickedSegmentViewHolder; 423 | // } 424 | 425 | List> getSelectedViewHolders() { 426 | return selectedSegments; 427 | } 428 | 429 | int[] getLastSelectedColumnAndRow() { 430 | SegmentViewHolder lastSelection = getLastSelectedViewHolder(); 431 | if (lastSelection != null) { 432 | return new int[]{lastSelection.getColumn(), lastSelection.getRow()}; 433 | } else { 434 | return new int[]{-1, -1}; 435 | } 436 | } 437 | 438 | // 439 | int getLastSelectedAbsolutePosition() { 440 | SegmentViewHolder lastSelection = getLastSelectedViewHolder(); 441 | if (lastSelection != null) { 442 | return lastSelection.getAbsolutePosition(); 443 | } else { 444 | return -1; 445 | } 446 | } 447 | 448 | // boolean isSelected() { 449 | // return lastClickedSegmentViewHolder != null; 450 | // } 451 | 452 | boolean isSelected() { 453 | return !selectedSegments.isEmpty(); 454 | } 455 | 456 | int size() { 457 | return dataList.size(); 458 | } 459 | 460 | int getAbsolutePosition(int column, int row) { 461 | return row * configs.columnCount + column; 462 | } 463 | } 464 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/SegmentedControlViewComponent.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.view.View; 5 | import android.widget.LinearLayout; 6 | 7 | import section_layout.widget.custom.android.com.sectionlayout.SectionLayout; 8 | import view_component.lib_android.com.view_component.base_view.ViewComponent; 9 | 10 | /** 11 | * Created by Robert Apikyan on 9/5/2017. 12 | */ 13 | 14 | class SegmentedControlViewComponent extends ViewComponent { 15 | final SectionLayout verticalSectionLayout; 16 | 17 | SegmentedControlViewComponent(@NonNull View rootView) { 18 | super(rootView); 19 | //noinspection unchecked 20 | verticalSectionLayout = (SectionLayout) getRootViewGroup().getChildAt(0); 21 | verticalSectionLayout.setOrientation(LinearLayout.VERTICAL); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/custom_segment/SegmentAdapterImpl.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.custom_segment; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import segmented_control.widget.custom.android.com.segmented_control.R; 8 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentAdapter; 9 | 10 | /** 11 | * Created by Robert Apikyan on 9/8/2017. 12 | */ 13 | 14 | public class SegmentAdapterImpl extends SegmentAdapter { 15 | 16 | @NonNull 17 | @Override 18 | protected SegmentViewHolderImpl onCreateViewHolder(@NonNull LayoutInflater layoutInflater, ViewGroup viewGroup, int i) { 19 | return new SegmentViewHolderImpl(layoutInflater.inflate(R.layout.item_segment_impl, null)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/custom_segment/SegmentViewHolderImpl.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.custom_segment; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.annotation.SuppressLint; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Build; 7 | import android.support.annotation.NonNull; 8 | import android.util.TypedValue; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.TextView; 13 | 14 | import segmented_control.widget.custom.android.com.segmented_control.R; 15 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 16 | 17 | import static segmented_control.widget.custom.android.com.segmentedcontrol.utils.Utils.createBackgroundAnimation; 18 | import static segmented_control.widget.custom.android.com.segmentedcontrol.utils.Utils.createRadius; 19 | import static segmented_control.widget.custom.android.com.segmentedcontrol.utils.Utils.defineRadiusForPosition; 20 | import static segmented_control.widget.custom.android.com.segmentedcontrol.utils.Utils.getBackground; 21 | import static segmented_control.widget.custom.android.com.segmentedcontrol.utils.Utils.isInBounds; 22 | 23 | /** 24 | * Created by Robert Apikyan on 9/8/2017. 25 | */ 26 | 27 | public class SegmentViewHolderImpl extends SegmentViewHolder { 28 | 29 | private TextView itemTV; 30 | private float[] radius; 31 | private ValueAnimator va; 32 | private int[] windowLocation; 33 | 34 | private final ValueAnimator.AnimatorUpdateListener bgAnimListener = new ValueAnimator.AnimatorUpdateListener() { 35 | @Override 36 | public void onAnimationUpdate(ValueAnimator animation) { 37 | int colorArgb = (int) animation.getAnimatedValue(); 38 | Drawable bg = getBackground(getStrokeWidth(), isSelected() ? getSelectedStrokeColor() : getUnSelectedStrokeColor(), colorArgb, radius); 39 | setBackground(bg); 40 | } 41 | }; 42 | 43 | @SuppressWarnings("FieldCanBeLocal") 44 | private final View.OnTouchListener segmentTouchListener = new View.OnTouchListener() { 45 | @SuppressLint("ClickableViewAccessibility") 46 | @Override 47 | public boolean onTouch(View v, MotionEvent event) { 48 | if (isSelected()) { 49 | return false; 50 | } 51 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 52 | setBackground(getFocusedBackground()); 53 | 54 | } else if (event.getAction() == MotionEvent.ACTION_UP || 55 | event.getAction() == MotionEvent.ACTION_CANCEL || 56 | event.getAction() == MotionEvent.ACTION_POINTER_UP || 57 | event.getAction() == MotionEvent.ACTION_OUTSIDE) { 58 | 59 | if (!isInBounds(event.getX(), event.getY(), windowLocation[0], windowLocation[1], 60 | getSectionView().getMeasuredWidth(), getSectionView().getMeasuredHeight())) { 61 | 62 | setBackground(isSelected() ? getSelectedBackground() : getUnSelectedBackground()); 63 | } 64 | } 65 | return false; 66 | } 67 | }; 68 | 69 | public SegmentViewHolderImpl(@NonNull View sectionView) { 70 | super(sectionView); 71 | itemTV = sectionView.findViewById(R.id.item_segment_tv); 72 | sectionView.setOnTouchListener(segmentTouchListener); 73 | } 74 | 75 | @Override 76 | protected void onSegmentBind(CharSequence segmentData) { 77 | initScreenLocation(); 78 | itemTV.setText(segmentData); 79 | if (isRadiusForEverySegment()) { 80 | radius = createRadius(getTopLeftRadius(), getTopRightRadius(), getBottomRightRadius(), getBottomLeftRadius()); 81 | } else { 82 | radius = defineRadiusForPosition(getAbsolutePosition(), getColumnCount(), getCurrentSize(), getTopLeftRadius(), getTopRightRadius(), getBottomRightRadius(), getBottomLeftRadius()); 83 | } 84 | setSectionDecorationSelected(false, false); 85 | itemTV.setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize()); 86 | if (getTypeFace() != null) { 87 | itemTV.setTypeface(getTypeFace()); 88 | } 89 | itemTV.setPadding(getTextHorizontalPadding(), getTextVerticalPadding(), getTextHorizontalPadding(), getTextVerticalPadding()); 90 | ((ViewGroup.MarginLayoutParams) itemTV.getLayoutParams()).setMargins(getSegmentHorizontalMargin(), getSegmentVerticalMargin(), getSegmentHorizontalMargin(), getSegmentVerticalMargin()); 91 | } 92 | 93 | private void initScreenLocation() { 94 | windowLocation = new int[2]; 95 | getSectionView().getLocationOnScreen(windowLocation); 96 | } 97 | 98 | @Override 99 | public void onSegmentSelected(boolean isSelected, boolean isReselected) { 100 | super.onSegmentSelected(isSelected, isReselected); 101 | setSectionDecorationSelected(isSelected, isReselected); 102 | } 103 | 104 | private Drawable getSelectedBackground() { 105 | return getBackground(getStrokeWidth(), getSelectedStrokeColor(), getSelectBackgroundColor(), radius); 106 | } 107 | 108 | private Drawable getUnSelectedBackground() { 109 | return getBackground(getStrokeWidth(), getUnSelectedStrokeColor(), getUnSelectedBackgroundColor(), radius); 110 | } 111 | 112 | private Drawable getFocusedBackground() { 113 | return getBackground(getStrokeWidth(), isSelected() ? getSelectedStrokeColor() : getUnSelectedStrokeColor(), getFocusedBackgroundColor(), radius); 114 | } 115 | 116 | private void setSectionDecorationSelected(boolean isSelected, boolean isReselected) { 117 | if (isReselected) 118 | return; 119 | 120 | if (hasBackground()) { 121 | animateNewBackground(isSelected); 122 | } else { 123 | setBackground(isSelected ? getSelectedBackground() : getUnSelectedBackground()); 124 | } 125 | 126 | itemTV.setTextColor(isSelected ? getSelectedTextColor() : getUnSelectedTextColor()); 127 | } 128 | 129 | private void animateNewBackground(boolean isSelected) { 130 | 131 | if (va != null) { 132 | va.end(); 133 | va.removeUpdateListener(bgAnimListener); 134 | } 135 | 136 | // animate 137 | int startColor = isSelected ? getFocusedBackgroundColor() : getSelectBackgroundColor(); 138 | int endColor = isSelected ? getSelectBackgroundColor() : getUnSelectedBackgroundColor(); 139 | 140 | va = createBackgroundAnimation(startColor, endColor); 141 | 142 | va.addUpdateListener(bgAnimListener); 143 | 144 | va.setDuration(getSelectionAnimationDuration()); 145 | 146 | va.start(); 147 | } 148 | 149 | private void setBackground(Drawable drawable) { 150 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 151 | itemTV.setBackground(drawable); 152 | } else { 153 | //noinspection deprecation 154 | itemTV.setBackgroundDrawable(drawable); 155 | } 156 | } 157 | 158 | private boolean hasBackground() { 159 | return itemTV.getBackground() != null; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row/SegmentRowAdapter.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import section_layout.widget.custom.android.com.sectionlayout.SectionLayout; 8 | import section_layout.widget.custom.android.com.sectionlayout.distributive_section_layout.DistributiveSectionLayout; 9 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentAdapter; 10 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentData; 11 | 12 | /** 13 | * Created by Robert Apikyan on 9/7/2017. 14 | */ 15 | 16 | public class SegmentRowAdapter extends SectionLayout.Adapter>> { 17 | 18 | private final SegmentAdapter segmentAdapter; 19 | 20 | public SegmentRowAdapter(SegmentAdapter segmentAdapter) { 21 | this.segmentAdapter = segmentAdapter; 22 | } 23 | 24 | @NonNull 25 | @Override 26 | protected SegmentRowViewHolder> onCreateViewHolder(@NonNull LayoutInflater layoutInflater, ViewGroup viewGroup, int i) { 27 | return new SegmentRowViewHolder<>(new DistributiveSectionLayout>(layoutInflater.getContext()), segmentAdapter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row/SegmentRowViewHolder.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.view.View; 5 | import android.widget.LinearLayout; 6 | 7 | import section_layout.widget.custom.android.com.sectionlayout.SectionLayout; 8 | import section_layout.widget.custom.android.com.sectionlayout.distributive_section_layout.DistributiveSectionLayout; 9 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentAdapter; 10 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentData; 11 | 12 | /** 13 | * Created by Robert Apikyan on 9/7/2017. 14 | */ 15 | 16 | public class SegmentRowViewHolder extends SectionLayout.ViewHolder { 17 | private final DistributiveSectionLayout> distributiveSectionLayout; 18 | 19 | SegmentRowViewHolder(@NonNull View sectionView, SegmentAdapter segmentAdapter) { 20 | super(sectionView); 21 | //noinspection unchecked 22 | distributiveSectionLayout = (DistributiveSectionLayout>) sectionView; 23 | distributiveSectionLayout.setOrientation(LinearLayout.HORIZONTAL); 24 | distributiveSectionLayout.withAdapter(segmentAdapter); 25 | } 26 | 27 | @Override 28 | protected void onBind(Boolean willDistributeEvenly) { 29 | distributiveSectionLayout.distributeEvenly(willDistributeEvenly); 30 | } 31 | 32 | public DistributiveSectionLayout> getDistributiveSectionLayout() { 33 | return distributiveSectionLayout; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row_column/SegmentAdapter.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column; 2 | 3 | import section_layout.widget.custom.android.com.sectionlayout.distributive_section_layout.DistributiveSectionLayout; 4 | 5 | /** 6 | * Created by Robert Apikyan on 9/7/2017. 7 | */ 8 | 9 | public abstract class SegmentAdapter> extends DistributiveSectionLayout.Adapter, VH> { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row_column/SegmentBackgroundType.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column; 2 | 3 | import android.support.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.BOTTOM_LEFT_BG; 9 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.BOTTOM_RIGHT_BG; 10 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.BOTTOM_SINGLE_BG; 11 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.MIDDLE_BG; 12 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.SINGLE_BG; 13 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.TOP_LEFT_BG; 14 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.TOP_LEFT_SINGLE_BG; 15 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.TOP_RIGHT_BG; 16 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.TOP_RIGHT_SINGLE_BG; 17 | import static segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType.TOP_SINGLE_BG; 18 | 19 | /** 20 | * Created by Robert Apikyan on 9/12/2017. 21 | */ 22 | @IntDef(value = {SINGLE_BG, 23 | TOP_SINGLE_BG, 24 | TOP_LEFT_BG, 25 | TOP_LEFT_SINGLE_BG, 26 | TOP_RIGHT_SINGLE_BG, 27 | TOP_RIGHT_BG, 28 | MIDDLE_BG, 29 | BOTTOM_SINGLE_BG, 30 | BOTTOM_LEFT_BG, 31 | BOTTOM_RIGHT_BG}) 32 | @Retention(RetentionPolicy.SOURCE) 33 | public @interface SegmentBackgroundType { 34 | int SINGLE_BG = 0; 35 | int TOP_SINGLE_BG = 1; 36 | int TOP_LEFT_BG = 2; 37 | int TOP_LEFT_SINGLE_BG = 3; 38 | int TOP_RIGHT_SINGLE_BG = 4; 39 | int TOP_RIGHT_BG = 5; 40 | int MIDDLE_BG = 6; 41 | int BOTTOM_SINGLE_BG = 7; 42 | int BOTTOM_LEFT_BG = 8; 43 | int BOTTOM_RIGHT_BG = 9; 44 | } 45 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row_column/SegmentData.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column; 2 | 3 | import android.graphics.Typeface; 4 | 5 | import segmented_control.widget.custom.android.com.segmentedcontrol.listeners.OnSegmentClickListener; 6 | 7 | /** 8 | * Created by Robert Apikyan on 9/7/2017. 9 | */ 10 | 11 | public class SegmentData { 12 | boolean isSelected; 13 | int absolutePosition; 14 | 15 | private int row; 16 | private int column; 17 | 18 | private D segmentData; 19 | private OnSegmentClickListener onSegmentClickListener; 20 | 21 | private SegmentDecoration segmentDecoration; 22 | private int currentSize; 23 | private int columnCount; 24 | 25 | public static SegmentData create(D segmentData, OnSegmentClickListener onSegmentClickListener, int absolutePosition, int row, int column, int currentSize, int columnCount, SegmentDecoration segmentDecoration) { 26 | SegmentData sd = new SegmentData<>(); 27 | sd.segmentData = segmentData; 28 | sd.absolutePosition = absolutePosition; 29 | sd.row = row; 30 | sd.column = column; 31 | sd.currentSize = currentSize; 32 | sd.columnCount = columnCount; 33 | sd.segmentDecoration = segmentDecoration; 34 | sd.onSegmentClickListener = onSegmentClickListener; 35 | return sd; 36 | } 37 | 38 | public boolean isSelected() { 39 | return isSelected; 40 | } 41 | 42 | public int getAbsolutePosition() { 43 | return absolutePosition; 44 | } 45 | 46 | public int getRow() { 47 | return row; 48 | } 49 | 50 | public int getColumn() { 51 | return column; 52 | } 53 | 54 | public D getSegmentData() { 55 | return segmentData; 56 | } 57 | 58 | public OnSegmentClickListener getOnSegmentClickListener() { 59 | return onSegmentClickListener; 60 | } 61 | 62 | public int getSelectedStrokeColor() { 63 | return segmentDecoration.getSelectedStrokeColor(); 64 | } 65 | 66 | public int getUnSelectedStrokeColor() { 67 | return segmentDecoration.getUnSelectedStrokeColor(); 68 | } 69 | 70 | public int getStrokeWidth() { 71 | return segmentDecoration.getStrokeWidth(); 72 | } 73 | 74 | public int getSelectBackgroundColor() { 75 | return segmentDecoration.getSelectBackgroundColor(); 76 | } 77 | 78 | public int getUnSelectedBackgroundColor() { 79 | return segmentDecoration.getUnSelectedBackgroundColor(); 80 | } 81 | 82 | public int getFocusedBackgroundColor(){ 83 | return segmentDecoration.getFocusedBackgroundColor(); 84 | } 85 | 86 | public int getSelectionAnimationDuration(){ 87 | return segmentDecoration.getSelectionAnimationDuration(); 88 | } 89 | 90 | public int getSelectedTextColor() { 91 | return segmentDecoration.getSelectedTextColor(); 92 | } 93 | 94 | public int getUnSelectedTextColor() { 95 | return segmentDecoration.getUnSelectedTextColor(); 96 | } 97 | 98 | public int getTextSize() { 99 | return segmentDecoration.getTextSize(); 100 | } 101 | 102 | public Typeface getTypeFace(){ 103 | return segmentDecoration.getTypeface(); 104 | } 105 | 106 | public int getTextHorizontalPadding() { 107 | return segmentDecoration.getTextHorizontalPadding(); 108 | } 109 | 110 | public int getTextVerticalPadding() { 111 | return segmentDecoration.getTextVerticalPadding(); 112 | } 113 | 114 | public int getSegmentVerticalMargin() { 115 | return segmentDecoration.getSegmentVerticalMargin(); 116 | } 117 | 118 | public int getSegmentHorizontalMargin() { 119 | return segmentDecoration.getSegmentHorizontalMargin(); 120 | } 121 | 122 | public int getTopLeftRadius() { 123 | return segmentDecoration.getTopLeftRadius(); 124 | } 125 | 126 | public int getTopRightRadius() { 127 | return segmentDecoration.getTopRightRadius(); 128 | } 129 | 130 | public int getBottomRightRadius() { 131 | return segmentDecoration.getBottomRightRadius(); 132 | } 133 | 134 | public int getBottomLeftRadius() { 135 | return segmentDecoration.getBottomLeftRadius(); 136 | } 137 | 138 | public boolean isRadiusForEverySegment() { 139 | return segmentDecoration.isRadiusForEverySegment(); 140 | } 141 | 142 | public int getCurrentSize() { 143 | return currentSize; 144 | } 145 | 146 | public int getColumnCount() { 147 | return columnCount; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row_column/SegmentDecoration.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.Typeface; 6 | import android.support.annotation.ColorInt; 7 | import android.support.v4.content.ContextCompat; 8 | 9 | /** 10 | * Created by Robert Apikyan on 9/11/2017. 11 | */ 12 | 13 | public class SegmentDecoration { 14 | public static final int DEFAULT_FOCUSED_BACKGROUND_ALPHA = 36; 15 | public static final int DEFAULT_SELECTION_ANIMATION_DURATION = 196; 16 | 17 | // segment decoration 18 | // stroke 19 | @ColorInt 20 | public int selectedStrokeColor; 21 | @ColorInt 22 | public int unSelectedStrokeColor; 23 | public int strokeWidth; 24 | // background 25 | @ColorInt 26 | public int selectBackgroundColor; 27 | @ColorInt 28 | public int unSelectedBackgroundColor; 29 | // focused segment background 30 | public int focusedBackgroundColor; 31 | // selection Animation Duration 32 | public int selectionAnimationDuration; 33 | // text 34 | @ColorInt 35 | public int selectedTextColor; 36 | @ColorInt 37 | public int unSelectedTextColor; 38 | public int textSize; 39 | // text padding 40 | public int textHorizontalPadding; 41 | public int textVerticalPadding; 42 | 43 | // segment margins 44 | public int segmentVerticalMargin; 45 | public int segmentHorizontalMargin; 46 | 47 | // segment font type 48 | public Typeface typeface; 49 | 50 | // radius 51 | public int topLeftRadius; 52 | public int topRightRadius; 53 | public int bottomRightRadius; 54 | public int bottomLeftRadius; 55 | public boolean radiusForEverySegment = false; // def. value 56 | 57 | public static SegmentDecoration createDefault(Context context, int accentColor) { 58 | SegmentDecoration sd = new SegmentDecoration(); 59 | sd.selectedStrokeColor = accentColor; 60 | sd.unSelectedStrokeColor = accentColor; 61 | sd.selectBackgroundColor = accentColor; 62 | sd.unSelectedBackgroundColor = ContextCompat.getColor(context, android.R.color.transparent); 63 | sd.focusedBackgroundColor = Color.argb(DEFAULT_FOCUSED_BACKGROUND_ALPHA, 64 | Color.red(accentColor), 65 | Color.green(accentColor), 66 | Color.blue(accentColor)); 67 | sd.selectionAnimationDuration = DEFAULT_SELECTION_ANIMATION_DURATION; 68 | sd.selectedTextColor = ContextCompat.getColor(context, android.R.color.white); 69 | sd.unSelectedTextColor = accentColor; 70 | sd.strokeWidth = 1; 71 | sd.typeface = null; 72 | return sd; 73 | } 74 | 75 | public int getSelectedStrokeColor() { 76 | return selectedStrokeColor; 77 | } 78 | 79 | public int getUnSelectedStrokeColor() { 80 | return unSelectedStrokeColor; 81 | } 82 | 83 | public int getStrokeWidth() { 84 | return strokeWidth; 85 | } 86 | 87 | public int getSelectBackgroundColor() { 88 | return selectBackgroundColor; 89 | } 90 | 91 | public int getUnSelectedBackgroundColor() { 92 | return unSelectedBackgroundColor; 93 | } 94 | 95 | public int getFocusedBackgroundColor() { 96 | return focusedBackgroundColor; 97 | } 98 | 99 | public int getSelectionAnimationDuration() { 100 | return selectionAnimationDuration; 101 | } 102 | 103 | public int getSelectedTextColor() { 104 | return selectedTextColor; 105 | } 106 | 107 | public int getUnSelectedTextColor() { 108 | return unSelectedTextColor; 109 | } 110 | 111 | public int getTextSize() { 112 | return textSize; 113 | } 114 | 115 | public int getTextHorizontalPadding() { 116 | return textHorizontalPadding; 117 | } 118 | 119 | public int getTextVerticalPadding() { 120 | return textVerticalPadding; 121 | } 122 | 123 | public int getSegmentVerticalMargin() { 124 | return segmentVerticalMargin; 125 | } 126 | 127 | public int getSegmentHorizontalMargin() { 128 | return segmentHorizontalMargin; 129 | } 130 | 131 | public int getTopLeftRadius() { 132 | return topLeftRadius; 133 | } 134 | 135 | public int getTopRightRadius() { 136 | return topRightRadius; 137 | } 138 | 139 | public int getBottomRightRadius() { 140 | return bottomRightRadius; 141 | } 142 | 143 | public int getBottomLeftRadius() { 144 | return bottomLeftRadius; 145 | } 146 | 147 | public boolean isRadiusForEverySegment() { 148 | return radiusForEverySegment; 149 | } 150 | 151 | public Typeface getTypeface() { 152 | return typeface; 153 | } 154 | 155 | ; 156 | } 157 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/item_row_column/SegmentViewHolder.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column; 2 | 3 | import android.graphics.Typeface; 4 | import android.support.annotation.NonNull; 5 | import android.view.View; 6 | 7 | import section_layout.widget.custom.android.com.sectionlayout.distributive_section_layout.DistributiveSectionLayout; 8 | 9 | /** 10 | * Created by Robert Apikyan on 9/7/2017. 11 | */ 12 | 13 | public abstract class SegmentViewHolder extends DistributiveSectionLayout.ViewHolder> { 14 | private SegmentData segmentData; 15 | 16 | private final View.OnClickListener onSectionViewClickListener = new View.OnClickListener() { 17 | @Override 18 | public void onClick(View v) { 19 | if (segmentData != null) 20 | segmentData.getOnSegmentClickListener().onSegmentClick(SegmentViewHolder.this); 21 | } 22 | }; 23 | 24 | public SegmentViewHolder(@NonNull View sectionView) { 25 | super(sectionView); 26 | } 27 | 28 | @Override 29 | protected final void onBind(SegmentData segmentData) { 30 | this.segmentData = segmentData; 31 | getSectionView().setOnClickListener(onSectionViewClickListener); 32 | onSegmentBind(segmentData.getSegmentData()); 33 | } 34 | 35 | public final void setSelected(boolean isSelected) { 36 | if (segmentData.isSelected() && isSelected) { 37 | segmentData.isSelected = true; 38 | onSegmentSelected(true, true); 39 | } else if (isSelected) { 40 | segmentData.isSelected = true; 41 | onSegmentSelected(true, false); 42 | } else { 43 | segmentData.isSelected = false; 44 | onSegmentSelected(false, false); 45 | } 46 | } 47 | 48 | /** 49 | * Override this method in order to define, performed action, selected, unselected, reselected 50 | * 51 | * @param isSelected, represent selected state 52 | * @param isReselected, represent reselected state 53 | */ 54 | public void onSegmentSelected(boolean isSelected, boolean isReselected) { 55 | 56 | } 57 | 58 | public int getAbsolutePosition() { 59 | return segmentData.absolutePosition; 60 | } 61 | 62 | public boolean isSelected() { 63 | return segmentData.isSelected; 64 | } 65 | 66 | public int getRow() { 67 | return segmentData.getRow(); 68 | } 69 | 70 | public int getColumn() { 71 | return segmentData.getColumn(); 72 | } 73 | 74 | public D getSegmentData() { 75 | return segmentData.getSegmentData(); 76 | } 77 | 78 | public int getSelectedStrokeColor() { 79 | return segmentData.getSelectedStrokeColor(); 80 | } 81 | 82 | public int getUnSelectedStrokeColor() { 83 | return segmentData.getUnSelectedStrokeColor(); 84 | } 85 | 86 | public int getStrokeWidth() { 87 | return segmentData.getStrokeWidth(); 88 | } 89 | 90 | public int getSelectBackgroundColor() { 91 | return segmentData.getSelectBackgroundColor(); 92 | } 93 | 94 | public int getUnSelectedBackgroundColor() { 95 | return segmentData.getUnSelectedBackgroundColor(); 96 | } 97 | 98 | public int getFocusedBackgroundColor(){ 99 | return segmentData.getFocusedBackgroundColor(); 100 | } 101 | 102 | public int getSelectionAnimationDuration(){ 103 | return segmentData.getSelectionAnimationDuration(); 104 | } 105 | 106 | public int getSelectedTextColor() { 107 | return segmentData.getSelectedTextColor(); 108 | } 109 | 110 | public int getUnSelectedTextColor() { 111 | return segmentData.getUnSelectedTextColor(); 112 | } 113 | 114 | public int getTextSize() { 115 | return segmentData.getTextSize(); 116 | } 117 | 118 | public Typeface getTypeFace(){return segmentData.getTypeFace();} 119 | 120 | public int getCurrentSize() { 121 | return segmentData.getCurrentSize(); 122 | } 123 | 124 | public int getColumnCount() { 125 | return segmentData.getColumnCount(); 126 | } 127 | 128 | public int getTextHorizontalPadding() { 129 | return segmentData.getTextHorizontalPadding(); 130 | } 131 | 132 | public int getTextVerticalPadding() { 133 | return segmentData.getTextVerticalPadding(); 134 | } 135 | 136 | public int getSegmentVerticalMargin() { 137 | return segmentData.getSegmentVerticalMargin(); 138 | } 139 | 140 | public int getSegmentHorizontalMargin() { 141 | return segmentData.getSegmentHorizontalMargin(); 142 | } 143 | 144 | public int getTopLeftRadius() { 145 | return segmentData.getTopLeftRadius(); 146 | } 147 | 148 | public int getTopRightRadius() { 149 | return segmentData.getTopRightRadius(); 150 | } 151 | 152 | public int getBottomRightRadius() { 153 | return segmentData.getBottomRightRadius(); 154 | } 155 | 156 | public int getBottomLeftRadius() { 157 | return segmentData.getBottomLeftRadius(); 158 | } 159 | 160 | public boolean isRadiusForEverySegment() { 161 | return segmentData.isRadiusForEverySegment(); 162 | } 163 | 164 | protected abstract void onSegmentBind(D segmentData); 165 | } 166 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/listeners/OnSegmentClickListener.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.listeners; 2 | 3 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 4 | 5 | /** 6 | * Created by Robert Apikyan on 9/7/2017. 7 | */ 8 | 9 | public interface OnSegmentClickListener { 10 | /** 11 | * @param segmentViewHolder, the clicked segment view holder instance 12 | */ 13 | void onSegmentClick(SegmentViewHolder segmentViewHolder); 14 | } 15 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/listeners/OnSegmentSelectRequestListener.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.listeners; 2 | 3 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 4 | 5 | /** 6 | * Created by Robert Apikyan on 9/12/2017. 7 | */ 8 | 9 | public interface OnSegmentSelectRequestListener { 10 | /** 11 | * The event will be triggered before perform segment selection,and after segment click event 12 | * 13 | * @param segmentViewHolder, clicked segment view holder 14 | * @return false segment selection will be ignored, true segment selection will be performed 15 | */ 16 | boolean onSegmentSelectRequest(SegmentViewHolder segmentViewHolder); 17 | } 18 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/listeners/OnSegmentSelectedListener.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.listeners; 2 | 3 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 4 | 5 | /** 6 | * Created by Robert Apikyan on 9/7/2017. 7 | */ 8 | 9 | public interface OnSegmentSelectedListener { 10 | /** 11 | * The Event will be triggered: if the segment is selected, reselected and unSelected 12 | * 1. Segment is selected for first time: isSelected = true ,isReselected = false 13 | * 2. Segment is reselected: isSelected = true ,isReselected = true 14 | * 3. Segment is unSelected: isSelected = false ,isReselected = false 15 | * 16 | * @param segmentViewHolder event related segmentViewHolder 17 | * @param isSelected, true segment is selected 18 | * @param isReselected, true segment is reselected 19 | */ 20 | void onSegmentSelected(SegmentViewHolder segmentViewHolder, boolean isSelected, boolean isReselected); 21 | } 22 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/utils/ArgbEvaluator.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.utils; 2 | 3 | import android.animation.TypeEvaluator; 4 | import android.animation.ValueAnimator; 5 | 6 | public class ArgbEvaluator implements TypeEvaluator { 7 | private static final android.animation.ArgbEvaluator sInstance = new android.animation.ArgbEvaluator(); 8 | 9 | /** 10 | * Returns an instance of ArgbEvaluator that may be used in 11 | * {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may 12 | * be used in multiple Animators because it holds no state. 13 | * @return An instance of ArgbEvalutor. 14 | * 15 | * @hide 16 | */ 17 | public static android.animation.ArgbEvaluator getInstance() { 18 | return sInstance; 19 | } 20 | 21 | /** 22 | * This function returns the calculated in-between value for a color 23 | * given integers that represent the start and end values in the four 24 | * bytes of the 32-bit int. Each channel is separately linearly interpolated 25 | * and the resulting calculated values are recombined into the return value. 26 | * 27 | * @param fraction The fraction from the starting to the ending values 28 | * @param startValue A 32-bit int value representing colors in the 29 | * separate bytes of the parameter 30 | * @param endValue A 32-bit int value representing colors in the 31 | * separate bytes of the parameter 32 | * @return A value that is calculated to be the linearly interpolated 33 | * result, derived by separating the start and end values into separate 34 | * color channels and interpolating each one separately, recombining the 35 | * resulting values in the same way. 36 | */ 37 | public Object evaluate(float fraction, Object startValue, Object endValue) { 38 | int startInt = (Integer) startValue; 39 | float startA = ((startInt >> 24) & 0xff) / 255.0f; 40 | float startR = ((startInt >> 16) & 0xff) / 255.0f; 41 | float startG = ((startInt >> 8) & 0xff) / 255.0f; 42 | float startB = ( startInt & 0xff) / 255.0f; 43 | 44 | int endInt = (Integer) endValue; 45 | float endA = ((endInt >> 24) & 0xff) / 255.0f; 46 | float endR = ((endInt >> 16) & 0xff) / 255.0f; 47 | float endG = ((endInt >> 8) & 0xff) / 255.0f; 48 | float endB = ( endInt & 0xff) / 255.0f; 49 | 50 | // convert from sRGB to linear 51 | startR = (float) Math.pow(startR, 2.2); 52 | startG = (float) Math.pow(startG, 2.2); 53 | startB = (float) Math.pow(startB, 2.2); 54 | 55 | endR = (float) Math.pow(endR, 2.2); 56 | endG = (float) Math.pow(endG, 2.2); 57 | endB = (float) Math.pow(endB, 2.2); 58 | 59 | // compute the interpolated color in linear space 60 | float a = startA + fraction * (endA - startA); 61 | float r = startR + fraction * (endR - startR); 62 | float g = startG + fraction * (endG - startG); 63 | float b = startB + fraction * (endB - startB); 64 | 65 | // convert back to sRGB in the [0..255] range 66 | a = a * 255.0f; 67 | r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f; 68 | g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f; 69 | b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f; 70 | 71 | return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b); 72 | } 73 | } -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/java/segmented_control/widget/custom/android/com/segmentedcontrol/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package segmented_control.widget.custom.android.com.segmentedcontrol.utils; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.animation.ValueAnimator; 5 | import android.graphics.drawable.Drawable; 6 | import android.graphics.drawable.GradientDrawable; 7 | import android.support.annotation.IntRange; 8 | import android.view.View; 9 | 10 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentBackgroundType; 11 | import segmented_control.widget.custom.android.com.segmentedcontrol.item_row_column.SegmentViewHolder; 12 | 13 | /** 14 | * Created by Robert Apikyan on 9/8/2017. 15 | */ 16 | 17 | public class Utils { 18 | public static T lazy(T nullable, T nonNull) { 19 | if (nullable == null) { 20 | nullable = nonNull; 21 | } 22 | return nullable; 23 | } 24 | 25 | /** 26 | * Utility method, use to define segment background type 27 | * 28 | * @param absolutePosition, Segment absolute position from {@link SegmentViewHolder#getAbsolutePosition()} 29 | * @param columnCount, from {@link SegmentViewHolder#getColumnCount()} 30 | * @param size, from {@link SegmentViewHolder#getCurrentSize()} 31 | * @return {@link SegmentBackgroundType} 32 | */ 33 | @SegmentBackgroundType 34 | public static int defineSegmentBackground(@IntRange(from = 0) int absolutePosition, @IntRange(from = 1) int columnCount, @IntRange(from = 1) int size) { 35 | 36 | // if only one item 37 | if (size == 1) { 38 | return SegmentBackgroundType.SINGLE_BG; 39 | } 40 | 41 | // if one column 42 | if (columnCount == 1) { 43 | // for first 44 | if (absolutePosition == 0) { 45 | return SegmentBackgroundType.TOP_SINGLE_BG; 46 | } 47 | 48 | // for last 49 | if (absolutePosition == size - 1) { 50 | return SegmentBackgroundType.BOTTOM_SINGLE_BG; 51 | } 52 | } 53 | 54 | // if not one column, but one row 55 | if (size <= columnCount) { 56 | if (absolutePosition == 0) { 57 | return SegmentBackgroundType.TOP_LEFT_SINGLE_BG; 58 | } 59 | 60 | if (absolutePosition == size - 1) { 61 | return SegmentBackgroundType.TOP_RIGHT_SINGLE_BG; 62 | } 63 | } 64 | 65 | // if not one column and multi row 66 | if (absolutePosition == 0) { 67 | return SegmentBackgroundType.TOP_LEFT_BG; 68 | } 69 | 70 | if (absolutePosition == columnCount - 1) { 71 | return SegmentBackgroundType.TOP_RIGHT_BG; 72 | } 73 | 74 | int notCompletedRowItemsCount = size % columnCount; 75 | 76 | int completeRowsItemsCount = size - notCompletedRowItemsCount; 77 | 78 | if (notCompletedRowItemsCount == 1 && absolutePosition == completeRowsItemsCount) { 79 | return SegmentBackgroundType.BOTTOM_SINGLE_BG; 80 | } 81 | 82 | if (notCompletedRowItemsCount == 0) { 83 | if (absolutePosition == size - columnCount) { 84 | return SegmentBackgroundType.BOTTOM_LEFT_BG; 85 | } 86 | if (absolutePosition == size - 1) { 87 | return SegmentBackgroundType.BOTTOM_RIGHT_BG; 88 | } 89 | } else if (notCompletedRowItemsCount > 0) { 90 | if (absolutePosition == size - notCompletedRowItemsCount) { 91 | return SegmentBackgroundType.BOTTOM_LEFT_BG; 92 | } 93 | if (absolutePosition == size - 1) { 94 | return SegmentBackgroundType.BOTTOM_RIGHT_BG; 95 | } 96 | } 97 | 98 | return SegmentBackgroundType.MIDDLE_BG; 99 | } 100 | 101 | /** 102 | * Use to define segment corner radius 103 | * 104 | * @param absolutePosition, Segment absolute position from {@link SegmentViewHolder#getAbsolutePosition()} 105 | * @param columnCount, from {@link SegmentViewHolder#getColumnCount()} 106 | * @param size, from {@link SegmentViewHolder#getCurrentSize()} 107 | * @param topLeftRadius, from {@link SegmentViewHolder#getTopLeftRadius()} 108 | * @param topRightRadius, from {@link SegmentViewHolder#getTopRightRadius()} ()} 109 | * @param bottomRightRadius, from {@link SegmentViewHolder#getBottomRightRadius()} 110 | * @param bottomLeftRadius, from {@link SegmentViewHolder#getBottomLeftRadius()} 111 | * @return, float[] corners radius, 112 | */ 113 | public static float[] defineRadiusForPosition(@IntRange(from = 0) int absolutePosition, @IntRange(from = 1) int columnCount, @IntRange(from = 1) int size, 114 | int topLeftRadius, int topRightRadius, int bottomRightRadius, int bottomLeftRadius) { 115 | @SegmentBackgroundType 116 | int bgType = defineSegmentBackground(absolutePosition, columnCount, size); 117 | 118 | switch (bgType) { 119 | 120 | case SegmentBackgroundType.BOTTOM_LEFT_BG: 121 | return createRadius(0, 0, 0, bottomLeftRadius); 122 | 123 | case SegmentBackgroundType.BOTTOM_RIGHT_BG: 124 | return createRadius(0, 0, bottomRightRadius, 0); 125 | 126 | case SegmentBackgroundType.BOTTOM_SINGLE_BG: 127 | return createRadius(0, 0, bottomRightRadius, bottomLeftRadius); 128 | 129 | case SegmentBackgroundType.MIDDLE_BG: 130 | return createRadius(0, 0, 0, 0); 131 | 132 | case SegmentBackgroundType.SINGLE_BG: 133 | return createRadius(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius); 134 | 135 | case SegmentBackgroundType.TOP_LEFT_BG: 136 | return createRadius(topLeftRadius, 0, 0, 0); 137 | 138 | case SegmentBackgroundType.TOP_LEFT_SINGLE_BG: 139 | return createRadius(topLeftRadius, 0, 0, bottomLeftRadius); 140 | 141 | case SegmentBackgroundType.TOP_RIGHT_BG: 142 | return createRadius(0, topRightRadius, 0, 0); 143 | 144 | case SegmentBackgroundType.TOP_RIGHT_SINGLE_BG: 145 | return createRadius(0, topRightRadius, bottomRightRadius, 0); 146 | 147 | case SegmentBackgroundType.TOP_SINGLE_BG: 148 | return createRadius(topLeftRadius, topRightRadius, 0, 0); 149 | 150 | default: 151 | return createRadius(0, 0, 0, 0); 152 | } 153 | } 154 | 155 | public static float[] createRadius(float topLeft, float topRight, float bottomRight, float bottomLeft) { 156 | return new float[]{topLeft, topLeft, topRight, topRight, bottomRight, bottomRight, bottomLeft, bottomLeft}; 157 | } 158 | 159 | /** 160 | * @param strokeWidth, stroke width 161 | * @param strokeColor, stroke color 162 | * @param argb, background color 163 | * @param radii, use {@link #defineRadiusForPosition(int, int, int, int, int, int, int)} method to define radii 164 | * @return background drawable 165 | */ 166 | public static Drawable getBackground(int strokeWidth, int strokeColor, int argb, float[] radii) { 167 | GradientDrawable drawable = new GradientDrawable(); 168 | drawable.setShape(GradientDrawable.RECTANGLE); 169 | drawable.setStroke(strokeWidth, strokeColor); 170 | drawable.setCornerRadii(radii); 171 | drawable.setColor(argb); 172 | return drawable; 173 | } 174 | 175 | public static ValueAnimator createBackgroundAnimation(int argbStart, int argbEnd) { 176 | return ValueAnimator.ofObject(ArgbEvaluator.getInstance(), argbStart, argbEnd); 177 | } 178 | 179 | public static boolean isInBounds(float touchX, float touchY, float viewX, float viewY, float viewW, float viewH) { 180 | return touchX >= viewX && touchX <= viewX + viewW && // in x bounds 181 | touchY >= viewY && touchY <= viewY + viewH; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/res/layout/item_segment_impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/res/values/attr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | -------------------------------------------------------------------------------- /segmentedcontrolmodule/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SegmentedControl 3 | 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':segmentedcontrolmodule' 2 | --------------------------------------------------------------------------------