├── sample ├── .gitignore ├── src │ └── main │ │ ├── assets │ │ ├── vazir.ttf │ │ └── vazir_b.ttf │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── java │ │ └── ir │ │ │ └── oveissi │ │ │ └── threestateswitch │ │ │ └── sample │ │ │ ├── FontHelper.java │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── threestateswitch ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── drawable │ │ │ └── switch_circle.png │ │ └── values │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── attrs.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── ir │ │ └── oveissi │ │ └── threestateswitch │ │ └── ThreeStateSwitch.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── assets └── demo.gif ├── .idea ├── copyright │ └── profiles_settings.xml ├── markdown-navigator │ └── profiles_settings.xml ├── encodings.xml ├── vcs.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml ├── misc.xml └── markdown-navigator.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /threestateswitch/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':threestateswitch' 2 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/assets/demo.gif -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sample/src/main/assets/vazir.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/assets/vazir.ttf -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ThreeStateSwitch 3 | 4 | -------------------------------------------------------------------------------- /sample/src/main/assets/vazir_b.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/assets/vazir_b.ttf -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /threestateswitch/src/main/res/drawable/switch_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/HEAD/threestateswitch/src/main/res/drawable/switch_circle.png -------------------------------------------------------------------------------- /threestateswitch/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16sp 3 | 16sp 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /threestateswitch/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ThreeStateSwitch 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 09 02:15:23 IRDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /threestateswitch/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /threestateswitch/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #bfbfbf 5 | #5ab72e 6 | 7 | 8 | #646464 9 | #5bb434 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /threestateswitch/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /sample/src/main/java/ir/oveissi/threestateswitch/sample/FontHelper.java: -------------------------------------------------------------------------------- 1 | package ir.oveissi.threestateswitch.sample; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by USER on 11/23/2015. 11 | */ 12 | public class FontHelper { 13 | private static final Map TYPEFACES = new HashMap<>(); 14 | 15 | public static Typeface get(Context context, String fontFileName){ 16 | Typeface typeface = TYPEFACES.get(fontFileName); 17 | if(typeface == null){ 18 | typeface = Typeface.createFromAsset(context.getResources().getAssets(), fontFileName); 19 | TYPEFACES.put(fontFileName, typeface); 20 | } 21 | return typeface; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "ir.oveissi.threestateswitch.sample" 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | compile 'com.android.support:appcompat-v7:25.3.1' 25 | compile project(':threestateswitch') 26 | } 27 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/abbas/android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /threestateswitch/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/abbas/android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /sample/src/main/java/ir/oveissi/threestateswitch/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package ir.oveissi.threestateswitch.sample; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import ir.oveissi.threestateswitch.ThreeStateSwitch; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | 15 | 16 | ThreeStateSwitch threeState= (ThreeStateSwitch) findViewById(R.id.threeState); 17 | ThreeStateSwitch threeState1= (ThreeStateSwitch) findViewById(R.id.threeState1); 18 | 19 | threeState.setNormalTextTypeface(FontHelper.get(this,"vazir.ttf")); 20 | threeState.setSelectedTextTypeface(FontHelper.get(this,"vazir_b.ttf")); 21 | 22 | threeState1.setNormalTextTypeface(FontHelper.get(this,"vazir.ttf")); 23 | threeState1.setSelectedTextTypeface(FontHelper.get(this,"vazir_b.ttf")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /threestateswitch/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'maven' 3 | 4 | group='com.github.abbas-oveissi' 5 | 6 | android { 7 | compileSdkVersion 25 8 | buildToolsVersion "25.0.2" 9 | 10 | defaultConfig { 11 | minSdkVersion 14 12 | targetSdkVersion 25 13 | versionCode 3 14 | versionName "0.9.0" 15 | 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | 18 | } 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile 'com.android.support:appcompat-v7:25.3.1' 29 | } 30 | 31 | 32 | task sourcesJar(type: Jar) { 33 | from android.sourceSets.main.java.srcDirs 34 | classifier = 'sources' 35 | } 36 | 37 | task javadoc(type: Javadoc) { 38 | failOnError false 39 | source = android.sourceSets.main.java.sourceFiles 40 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 41 | } 42 | 43 | task javadocJar(type: Jar, dependsOn: javadoc) { 44 | classifier = 'javadoc' 45 | from javadoc.destinationDir 46 | } 47 | 48 | artifacts { 49 | archives sourcesJar 50 | archives javadocJar 51 | } 52 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 33 | 34 | 35 | 40 | 41 | 42 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Release](https://jitpack.io/v/abbas-oveissi/ThreeStateSwitch.svg)](https://jitpack.io/#abbas-oveissi/ThreeStateSwitch) 2 | 3 | ## ThreeStateSwitch 4 | 5 | A simple three-state switch view for Android. 6 | 7 | **Please Note:** This library has not been fully tested, so use with a little caution, and submit an issue or better, a pull request if you notice any issues at all. 8 | 9 | **Project Setup and Dependencies** 10 | - MinSDK 14 11 | 12 | **Highlights** 13 | - supports showing text on both sides of view 14 | - supports customization in color or size 15 | 16 | 17 | # Preview 18 | 19 | ![](https://raw.githubusercontent.com/abbas-oveissi/ThreeStateSwitch/master/assets/demo.gif) 20 | 21 | # Setup 22 | ## 1. Provide the gradle dependency 23 | 24 | Add it in your root build.gradle at the end of repositories: 25 | ```gradle 26 | allprojects { 27 | repositories { 28 | ... 29 | maven { url "https://jitpack.io" } 30 | } 31 | } 32 | ``` 33 | Add the dependency: 34 | ```gradle 35 | dependencies { 36 | compile 'com.github.abbas-oveissi:threestateswitch:0.8.2' 37 | } 38 | ``` 39 | 40 | ## 2. How to use 41 | 42 | Add the ThreeStateSwitch in your layout file and customize it the way you like it. 43 | ```xml 44 | 56 | ``` 57 | You can set a listener for state changes 58 | ```java 59 | threeState.setOnChangeListener(new ThreeStateSwitch.OnStateChangeListener() { 60 | @Override 61 | public void OnStateChangeListener(int currentState) { 62 | //current state= -1 0 1 63 | Toast.makeText(MainActivity.this, String.valueOf(currentState), Toast.LENGTH_SHORT).show(); 64 | } 65 | }); 66 | ``` 67 | You can set typeface for texts. 68 | ```java 69 | threeState.setNormalTextTypeface( ); 70 | threeState.setSelectedTextTypeface( ); 71 | ``` 72 | Get the current state. 73 | ```java 74 | //state= -1 0 1 75 | threeState.getState(); 76 | ``` 77 | ## attributes 78 | 79 | | Name | Type | Default | Description | 80 | |:----:|:----:|:-------:|:-----------:| 81 | |background_selected_color|Color|#5bb434| | 82 | |background_normal_color|Color|#bfbfbf| | 83 | |text_normal_color|Color|#646464| | 84 | |text_selected_color|Color|#5bb434| | 85 | |text_left|String|| | 86 | |text_right|String|| | 87 | |text_normal_size|Dp or Sp|16sp| | 88 | |text_selected_size|Dp or Sp|16sp| | 89 | |two_state_after_init|Boolean|false|Converts switch to a two state switch after first interaction | 90 | 91 | # Bugs and features 92 | 93 | For bugs, feature requests, and discussion please use GitHub Issues. 94 | 95 | # Developed By 96 | 97 | * Abbas Oveissi - [@abbas_oveissi](https://twitter.com/abbas_oveissi) 98 | 99 | 100 | # License 101 | 102 | Copyright 2017 Abbas Oveissi 103 | 104 | Licensed under the Apache License, Version 2.0 (the "License"); 105 | you may not use this file except in compliance with the License. 106 | You may obtain a copy of the License at 107 | 108 | http://www.apache.org/licenses/LICENSE-2.0 109 | 110 | Unless required by applicable law or agreed to in writing, software 111 | distributed under the License is distributed on an "AS IS" BASIS, 112 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 113 | See the License for the specific language governing permissions and 114 | limitations under the License. -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 35 | 36 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /threestateswitch/src/main/java/ir/oveissi/threestateswitch/ThreeStateSwitch.java: -------------------------------------------------------------------------------- 1 | package ir.oveissi.threestateswitch; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ArgbEvaluator; 6 | import android.animation.ObjectAnimator; 7 | import android.annotation.TargetApi; 8 | import android.content.Context; 9 | import android.content.res.Resources; 10 | import android.content.res.TypedArray; 11 | import android.graphics.Bitmap; 12 | import android.graphics.BitmapFactory; 13 | import android.graphics.Canvas; 14 | import android.graphics.Color; 15 | import android.graphics.Paint; 16 | import android.graphics.Rect; 17 | import android.graphics.RectF; 18 | import android.graphics.Typeface; 19 | import android.os.Build; 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | import android.util.AttributeSet; 23 | import android.view.MotionEvent; 24 | import android.view.View; 25 | 26 | import java.util.ArrayList; 27 | 28 | /** 29 | * Created by abbas on 8/6/16. 30 | */ 31 | 32 | public class ThreeStateSwitch extends View { 33 | private Context context; 34 | private AttributeSet attrs; 35 | 36 | private int state=0; 37 | private boolean isDrag; 38 | private float xDrag; 39 | private float yDrag; 40 | private float startX; 41 | private boolean isAnimate; 42 | private boolean isPressed; 43 | private Typeface selectedTextTypeface; 44 | private Typeface normalTextTypeface; 45 | 46 | 47 | 48 | 49 | 50 | 51 | int text_normal_color ; 52 | int text_selected_color; 53 | int text_normal_size ; 54 | int text_selected_size; 55 | 56 | boolean twoStateAfterInit = false; 57 | 58 | // #bfbfbf 59 | // #5ab72e 60 | int colorStateUnSelected = Color.parseColor("#bfbfbf"); 61 | int colorStateSelected = Color.parseColor("#5ab72e"); 62 | 63 | public int backgroundColor; 64 | 65 | 66 | Paint ovalPaint,textSelectedPaint,textNormalPaint; 67 | Bitmap thumbIcon; 68 | RectF ovalRectF; 69 | Rect orginalBitmapRect,drawnBitmapRect; 70 | Rect textBounds; 71 | 72 | private float viewWidth; 73 | private float viewHeight; 74 | 75 | 76 | private int paddingRight; 77 | private int paddingLeft; 78 | private int paddingBottom; 79 | private int paddingTop; 80 | 81 | 82 | public int xCircle=0; 83 | public int yCircle=0; 84 | public int diameterSize =0; 85 | 86 | 87 | String lessText=""; 88 | String moreText=""; 89 | 90 | 91 | public ThreeStateSwitch(Context context) { 92 | super(context); 93 | init(context,null); 94 | } 95 | 96 | public ThreeStateSwitch(Context context, AttributeSet attrs) { 97 | super(context, attrs); 98 | init(context,attrs); 99 | } 100 | 101 | public ThreeStateSwitch(Context context, AttributeSet attrs, int defStyleAttr) { 102 | super(context, attrs, defStyleAttr); 103 | init(context,attrs); 104 | } 105 | 106 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 107 | public ThreeStateSwitch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 108 | super(context, attrs, defStyleAttr, defStyleRes); 109 | init(context,attrs); 110 | } 111 | 112 | 113 | public void init(Context context, AttributeSet attrs) 114 | { 115 | this.context = context; 116 | this.attrs = attrs; 117 | 118 | // int[] attrsArray = new int[] { 119 | // R.attr.background_selected_color, // 0 120 | // R.attr.background_normal_color, // 1 121 | // R.attr.text_normal_color, // 2 122 | // R.attr.text_selected_color, // 3 123 | // R.attr.text_normal_size, // 4 124 | // R.attr.text_selected_size, // 5 125 | // R.styleable.ThreeStateSwitch_text_left, // 6 126 | // R.styleable.ThreeStateSwitch_text_right // 7 127 | // }; 128 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ThreeStateSwitch); 129 | 130 | colorStateSelected = ta.getColor(R.styleable.ThreeStateSwitch_background_selected_color ,getResources().getColor(R.color.background_selected_color)); 131 | colorStateUnSelected = ta.getColor(R.styleable.ThreeStateSwitch_background_normal_color ,getResources().getColor(R.color.background_normal_color)); 132 | 133 | backgroundColor=colorStateUnSelected; 134 | 135 | 136 | text_normal_color = ta.getColor(R.styleable.ThreeStateSwitch_text_normal_color ,getResources().getColor(R.color.text_normal_color)); 137 | text_selected_color = ta.getColor(R.styleable.ThreeStateSwitch_text_selected_color ,getResources().getColor(R.color.text_selected_color)); 138 | 139 | text_normal_size = ta.getDimensionPixelSize(R.styleable.ThreeStateSwitch_text_normal_size ,(int)getResources().getDimension(R.dimen.text_normal_size)); 140 | text_selected_size = ta.getDimensionPixelSize(R.styleable.ThreeStateSwitch_text_selected_size ,(int)getResources().getDimension(R.dimen.text_selected_size)); 141 | 142 | twoStateAfterInit = ta.getBoolean(R.styleable.ThreeStateSwitch_two_state_after_init , false); 143 | 144 | 145 | lessText = ta.getString(R.styleable.ThreeStateSwitch_text_left); 146 | if(lessText==null) 147 | { 148 | lessText=getResources().getString(R.string.text_left); 149 | } 150 | moreText = ta.getString(R.styleable.ThreeStateSwitch_text_right); 151 | if(moreText==null) 152 | { 153 | moreText=getResources().getString(R.string.text_right); 154 | } 155 | 156 | 157 | ta.recycle(); 158 | 159 | 160 | ovalPaint =new Paint(); 161 | ovalPaint.setColor(backgroundColor); 162 | ovalPaint.setAntiAlias(true); 163 | 164 | thumbIcon = BitmapFactory.decodeResource(context.getResources(), 165 | R.drawable.switch_circle); 166 | 167 | orginalBitmapRect=new Rect(); 168 | drawnBitmapRect=new Rect(); 169 | ovalRectF=new RectF(); 170 | textBounds= new Rect(); 171 | 172 | textSelectedPaint = new Paint(); 173 | textSelectedPaint.setColor(text_selected_color); 174 | textSelectedPaint.setTextSize( text_selected_size); 175 | textSelectedPaint.setAntiAlias(true); 176 | 177 | 178 | textNormalPaint = new Paint(); 179 | textNormalPaint.setColor(text_normal_color); 180 | textNormalPaint.setTextSize( text_normal_size); 181 | textNormalPaint.setAntiAlias(true); 182 | } 183 | 184 | 185 | public interface OnStateChangeListener 186 | { 187 | void OnStateChangeListener(int currentState); 188 | } 189 | public OnStateChangeListener onStateChangeListener; 190 | public void setOnChangeListener(OnStateChangeListener onStateChangeListener) { 191 | this.onStateChangeListener = onStateChangeListener; 192 | } 193 | 194 | 195 | public int getBackColor() { 196 | return this.backgroundColor; 197 | } 198 | 199 | public int getXCircle() { 200 | return xCircle; 201 | } 202 | 203 | public void setBackColor(int backgroundColor) { 204 | this.backgroundColor = backgroundColor; 205 | invalidate(); 206 | } 207 | 208 | public void setXCircle(int xCircle) { 209 | this.xCircle = xCircle; 210 | invalidate(); 211 | } 212 | 213 | 214 | public int getState() 215 | { 216 | return this.state; 217 | } 218 | 219 | @Override 220 | protected void onMeasure(int widthSpec, int heightSpec) { 221 | int widthSize = MeasureSpec.getSize(widthSpec); 222 | int heightSize = MeasureSpec.getSize(heightSpec); 223 | 224 | paddingRight=getPaddingRight(); 225 | paddingLeft=getPaddingLeft(); 226 | paddingBottom=getPaddingBottom(); 227 | paddingTop=getPaddingTop(); 228 | 229 | 230 | textLeftNormalWidth =textNormalPaint.measureText(lessText); 231 | textRightNormalWidth =textNormalPaint.measureText(moreText); 232 | 233 | textLeftSelectedWidth =textSelectedPaint.measureText(lessText); 234 | textRightSelectedWidth =textSelectedPaint.measureText(moreText); 235 | 236 | maxNeededTextWidth= Math.max(textLeftNormalWidth, textRightNormalWidth); 237 | maxNeededTextWidth= Math.max(maxNeededTextWidth, textLeftSelectedWidth); 238 | maxNeededTextWidth= Math.max(maxNeededTextWidth, textRightSelectedWidth); 239 | maxNeededTextWidth+=dpToPx(5); 240 | 241 | 242 | float minWidth=(2*maxNeededTextWidth)+dpToPx(130)+paddingLeft+paddingRight; 243 | float minHeight=dpToPx(35)+paddingBottom+paddingTop; 244 | 245 | 246 | int pWidth=resolveSize((int) minWidth,widthSpec); 247 | int pHeight=resolveSize((int) minHeight,heightSpec); 248 | 249 | 250 | float LeftRect=maxNeededTextWidth+paddingLeft; 251 | float topRect=paddingTop; 252 | float rightRect=pWidth-maxNeededTextWidth-paddingRight; 253 | float bottomRect=pHeight-paddingBottom; 254 | 255 | ovalRectF.set(LeftRect,topRect,rightRect,bottomRect); 256 | 257 | 258 | diameterSize = Math.min(pWidth-paddingRight-paddingLeft-(int)(2*maxNeededTextWidth) 259 | ,pHeight-paddingTop-paddingBottom); 260 | 261 | switch (state) 262 | { 263 | case -1: 264 | if (!twoStateAfterInit) 265 | xCircle=(int)(ovalRectF.left+(diameterSize /2.0)); 266 | else 267 | xCircle=(int)(ovalRectF.centerX()); 268 | break; 269 | case 0: 270 | xCircle=(int)(ovalRectF.centerX()); 271 | break; 272 | case 1: 273 | xCircle=(int)(ovalRectF.right- (diameterSize /2.0)); 274 | break; 275 | } 276 | yCircle= (int) ovalRectF.centerY(); 277 | 278 | 279 | 280 | // changeViewSize(widthSize,heightSize); 281 | viewWidth = pWidth; 282 | viewHeight = pHeight; 283 | setMeasuredDimension(pWidth,pHeight); 284 | } 285 | 286 | 287 | float textLeftSelectedWidth,textRightSelectedWidth,textLeftNormalWidth, textRightNormalWidth,maxNeededTextWidth; 288 | 289 | 290 | 291 | int delta=0; 292 | 293 | @Override 294 | public boolean onTouchEvent(MotionEvent event) { 295 | int action = event.getAction() & MotionEvent.ACTION_MASK; 296 | switch(action) { 297 | case MotionEvent.ACTION_DOWN : { 298 | this.getParent().requestDisallowInterceptTouchEvent(true); 299 | startX=event.getX(); 300 | if(state==whichStateClick(event.getX(),event.getY())) 301 | { 302 | delta=-1*((int)event.getX()-xCircle); 303 | isPressed=true; 304 | } 305 | } 306 | case MotionEvent.ACTION_MOVE: 307 | if(isDrag==false&& Math.abs(startX-event.getX())>15) { 308 | isDrag = true; 309 | } 310 | if(isDrag) 311 | { 312 | int endBoundry=(int) Math.max((diameterSize /2)-delta,event.getX()); 313 | int tempX=(int) Math.min(endBoundry,viewWidth- (diameterSize /2)); 314 | xCircle=tempX+delta; 315 | xCircle=setBoundForXCircle(xCircle); 316 | } 317 | break; 318 | case MotionEvent.ACTION_UP: 319 | int tempState=whichStateClick(event.getX(),event.getY()); 320 | isDrag=false; 321 | isPressed=false; 322 | changeState(tempState); 323 | break; 324 | } 325 | xDrag=event.getX(); 326 | yDrag=event.getY(); 327 | invalidate(); 328 | return true; 329 | } 330 | 331 | public void setNormalTextTypeface(Typeface typeface) 332 | { 333 | this.normalTextTypeface=typeface; 334 | textNormalPaint.setTypeface(typeface); 335 | } 336 | 337 | public void setSelectedTextTypeface(Typeface typeface) 338 | { 339 | this.selectedTextTypeface=typeface; 340 | textSelectedPaint.setTypeface(typeface); 341 | } 342 | 343 | 344 | private int whichStateClick(float x ,float y) 345 | { 346 | if (twoStateAfterInit) { 347 | int half = (int) ((viewWidth - (2 * maxNeededTextWidth) - paddingLeft - paddingRight) / 2.0); 348 | if (x < half + maxNeededTextWidth + paddingLeft) 349 | return -1; 350 | else 351 | return 1; 352 | } 353 | int yekSevom=(int)((viewWidth-(2*maxNeededTextWidth)-paddingLeft-paddingRight)/3.0); 354 | if(x(2*yekSevom)+maxNeededTextWidth+paddingLeft) 357 | return 1; 358 | return 0; 359 | } 360 | 361 | 362 | private int setBoundForXCircle(float xCircle) 363 | { 364 | //Log.d("before",String.valueOf(xCircle)); 365 | if(xCircle(float)(ovalRectF.right-(diameterSize /2.0))) 368 | xCircle=(float)(ovalRectF.right-(diameterSize /2.0)); 369 | 370 | //Log.d("after",String.valueOf(xCircle)); 371 | return (int)xCircle; 372 | } 373 | 374 | @Override 375 | protected void onDraw(Canvas canvas) { 376 | 377 | //ovalRectF.set(0,0,viewWidth,viewHeight); 378 | ovalPaint.setColor(backgroundColor); 379 | canvas.drawRoundRect(ovalRectF, diameterSize /2, diameterSize /2, ovalPaint); 380 | 381 | 382 | if(!isAnimate&&!isDrag) 383 | { 384 | if(state==-1) 385 | { 386 | xCircle=(int)(diameterSize /2.0)+(int)ovalRectF.left; 387 | } 388 | else if(state==0) 389 | { 390 | xCircle=(int)(ovalRectF.centerX()); 391 | } 392 | else if(state==1) 393 | { 394 | xCircle=(int)(ovalRectF.right- (diameterSize /2.0)); 395 | } 396 | yCircle=(int)(ovalRectF.centerY()); 397 | } 398 | 399 | 400 | if(isPressed) 401 | diameterSize +=dpToPx(4); 402 | 403 | 404 | yCircle=(int)(ovalRectF.centerY()); 405 | 406 | 407 | 408 | xCircle=setBoundForXCircle(xCircle); 409 | orginalBitmapRect.set(0,0,thumbIcon.getWidth(),thumbIcon.getHeight()); 410 | drawnBitmapRect.set(xCircle-(int)(diameterSize /2.0),yCircle-(int)(diameterSize /2.0) 411 | ,xCircle+ (int)(diameterSize /2.0), yCircle+(int)(diameterSize /2.0)); 412 | 413 | canvas.drawBitmap(thumbIcon,orginalBitmapRect,drawnBitmapRect,null); 414 | if (isPressed) 415 | diameterSize -=dpToPx(4); 416 | 417 | if(state==-1) 418 | drawCenter(canvas,(int)(maxNeededTextWidth/2.0)+paddingLeft,(int)(ovalRectF.centerY()),textSelectedPaint,lessText); 419 | else 420 | drawCenter(canvas,(int)(maxNeededTextWidth/2.0)+paddingLeft,(int)(ovalRectF.centerY()),textNormalPaint,lessText); 421 | 422 | if(state==1) 423 | drawCenter(canvas,(int)(viewWidth-(maxNeededTextWidth/2.0)-paddingRight),(int)(ovalRectF.centerY()),textSelectedPaint,moreText); 424 | else 425 | drawCenter(canvas,(int)(viewWidth-(maxNeededTextWidth/2.0)-paddingRight),(int)(ovalRectF.centerY()),textNormalPaint,moreText); 426 | 427 | } 428 | 429 | 430 | private Rect r = new Rect(); 431 | private void drawCenter(Canvas canvas, int drawX, int drawY, Paint paint, String text) { 432 | paint.setTextAlign(Paint.Align.LEFT); 433 | paint.getTextBounds(text, 0, text.length(), r); 434 | float x = drawX - r.width() / 2f - r.left; 435 | float y = drawY + r.height() / 2f - r.bottom; 436 | canvas.drawText(text, x, y, paint); 437 | } 438 | 439 | 440 | public void changeState(int mState) 441 | { 442 | this.changeState(mState,true); 443 | } 444 | public void changeState(int mState,boolean animate) 445 | { 446 | if(animate==false) 447 | { 448 | switch (mState) { 449 | case -1: 450 | if (!twoStateAfterInit) 451 | setBackColor(colorStateSelected); 452 | else 453 | setBackColor(colorStateUnSelected); 454 | 455 | break; 456 | case 0: 457 | setBackColor(colorStateUnSelected); 458 | break; 459 | case 1: 460 | setBackColor(colorStateSelected); 461 | break; 462 | } 463 | this.state=mState; 464 | return; 465 | } 466 | if(onStateChangeListener!=null) 467 | onStateChangeListener.OnStateChangeListener(mState); 468 | AnimatorSet animSet = new AnimatorSet(); 469 | ArrayList viewAnimList = new ArrayList(); 470 | ObjectAnimator anim,anim1; 471 | switch (mState) { 472 | case -1: 473 | if (twoStateAfterInit) { 474 | anim = ObjectAnimator.ofInt(this, "BackColor", colorStateUnSelected) 475 | .setDuration(400); 476 | } else { 477 | anim = ObjectAnimator.ofInt(this, "BackColor", colorStateSelected) 478 | .setDuration(400); 479 | } 480 | anim.setEvaluator(new ArgbEvaluator()); 481 | viewAnimList.add(anim); 482 | anim1 = ObjectAnimator.ofInt(this, "xCircle",setBoundForXCircle((float)(diameterSize /2.0))) 483 | .setDuration(400); 484 | viewAnimList.add(anim1); 485 | break; 486 | case 0: 487 | anim = ObjectAnimator.ofInt(this, "BackColor", colorStateUnSelected) 488 | .setDuration(400); 489 | anim.setEvaluator(new ArgbEvaluator()); 490 | viewAnimList.add(anim); 491 | 492 | anim1 = ObjectAnimator.ofInt(this, "xCircle",setBoundForXCircle((float)(ovalRectF.centerX()))) 493 | .setDuration(400); 494 | viewAnimList.add(anim1); 495 | break; 496 | case 1: 497 | anim = ObjectAnimator.ofInt(this, "BackColor", colorStateSelected) 498 | .setDuration(400); 499 | anim.setEvaluator(new ArgbEvaluator()); 500 | viewAnimList.add(anim); 501 | 502 | anim1 = ObjectAnimator.ofInt(this, "xCircle", setBoundForXCircle((float)(viewWidth- (diameterSize /2.0)))) 503 | .setDuration(400); 504 | viewAnimList.add(anim1); 505 | break; 506 | } 507 | animSet.playTogether(viewAnimList); 508 | animSet.addListener(new Animator.AnimatorListener() { 509 | @Override 510 | public void onAnimationStart(Animator animation) { 511 | isAnimate=true; 512 | 513 | } 514 | 515 | @Override 516 | public void onAnimationEnd(Animator animation) { 517 | isAnimate=false; 518 | } 519 | 520 | @Override 521 | public void onAnimationCancel(Animator animation) { 522 | isAnimate=false; 523 | 524 | } 525 | 526 | @Override 527 | public void onAnimationRepeat(Animator animation) { 528 | isAnimate=false; 529 | 530 | } 531 | }); 532 | 533 | animSet.start(); 534 | this.state=mState; 535 | } 536 | 537 | public static int dpToPx(int dp) 538 | { 539 | return (int) (dp * Resources.getSystem().getDisplayMetrics().density); 540 | } 541 | 542 | public static int pxToDp(int px) 543 | { 544 | return (int) (px / Resources.getSystem().getDisplayMetrics().density); 545 | } 546 | 547 | 548 | 549 | @Override 550 | protected Parcelable onSaveInstanceState() { 551 | Parcelable superState = super.onSaveInstanceState(); 552 | return new SavedState(superState, this.state); 553 | } 554 | 555 | @Override 556 | protected void onRestoreInstanceState(Parcelable state) { 557 | SavedState savedState = (SavedState) state; 558 | super.onRestoreInstanceState(savedState.getSuperState()); 559 | changeState(savedState.state,false); 560 | } 561 | 562 | 563 | /** 564 | * Convenience class to save / restore the lock combination picker state. Looks clumsy 565 | * but once created is easy to maintain and use. 566 | */ 567 | protected static class SavedState extends BaseSavedState { 568 | 569 | public static final Parcelable.Creator CREATOR = new Creator() { 570 | 571 | public SavedState createFromParcel(Parcel in) { 572 | return new SavedState(in); 573 | } 574 | 575 | public SavedState[] newArray(int size) { 576 | return new SavedState[size]; 577 | } 578 | 579 | }; 580 | public final Integer state; 581 | 582 | private SavedState(Parcelable superState, Integer state) { 583 | super(superState); 584 | this.state = state; 585 | } 586 | 587 | private SavedState(Parcel in) { 588 | super(in); 589 | state = in.readInt(); 590 | } 591 | 592 | 593 | 594 | @Override 595 | public void writeToParcel(Parcel destination, int flags) { 596 | super.writeToParcel(destination, flags); 597 | destination.writeInt(state); 598 | } 599 | 600 | } 601 | } 602 | --------------------------------------------------------------------------------