├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── id │ │ └── ss564 │ │ └── sample │ │ └── swipebuttonexample │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── id │ │ │ └── ss564 │ │ │ └── sample │ │ │ └── swipebuttonexample │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_arrow_forward.xml │ │ ├── ic_check_circle.xml │ │ ├── ic_chevron_right.xml │ │ ├── ic_favorite.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_lock_open.xml │ │ ├── ic_lock_outline.xml │ │ ├── oval_morp_shadow.png │ │ ├── sliding_background.xml │ │ ├── sliding_button_background.xml │ │ ├── sliding_button_oval_background.xml │ │ ├── sliding_button_oval_background_2.xml │ │ ├── sliding_button_oval_background_3.xml │ │ ├── sliding_icon_example_2.xml │ │ ├── sliding_icon_example_3.xml │ │ ├── sliding_icon_tint.xml │ │ ├── sliding_icon_tint_example_2.xml │ │ ├── sliding_rect_background.xml │ │ ├── sliding_rect_background_2.xml │ │ ├── sliding_rect_background_3.xml │ │ ├── sliding_shape_background.xml │ │ └── sliding_track_tint_example_3.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── id │ └── ss564 │ └── sample │ └── swipebuttonexample │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── previews ├── preview_sample_a_1.jpg ├── preview_sample_a_2.jpg ├── preview_sample_a_3.jpg ├── preview_sample_a_4.jpg ├── preview_sample_b_1.JPG ├── preview_sample_b_2.JPG ├── preview_sample_b_3.JPG └── preview_sample_b_4.JPG ├── settings.gradle └── slidingbutton ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src ├── androidTest └── java │ └── id │ └── ss564 │ └── lib │ └── slidingbutton │ └── ExampleInstrumentedTest.kt ├── main ├── AndroidManifest.xml ├── java │ └── id │ │ └── ss564 │ │ └── lib │ │ └── slidingbutton │ │ └── SlidingButton.kt └── res │ ├── drawable-v21 │ ├── default_sliding_indicator_background.xml │ ├── default_slidingbutton_background.xml │ └── ic_default_slide_icon_new.xml │ ├── drawable │ ├── default_sliding_indicator_background.xml │ ├── default_slidingbutton_background.xml │ ├── default_slidingcontainer_background.xml │ └── ic_default_slide_icon.png │ ├── layout │ └── layout_button.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── public.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── id └── ss564 └── lib └── slidingbutton └── ExampleUnitTest.kt /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | 4 | 5 | ## Version 2.0.0 _(15 Apr 2020)_ 6 | 7 | Behavior change on state listener, rename to `OnStateChangeListener`. So you can listen *state change* using method `setOnStateChangeListener()` with value `SlidingButton.OnStateChangeListener`. 8 | 9 | Change state rename to `slidingButton.changeState(active,animated)` 10 | 11 | 12 | 13 | Additional listener when button sliding. You can now listen it using method `setOnSlidingListener()` with value `SlidingButton.OnSlidingListener`. For example : 14 | 15 | - Java 16 | 17 | ```java 18 | slidingButton.setOnSlidingListener(new SlidingButton.OnSlidingListener{ 19 | 20 | @Override 21 | public void onChange(float progress){ 22 | // do what you wanna do 23 | } 24 | }); 25 | ``` 26 | 27 | 28 | 29 | - Kotlin 30 | 31 | ```kotlin 32 | slidingButton.setOnSlidingListener { progress -> 33 | // do what you wanna do 34 | } 35 | ``` 36 | 37 | 38 | 39 | Additional features like **text alpha** when sliding the button, **track indicator** and **corner radius** (especially for API 21). You can set using attributes like the following : 40 | 41 | - `app:sliding_enabledTextAlpha` 42 | 43 | Use to alpha text when sliding, value of this attribute **Boolean**. Default value true 44 | 45 | - `app:sliding_showTrack` 46 | 47 | Use to show track indicator when sliding, value of this attribute **Boolean**. Default value false 48 | 49 | - `app:sliding_trackBackground` 50 | 51 | Use to set track indicator background. 52 | 53 | - `app:sliding_trackBackgroundTint` 54 | 55 | Use to set tint track indicator background. 56 | 57 | - `app:sliding_trackExtendTo` 58 | 59 | Use to set track extended to `container` or `button` 60 | 61 | - `app:sliding_corner_radius` only on API level 21 (Lollipop) 62 | 63 | 64 | 65 | ------- 66 | 67 | ## Version 1.0.2 _(09 Apr 2020)_ 68 | 69 | Add method to change status programmatically. So you can change status use method `changeStatus(active,animated)` as you needed 70 | 71 | 72 | 73 | ------- 74 | 75 | ## Version 1.0.1 _(03 Apr 2020)_ 76 | Linked to `jcenter()`, Now not need to add maven url to repositories `build.gradle` project level. 77 | If you still using `v1.0.0`, should add maven url to repositories `build.gradle` project level 78 | ```groovy 79 | repositories { 80 | ... 81 | maven { url 'https://dl.bintray.com/ss564/SlidingButton' } 82 | ... 83 | } 84 | ``` 85 | 86 | 87 | 88 | ------- 89 | 90 | ## Version 1.0.0 _(03 Apr 2020)_ 91 | 92 | Slide button library for android, we hope this library is useful and easy to customize as you needed. 93 | 94 | 95 | 96 | ------- 97 | 98 | ## Version 1.0.0-rc _(03 Apr 2020)_ 99 | 100 | First launch as pre-release version 101 | 102 | > **Note** : Don't use this version, coz we have an missed configuration, so this version can't be used 103 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kedai564@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Source Set 564 Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SlidingButton 2 | 3 | ![Min SDK](https://img.shields.io/badge/Min%20Sdk-17-orange) 4 | ![Version](https://img.shields.io/badge/Version-v2.0.0-blue) 5 | 6 | Slide button library for android, we hope this library is useful and easy to customize as you needed. For additional information you can see [**change log**](https://github.com/Source-Set-564/SlidingButton/blob/master/CHANGELOG.md) 7 | 8 | ### Sample Preview 9 | 10 | 11 | 12 | #### Sample A 13 | 14 |
15 | Preview A-1 16 | Preview A-2 17 | Preview A-3 18 | Preview A-4 19 |
20 | 21 | #### Sample B 22 |
23 | Preview B-1 24 | Preview B-2 25 | Preview B-3 26 | Preview B-4 27 |
28 | 29 | 30 | -------- 31 | 32 | # Gradle 33 | 34 | Add **dependencies** to your `build.gradle` file at `:app` or modules level 35 | 36 | ```groovy 37 | implementation 'id.ss564.lib.slidingbutton:slidingbutton:' 38 | ``` 39 | 40 | and don't forget add `jcenter()` to your repository 41 | ```groovy 42 | repositories { 43 | //... 44 | jcenter() 45 | //... 46 | } 47 | ``` 48 | 49 | Then **sync** your gradle. 50 | 51 | ---------- 52 | 53 | # How To Use 54 | 55 | > **Notes** : This guide is still in process, I will work on it as soon as possible. So, keep checking later if you see this note. 56 | 57 | 58 | 59 | In your *layout.xml* file, add the view 60 | 61 | ```xml 62 | 66 | ``` 67 | 68 | Yeah... just add like code above. It's pretty simple :smile: 69 | 70 | You can use `SlidingButton.OnStateChangeListener` to find out if the button is shifted or not. Just add a little bit code like below on your `java` or `kotlin` file, for example on **MainActivity** 71 | 72 | - Java 73 | 74 | ```java 75 | public class MainActivity extend AppCompatActivty { 76 | 77 | @Override 78 | protected void onCreate(Bundle savedInstanceState){ 79 | super.onCreate(savedInstanceState); 80 | setContentView(R.layout.activity_main); 81 | 82 | SlidingButton mSlidingButton = findViewById(R.id.slidingButton); 83 | mSlidingButton.setOnStateChangeListener(new SlidingButton.OnStateChangeListener(){ 84 | 85 | @Override 86 | public void onChange(boolean active){ 87 | //do what you wanna to do 88 | } 89 | }) 90 | } 91 | } 92 | ``` 93 | 94 | 95 | - Kotlin 96 | 97 | ```kotlin 98 | class MainActivity : AppCompatActivity() { 99 | 100 | override fun onCreate(savedInstanceState : Bundle?){ 101 | super.onCreate(savedInstanceState) 102 | setContentView(R.layout.activity_main) 103 | 104 | //access view using synthetic, do your own style to access the view :) 105 | slidingButton.setOnStateChangeListener { active -> 106 | //or using `object : SlidingButton.OnStateChangeListener` instead of lambda 107 | 108 | //do what you wanna to do 109 | } 110 | } 111 | } 112 | ``` 113 | 114 | It's pretty simple right? :smile: 115 | 116 | 117 | 118 | If you wanna customizing, please attention to the following attributes: 119 | 120 | - `app:sliding_text` 121 | 122 | Use to set text to the view, value of this attribute is *string* that can be **hardcode**, **reference**,or **null** 123 | 124 | - `app:sliding_text_color` 125 | 126 | Use to set text color, value of this attribute can be **hardcode** or **reference**. Absolutely you can reference it to *ColorStateList* :smile: 127 | 128 | - `app:sliding_text_size` 129 | 130 | Use to set text size, value of this attribute is *dimension* that can be **hardcode** or **reference** 131 | 132 | - `app:sliding_text_fontFamily` 133 | 134 | Use to set font family by name, value of this attribute is *string* that can be **hardcode**, or **reference** 135 | 136 | - `app:sliding_text_textStyle` 137 | 138 | Use to set text style, value of this attribute `normal`, `italic`,`bold`, or `bold|italic` 139 | 140 | - `app:sliding_text_background` 141 | 142 | Use to set text background, value of this attribute can be **color**, **drawable**, or **null**. Absolutely you can reference it to *ColorStateList* or *StateListDrawable* :smile: 143 | 144 | - `app:sliding_text_paddingStart`, `app:sliding_text_paddingTop`, `app:sliding_text_paddingEnd`, `app:sliding_text_paddingBottom` 145 | 146 | Use to set text padding, value is *dimension* that can be **hardcode** or **reference** 147 | 148 | - `app:sliding_button_width`, `app:sliding_button_height` 149 | 150 | - `app:sliding_button_icon` 151 | 152 | - `app:sliding_button_icon_tint` 153 | 154 | - `app:sliding_icon_scaleType` 155 | 156 | - `app:sliding_button_background` 157 | 158 | - `app:sliding_button_paddingStart`, `app:sliding_button_paddingTop`, `app:sliding_button_paddingEnd`, `app:sliding_button_paddingBottom` 159 | 160 | - `app:sliding_button_marginStart`, `app:sliding_button_marginTop`, `app:sliding_button_marginEnd`, `app:sliding_button_marginBottom` 161 | 162 | - `app:sliding_enabledTextAlpha` 163 | 164 | Use to alpha text when sliding, value of this attribute **Boolean**. Default value true 165 | 166 | - `app:sliding_showTrack` 167 | 168 | Use to show track indicator when sliding, value of this attribute **Boolean**. Default value false 169 | 170 | - `app:sliding_trackBackground` 171 | 172 | Use to set track indicator background. 173 | 174 | - `app:sliding_trackBackgroundTint` 175 | 176 | Use to set tint track indicator background. 177 | 178 | - `app:sliding_trackExtendTo` 179 | 180 | Use to set track extended to `container` or `button` 181 | 182 | - `app:sliding_corner_radius` only on API level 21 (Lollipop) 183 | 184 | 185 | 186 | -------- 187 | 188 | # License 189 | MIT License 190 | 191 | Copyright (c) 2020 Source Set 564 Contributors 192 | 193 | Permission is hereby granted, free of charge, to any person obtaining a copy 194 | of this software and associated documentation files (the "Software"), to deal 195 | in the Software without restriction, including without limitation the rights 196 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 197 | copies of the Software, and to permit persons to whom the Software is 198 | furnished to do so, subject to the following conditions: 199 | 200 | The above copyright notice and this permission notice shall be included in all 201 | copies or substantial portions of the Software. 202 | 203 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 204 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 205 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 206 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 208 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 209 | SOFTWARE. 210 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 29 7 | buildToolsVersion "29.0.3" 8 | 9 | defaultConfig { 10 | applicationId "id.ss564.sample.swipebuttonexample" 11 | minSdkVersion 17 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 31 | implementation 'androidx.appcompat:appcompat:1.1.0' 32 | implementation 'androidx.core:core-ktx:1.2.0' 33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 34 | implementation project(':slidingbutton') 35 | 36 | testImplementation 'junit:junit:4.13' 37 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 39 | } 40 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/id/ss564/sample/swipebuttonexample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package id.ss564.sample.swipebuttonexample 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("id.ss564.sample.swipebuttonexample", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/id/ss564/sample/swipebuttonexample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package id.ss564.sample.swipebuttonexample 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.os.Handler 6 | import kotlinx.android.synthetic.main.activity_main.* 7 | 8 | class MainActivity : AppCompatActivity() { 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.activity_main) 13 | slidingButton.setOnStateChangeListener { 14 | if(it){ 15 | Handler().postDelayed({ 16 | slidingButton.changeState(false,true) 17 | },3000L) 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 18 | 22 | 26 | 27 | 28 | 29 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_forward.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_chevron_right.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 13 | 19 | 25 | 31 | 37 | 43 | 49 | 55 | 61 | 67 | 73 | 79 | 85 | 91 | 97 | 103 | 109 | 115 | 121 | 127 | 133 | 139 | 145 | 151 | 157 | 163 | 169 | 175 | 181 | 187 | 193 | 199 | 205 | 206 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_lock_open.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_lock_outline.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/oval_morp_shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/drawable/oval_morp_shadow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_button_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_button_oval_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_button_oval_background_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_button_oval_background_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_icon_example_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_icon_example_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_icon_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_icon_tint_example_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_rect_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_rect_background_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_rect_background_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_shape_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sliding_track_tint_example_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 23 | 24 | 42 | 43 | 68 | 69 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #2ec7c5 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SlidingButtonExample 3 | Move it 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/id/ss564/sample/swipebuttonexample/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package id.ss564.sample.swipebuttonexample 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.71' 5 | repositories { 6 | google() 7 | jcenter() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.6.2' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | plugins { 20 | id "com.jfrog.bintray" version "1.8.4" 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | google() 26 | jcenter() 27 | mavenCentral() 28 | } 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } 34 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | -------------------------------------------------------------------------------- /previews/preview_sample_a_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_a_1.jpg -------------------------------------------------------------------------------- /previews/preview_sample_a_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_a_2.jpg -------------------------------------------------------------------------------- /previews/preview_sample_a_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_a_3.jpg -------------------------------------------------------------------------------- /previews/preview_sample_a_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_a_4.jpg -------------------------------------------------------------------------------- /previews/preview_sample_b_1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_b_1.JPG -------------------------------------------------------------------------------- /previews/preview_sample_b_2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_b_2.JPG -------------------------------------------------------------------------------- /previews/preview_sample_b_3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_b_3.JPG -------------------------------------------------------------------------------- /previews/preview_sample_b_4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/previews/preview_sample_b_4.JPG -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='SlidingButtonExample' 2 | include ':app' 3 | include ':slidingbutton' 4 | -------------------------------------------------------------------------------- /slidingbutton/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 29 7 | buildToolsVersion "29.0.3" 8 | 9 | defaultConfig { 10 | minSdkVersion 17 11 | targetSdkVersion 29 12 | versionCode 5 13 | versionName "2.0.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles 'consumer-rules.pro' 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 31 | implementation 'androidx.appcompat:appcompat:1.1.0' 32 | 33 | testImplementation 'junit:junit:4.13' 34 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 35 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 36 | } 37 | 38 | apply from: 'publish.gradle' 39 | -------------------------------------------------------------------------------- /slidingbutton/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/slidingbutton/consumer-rules.pro -------------------------------------------------------------------------------- /slidingbutton/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /slidingbutton/src/androidTest/java/id/ss564/lib/slidingbutton/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package id.ss564.lib.slidingbutton 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("id.ss564.lib.slidingbutton.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /slidingbutton/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /slidingbutton/src/main/java/id/ss564/lib/slidingbutton/SlidingButton.kt: -------------------------------------------------------------------------------- 1 | package id.ss564.lib.slidingbutton 2 | 3 | import android.animation.Animator 4 | import android.animation.ValueAnimator 5 | import android.content.Context 6 | import android.content.res.ColorStateList 7 | import android.graphics.* 8 | import android.graphics.drawable.ColorDrawable 9 | import android.graphics.drawable.Drawable 10 | import android.os.Build 11 | import android.util.AttributeSet 12 | import android.util.TypedValue 13 | import android.view.* 14 | import android.view.animation.* 15 | import android.widget.FrameLayout 16 | import android.widget.ImageView 17 | import android.widget.TextView 18 | import androidx.annotation.* 19 | import androidx.core.content.ContextCompat 20 | import kotlin.math.max 21 | 22 | /** 23 | * Created by Anwar on 29 Mar 2020. 24 | */ 25 | 26 | class SlidingButton : FrameLayout { 27 | 28 | private val inflatedView: View 29 | private lateinit var slidingImage: ImageView 30 | private lateinit var slidingText: TextView 31 | private lateinit var slidingIndicator: View 32 | 33 | private var mStateListener: OnStateChangeListener? = null 34 | private var slidingListener: OnSlidingListener? = null 35 | 36 | private var statusActive = false 37 | set(value) { 38 | field = value 39 | if (value && showIndicator) animatedIndicator() 40 | mStateListener?.onChange(value) 41 | } 42 | 43 | private var startOfButton = 0F 44 | private var endOfButton = 0F 45 | 46 | private var showIndicator = true 47 | private var enableTextAlpha = true 48 | 49 | private val stateListIconTint: ColorStateList 50 | 51 | var buttonBackground: Drawable? = null 52 | set(value) { 53 | field = value 54 | if (::slidingImage.isInitialized) 55 | slidingImage.background = value 56 | } 57 | 58 | var buttonIcon: Drawable? = null 59 | set(value) { 60 | field = value 61 | if (::slidingImage.isInitialized) 62 | slidingImage.setImageDrawable(value) 63 | } 64 | 65 | var iconScaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER_INSIDE 66 | set(value) { 67 | when (value) { 68 | ImageView.ScaleType.CENTER_INSIDE, 69 | ImageView.ScaleType.CENTER_CROP, 70 | ImageView.ScaleType.CENTER, 71 | ImageView.ScaleType.FIT_CENTER, 72 | ImageView.ScaleType.FIT_XY -> { 73 | field = value 74 | if (::slidingImage.isInitialized) slidingImage.scaleType = value 75 | } 76 | else -> throw IllegalArgumentException("ScaleType $value aren't allowed, please use CENTER, CENTER_CROP, CENTER_INSIDE,FIT_CENTER, or FIT_XY") 77 | } 78 | } 79 | 80 | var textBackground: Drawable? = null 81 | set(value) { 82 | field = value 83 | if (::slidingText.isInitialized) slidingText.background = value 84 | } 85 | 86 | private var mTextSize: Float = 0F 87 | private set(value) { 88 | field = value 89 | if (::slidingText.isInitialized) 90 | slidingText.setTextSize(TypedValue.COMPLEX_UNIT_PX, value) 91 | } 92 | 93 | val textSize: Float 94 | get() = if (::slidingText.isInitialized) { 95 | slidingText.textSize 96 | } else { 97 | mTextSize 98 | } 99 | 100 | /** 101 | * [textPaddings] 102 | * index of the array mean [0] start,[1] top,[2] end,[3] bottom 103 | */ 104 | val textPaddings = intArrayOf(0, 0, 0, 0) 105 | 106 | var textColors: ColorStateList? = null 107 | set(value) { 108 | field = value 109 | if (::slidingText.isInitialized) 110 | slidingText.setTextColor(value) 111 | } 112 | 113 | val currentTextColor = 114 | if (::slidingText.isInitialized) slidingText.currentTextColor else textColors?.defaultColor 115 | 116 | var textTypeface: Typeface? = null 117 | set(value) { 118 | field = value 119 | if (::slidingText.isInitialized && value != null) slidingText.typeface = value 120 | } 121 | 122 | private var mText: String? = null 123 | set(value) { 124 | field = value 125 | if (::slidingText.isInitialized) 126 | slidingText.text = value 127 | } 128 | 129 | var buttonWidth: Int = 0 130 | set(value) { 131 | field = value 132 | if (::slidingImage.isInitialized) slidingImage.layoutParams.width = value 133 | } 134 | 135 | var buttonHeight: Int = 0 136 | set(value) { 137 | field = value 138 | if (::slidingImage.isInitialized) slidingImage.layoutParams.height = value 139 | } 140 | 141 | /** 142 | * [buttonMargins],[buttonPaddings] 143 | * index of the array mean [0] start,[1] top,[2] end,[3] bottom 144 | */ 145 | val buttonMargins = intArrayOf(0, 0, 0, 0) 146 | val buttonPaddings = intArrayOf(0, 0, 0, 0) 147 | 148 | private var trackExtendedTo = TrackExtended.BUTTON 149 | 150 | var cornerRadius: Float = 0F 151 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 152 | set(value) { 153 | field = value 154 | if (::slidingImage.isInitialized) { 155 | RoundedOutlineProvider(value).also { 156 | this.outlineProvider = it 157 | slidingImage.outlineProvider = it 158 | slidingText.outlineProvider = it 159 | slidingIndicator.outlineProvider = it 160 | } 161 | } 162 | } 163 | 164 | var trackBackground: Drawable? = null 165 | set(value) { 166 | field = value 167 | if (::slidingIndicator.isInitialized) { 168 | slidingIndicator.background = value 169 | } 170 | } 171 | 172 | var trackBackgroundTint: ColorStateList? = null 173 | set(value) { 174 | field = value 175 | if (::slidingIndicator.isInitialized) { 176 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 177 | slidingIndicator.background?.setTintList(value) 178 | } else if (value != null) { 179 | slidingIndicator.background?.colorFilter = PorterDuffColorFilter( 180 | value.getColorForState(slidingIndicator.drawableState, value.defaultColor), 181 | PorterDuff.Mode.SRC_IN 182 | ) 183 | } 184 | } 185 | } 186 | 187 | constructor(context: Context) : this(context, null) 188 | 189 | constructor(context: Context, attrs: AttributeSet?) : this( 190 | context, 191 | attrs, 192 | R.attr.slidingButtonStyle 193 | ) 194 | 195 | constructor( 196 | _context: Context, 197 | attrs: AttributeSet?, 198 | defStyleInt: Int = R.attr.slidingButtonStyle 199 | ) : super( 200 | _context, 201 | attrs, 202 | defStyleInt 203 | ) { 204 | 205 | val colorPrimary = TypedValue() 206 | context.theme.resolveAttribute(R.attr.colorPrimary, colorPrimary, true) 207 | 208 | val ex by lazy { 209 | TypedValue().apply { 210 | context.theme.resolveAttribute( 211 | R.attr.colorAccent, 212 | this, 213 | true 214 | ) 215 | }.data 216 | } 217 | val colorAccent = TypedValue() 218 | context.theme.resolveAttribute(R.attr.colorAccent, colorAccent, true) 219 | val arr = context.obtainStyledAttributes( 220 | attrs, 221 | R.styleable.SlidingButton, 222 | defStyleInt, 223 | R.style.SlidingButton 224 | ) 225 | 226 | /** 227 | * TextView attrs configuration 228 | */ 229 | val defaultTextSize = context.resources.getDimension(R.dimen.default_text_size) 230 | mTextSize = arr.getDimension(R.styleable.SlidingButton_sliding_text_size, defaultTextSize) 231 | 232 | textColors = arr.getColorStateList(R.styleable.SlidingButton_sliding_text_color) 233 | ?: ColorStateList.valueOf(ex) 234 | 235 | mText = arr.getString(R.styleable.SlidingButton_sliding_text) 236 | textBackground = arr.getDrawable(R.styleable.SlidingButton_sliding_text_background) 237 | 238 | val textStyle = arr.getInteger(R.styleable.SlidingButton_sliding_text_textStyle, 0).let { 239 | if (it < 0) 0 else it 240 | } 241 | val fontFamilyName = arr.getString(R.styleable.SlidingButton_sliding_text_fontFamily) 242 | ?: "sans-serif" 243 | textTypeface = Typeface.create(fontFamilyName, textStyle) 244 | textPaddings[0] = arr.getDimensionPixelSize( 245 | R.styleable.SlidingButton_sliding_text_paddingStart, 246 | 0 247 | ) 248 | textPaddings[1] = arr.getDimensionPixelSize( 249 | R.styleable.SlidingButton_sliding_text_paddingTop, 250 | 0 251 | ) 252 | textPaddings[2] = arr.getDimensionPixelSize( 253 | R.styleable.SlidingButton_sliding_text_paddingEnd, 254 | 0 255 | ) 256 | textPaddings[3] = arr.getDimensionPixelSize( 257 | R.styleable.SlidingButton_sliding_text_paddingBottom, 258 | 0 259 | ) 260 | 261 | /** 262 | * ImageView attrs configuration 263 | */ 264 | val defaultButtonDrawable = 265 | ContextCompat.getDrawable(context, R.drawable.ic_default_slide_icon) 266 | buttonIcon = arr.getDrawable(R.styleable.SlidingButton_sliding_button_icon) 267 | ?: defaultButtonDrawable 268 | 269 | stateListIconTint = arr.getColorStateList( 270 | R.styleable.SlidingButton_sliding_button_icon_tint 271 | ) ?: ColorStateList.valueOf(colorAccent.data) 272 | 273 | buttonBackground = arr.getDrawable(R.styleable.SlidingButton_sliding_button_background) 274 | 275 | val defaultButtonSize = resources.getDimensionPixelSize(R.dimen.default_image_height) 276 | buttonWidth = arr.getDimensionPixelSize( 277 | R.styleable.SlidingButton_sliding_button_width, 278 | defaultButtonSize 279 | ) 280 | buttonHeight = arr.getDimensionPixelSize( 281 | R.styleable.SlidingButton_sliding_button_height, 282 | defaultButtonSize 283 | ) 284 | 285 | val scaleName = arr.getInteger(R.styleable.SlidingButton_sliding_icon_scaleType, 7).let { 286 | when (it) { 287 | 1 -> "FIT_XY" 288 | 3 -> "FIT_CENTER" 289 | 5 -> "CENTER" 290 | 6 -> "CENTER_CROP" 291 | else -> "CENTER_INSIDE" 292 | } 293 | } 294 | iconScaleType = ImageView.ScaleType.valueOf(scaleName) 295 | 296 | buttonMargins[0] = arr.getDimensionPixelSize( 297 | R.styleable.SlidingButton_sliding_button_marginStart, 298 | 0 299 | ) 300 | buttonMargins[1] = arr.getDimensionPixelSize( 301 | R.styleable.SlidingButton_sliding_button_marginTop, 302 | 0 303 | ) 304 | buttonMargins[2] = arr.getDimensionPixelSize( 305 | R.styleable.SlidingButton_sliding_button_marginEnd, 306 | 0 307 | ) 308 | buttonMargins[3] = arr.getDimensionPixelSize( 309 | R.styleable.SlidingButton_sliding_button_marginBottom, 310 | 0 311 | ) 312 | 313 | buttonPaddings[0] = arr.getDimensionPixelSize( 314 | R.styleable.SlidingButton_sliding_button_paddingStart, 315 | 0 316 | ) 317 | buttonPaddings[1] = arr.getDimensionPixelSize( 318 | R.styleable.SlidingButton_sliding_button_paddingTop, 319 | 0 320 | ) 321 | buttonPaddings[2] = arr.getDimensionPixelSize( 322 | R.styleable.SlidingButton_sliding_button_paddingEnd, 323 | 0 324 | ) 325 | buttonPaddings[3] = arr.getDimensionPixelSize( 326 | R.styleable.SlidingButton_sliding_button_paddingBottom, 327 | 0 328 | ) 329 | 330 | enableTextAlpha = arr.getBoolean(R.styleable.SlidingButton_sliding_enabledTextAlpha, true) 331 | showIndicator = arr.getBoolean(R.styleable.SlidingButton_sliding_showTrack, false) 332 | 333 | trackBackground = arr.getDrawable(R.styleable.SlidingButton_sliding_trackBackground) 334 | ?: ContextCompat.getDrawable(context, R.drawable.default_sliding_indicator_background) 335 | 336 | trackBackgroundTint = arr.getColorStateList( 337 | R.styleable.SlidingButton_sliding_trackBackgroundTint 338 | ) 339 | 340 | val index = arr.getInteger(R.styleable.SlidingButton_sliding_trackExtendTo, 1) 341 | trackExtendedTo = arrayOf(TrackExtended.CONTAINER, TrackExtended.BUTTON)[index] 342 | 343 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 344 | cornerRadius = arr.getDimension(R.styleable.SlidingButton_sliding_corner_radius, 0F) 345 | buttonIcon?.setTintList(stateListIconTint) 346 | trackBackground?.setTintList(trackBackgroundTint) 347 | } else { 348 | buttonIcon?.colorFilter = PorterDuffColorFilter( 349 | stateListIconTint.defaultColor, 350 | PorterDuff.Mode.SRC_IN 351 | ) 352 | if (trackBackgroundTint != null && trackBackground != null) { 353 | trackBackground?.colorFilter = PorterDuffColorFilter( 354 | trackBackgroundTint!!.getColorForState( 355 | trackBackground!!.state, 356 | trackBackgroundTint!!.defaultColor 357 | ), 358 | PorterDuff.Mode.SRC_IN 359 | ) 360 | } 361 | } 362 | 363 | arr.recycle() 364 | 365 | inflatedView = LayoutInflater.from(context).inflate(R.layout.layout_button, this, true) 366 | } 367 | 368 | override fun onFinishInflate() { 369 | super.onFinishInflate() 370 | slidingText = inflatedView.findViewById(R.id.slidingText) 371 | slidingIndicator = inflatedView.findViewById(R.id.slidingIndicator) 372 | slidingImage = inflatedView.findViewById(R.id.slidingImage) 373 | 374 | configureTextView() 375 | configureTrackView() 376 | configureImageView() 377 | 378 | //Apply Rounded corner to views 379 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 380 | RoundedOutlineProvider(cornerRadius).also { 381 | this.outlineProvider = it 382 | slidingText.outlineProvider = it 383 | slidingImage.outlineProvider = it 384 | slidingIndicator.outlineProvider = it 385 | } 386 | } 387 | 388 | configureTouch() 389 | } 390 | 391 | private fun configureTrackView() { 392 | if (showIndicator) { 393 | slidingIndicator.background = trackBackground 394 | slidingIndicator.viewTreeObserver.addOnGlobalLayoutListener(object : 395 | ViewTreeObserver.OnGlobalLayoutListener { 396 | override fun onGlobalLayout() { 397 | slidingIndicator.visibility = View.VISIBLE 398 | val lp = slidingIndicator.layoutParams as LayoutParams 399 | when (trackExtendedTo) { 400 | TrackExtended.BUTTON -> { 401 | lp.width = startOfButton.toInt() + buttonWidth - buttonMargins[2] 402 | lp.height = buttonHeight 403 | lp.marginStart = buttonMargins[0] 404 | lp.marginEnd = buttonMargins[2] 405 | } 406 | TrackExtended.CONTAINER -> { 407 | lp.width = 0 408 | lp.height = this@SlidingButton.measuredHeight 409 | lp.marginStart = 0 410 | lp.marginEnd = 0 411 | } 412 | } 413 | slidingIndicator.layoutParams = lp 414 | inflatedView.viewTreeObserver.removeOnGlobalLayoutListener(this) 415 | } 416 | }) 417 | } else { 418 | slidingIndicator.visibility = View.GONE 419 | } 420 | } 421 | 422 | private fun configureImageView() { 423 | slidingImage.background = buttonBackground 424 | slidingImage.layoutParams.let { it as LayoutParams }.also { 425 | it.width = buttonWidth 426 | it.height = buttonHeight 427 | it.marginStart = buttonMargins[0] 428 | it.topMargin = buttonMargins[1] 429 | it.marginEnd = buttonMargins[2] 430 | it.bottomMargin = buttonMargins[3] 431 | slidingImage.layoutParams = it 432 | } 433 | slidingImage.setPaddingRelative( 434 | buttonPaddings[0], 435 | buttonPaddings[1], 436 | buttonPaddings[2], 437 | buttonPaddings[3] 438 | ) 439 | slidingImage.scaleType = iconScaleType 440 | slidingImage.setImageDrawable(buttonIcon) 441 | } 442 | 443 | private fun configureTextView() { 444 | slidingText.background = textBackground 445 | slidingText.setPaddingRelative( 446 | textPaddings[0], 447 | textPaddings[1], 448 | textPaddings[2], 449 | textPaddings[3] 450 | ) 451 | slidingText.text = mText 452 | slidingText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize) 453 | slidingText.setTextColor(textColors) 454 | slidingText.typeface = textTypeface 455 | } 456 | 457 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { 458 | super.onSizeChanged(w, h, oldw, oldh) 459 | startOfButton = buttonMargins[0].toFloat() 460 | endOfButton = 461 | w.toFloat() - (buttonWidth.toFloat() + buttonMargins[2].toFloat() + paddingEnd.toFloat() + paddingStart.toFloat()) 462 | } 463 | 464 | override fun removeAllViews() = throw IllegalStateException("This method isn't allowed ") 465 | 466 | override fun removeView(view: View?) = throw IllegalStateException("This method isn't allowed ") 467 | 468 | override fun removeViewAt(index: Int) = 469 | throw IllegalStateException("This method isn't allowed ") 470 | 471 | private fun configureTouch() { 472 | this.setOnTouchListener { _, event -> 473 | val startTouch = (paddingStart + buttonMargins[0]).toFloat() 474 | val maxStartTouch = startTouch + buttonWidth 475 | val isStartTouch = event.x in startTouch..maxStartTouch 476 | 477 | val maxEndTouch = (this.width - paddingEnd - buttonMargins[2]).toFloat() 478 | val isEndTouch = event.x in endOfButton..maxEndTouch 479 | 480 | when (event.action) { 481 | MotionEvent.ACTION_DOWN -> (isStartTouch && !statusActive) || (isEndTouch && statusActive) 482 | MotionEvent.ACTION_MOVE -> onMove(event, startTouch, endOfButton + buttonWidth) 483 | MotionEvent.ACTION_UP -> onUp() 484 | else -> true 485 | } 486 | } 487 | } 488 | 489 | override fun performClick(): Boolean = super.performClick() 490 | 491 | private fun onUp(): Boolean = when { 492 | slidingImage.x + buttonWidth < this.width * 0.55F && slidingImage.x > startOfButton -> { 493 | animatedToStart() 494 | true 495 | } 496 | slidingImage.x + buttonWidth >= this.width * 0.55F -> { 497 | animatedToEnd() 498 | true 499 | } 500 | slidingImage.x <= startOfButton -> { 501 | translateAnimation() 502 | true 503 | } 504 | else -> false 505 | } 506 | 507 | fun changeState(active: Boolean, animated: Boolean = false) { 508 | if (animated && active) { 509 | statusActive = true 510 | animatedToEnd() 511 | } else if (animated && !active) { 512 | statusActive = false 513 | animatedToStart() 514 | } else if (active) { 515 | slidingImage.x = endOfButton 516 | statusActive = true 517 | } else { 518 | slidingImage.x = startOfButton 519 | statusActive = false 520 | } 521 | } 522 | 523 | private fun animatedToStart() { 524 | if (isActivated) isActivated = false 525 | 526 | val floatAnimator = ValueAnimator.ofFloat(slidingImage.x, startOfButton) 527 | floatAnimator.addUpdateListener { 528 | updateSlidingXPosition(it.animatedValue as Float) 529 | } 530 | floatAnimator.addListener(object : Animator.AnimatorListener { 531 | override fun onAnimationRepeat(animation: Animator?) {} 532 | 533 | override fun onAnimationEnd(animation: Animator?) { 534 | isActivated = false 535 | if (statusActive) statusActive = false 536 | } 537 | 538 | override fun onAnimationCancel(animation: Animator?) {} 539 | 540 | override fun onAnimationStart(animation: Animator?) {} 541 | }) 542 | floatAnimator.duration = 115L 543 | floatAnimator.interpolator = AccelerateDecelerateInterpolator() 544 | floatAnimator.start() 545 | } 546 | 547 | private fun animatedToEnd() { 548 | val floatAnimator = ValueAnimator.ofFloat(slidingImage.x, endOfButton) 549 | floatAnimator.addUpdateListener { 550 | updateSlidingXPosition(it.animatedValue as Float) 551 | } 552 | floatAnimator.addListener(object : Animator.AnimatorListener { 553 | override fun onAnimationRepeat(animation: Animator?) {} 554 | 555 | override fun onAnimationEnd(animation: Animator?) { 556 | isActivated = true 557 | if (!statusActive) statusActive = true 558 | } 559 | 560 | override fun onAnimationCancel(animation: Animator?) {} 561 | 562 | override fun onAnimationStart(animation: Animator?) {} 563 | }) 564 | floatAnimator.duration = 115L 565 | floatAnimator.interpolator = AccelerateDecelerateInterpolator() 566 | floatAnimator.start() 567 | } 568 | 569 | private fun animatedIndicator() { 570 | val animation = ScaleAnimation( 571 | 0F, 572 | 1F, 573 | 1F, 574 | 1F, 575 | slidingIndicator.width.toFloat(), 576 | slidingIndicator.height * 0.5F 577 | ) 578 | animation.duration = 500L 579 | animation.interpolator = DecelerateInterpolator() 580 | slidingIndicator.startAnimation(animation) 581 | } 582 | 583 | private fun onMove(event: MotionEvent, start: Float, end: Float): Boolean { 584 | if (isActivated) isActivated = false 585 | 586 | if (event.x < startOfButton + buttonWidth) { 587 | updateSlidingXPosition(startOfButton) 588 | return true 589 | } 590 | 591 | if (event.x in start..end) { 592 | updateSlidingXPosition(event.x - buttonWidth) 593 | return true 594 | } 595 | 596 | return false 597 | } 598 | 599 | private fun updateSlidingXPosition(x: Float) { 600 | slidingImage.x = x 601 | val realX = x - startOfButton 602 | val percent = realX / (endOfButton - startOfButton) 603 | 604 | if (enableTextAlpha) { 605 | slidingText.alpha = if (percent < 0.2F) 1F - percent else max(0F, 1F - (percent + 0.3F)) 606 | } 607 | 608 | if (showIndicator) { 609 | val lp = slidingIndicator.layoutParams as LayoutParams 610 | lp.width = when { 611 | trackExtendedTo == TrackExtended.CONTAINER && percent == 0F -> 0 612 | trackExtendedTo == TrackExtended.CONTAINER && percent < 1F -> x.toInt() + buttonWidth 613 | trackExtendedTo == TrackExtended.CONTAINER && percent >= 1F -> x.toInt() + buttonWidth + buttonMargins[2] 614 | else -> x.toInt() + buttonWidth - buttonMargins[2] 615 | } 616 | slidingIndicator.layoutParams = lp 617 | } 618 | slidingListener?.onSliding(percent) 619 | } 620 | 621 | private fun translateAnimation() { 622 | slidingImage.clearAnimation() 623 | val animation = TranslateAnimation(0F, endOfButton, 0F, 0F) 624 | animation.interpolator = AccelerateDecelerateInterpolator() 625 | animation.duration = 350L 626 | animation.setAnimationListener(object : Animation.AnimationListener { 627 | override fun onAnimationRepeat(animation: Animation?) {} 628 | 629 | override fun onAnimationEnd(animation: Animation?) { 630 | slidingImage.clearAnimation() 631 | val anim = ScaleAnimation( 632 | 0.35F, 633 | 1F, 634 | 0.35F, 635 | 1F, 636 | slidingImage.width.toFloat() / 2F, 637 | slidingImage.height.toFloat() / 2F 638 | ) 639 | anim.interpolator = DecelerateInterpolator() 640 | anim.duration = 225L 641 | anim.setAnimationListener(object : Animation.AnimationListener { 642 | override fun onAnimationRepeat(animation: Animation?) {} 643 | 644 | override fun onAnimationEnd(animation: Animation?) { 645 | slidingImage.clearAnimation() 646 | slidingImage.scaleX = 1F 647 | slidingImage.scaleY = 1F 648 | if (showIndicator) slidingIndicator.alpha = 1F 649 | } 650 | 651 | override fun onAnimationStart(animation: Animation?) {} 652 | }) 653 | slidingImage.startAnimation(anim) 654 | } 655 | 656 | override fun onAnimationStart(animation: Animation?) { 657 | if (showIndicator) slidingIndicator.alpha = 0F 658 | } 659 | }) 660 | slidingImage.startAnimation(animation) 661 | } 662 | 663 | override fun setEnabled(enabled: Boolean) { 664 | super.setEnabled(enabled) 665 | slidingText.isEnabled = enabled 666 | slidingImage.isEnabled = enabled 667 | slidingIndicator.isEnabled = enabled 668 | changeStateDrawablePreLollipop() 669 | } 670 | 671 | fun setText(text: String) { 672 | mText = text 673 | } 674 | 675 | fun setText(@StringRes resId: Int) { 676 | mText = context.resources.getString(resId) 677 | } 678 | 679 | fun setTextSize(@Dimension size: Float) { 680 | mTextSize = size 681 | } 682 | 683 | fun setTextColor(@ColorInt color: Int) { 684 | textColors = ColorStateList.valueOf(color) 685 | } 686 | 687 | fun setTextBackground(@DrawableRes resId: Int) { 688 | textBackground = ContextCompat.getDrawable(context, resId) 689 | } 690 | 691 | fun setTextBackgroundColor(@ColorInt color: Int) { 692 | when (textBackground) { 693 | is ColorDrawable -> (slidingText.background as ColorDrawable).color = color 694 | else -> textBackground = ColorDrawable(color) 695 | } 696 | } 697 | 698 | fun setTextPadding(start: Int, top: Int, end: Int, bottom: Int) { 699 | textPaddings[0] = start 700 | textPaddings[1] = top 701 | textPaddings[2] = end 702 | textPaddings[3] = bottom 703 | slidingText.setPadding(textPaddings[0], textPaddings[1], textPaddings[2], textPaddings[3]) 704 | } 705 | 706 | fun setButtonIcon(@DrawableRes resId: Int) { 707 | buttonIcon = ContextCompat.getDrawable(context, resId) 708 | } 709 | 710 | fun setButtonBackground(@DrawableRes resId: Int) { 711 | buttonBackground = ContextCompat.getDrawable(context, resId) 712 | } 713 | 714 | fun setButtonBackgroundColor(@ColorInt color: Int) { 715 | when (buttonBackground) { 716 | is ColorDrawable -> (slidingImage.background as ColorDrawable).color = color 717 | else -> buttonBackground = ColorDrawable(color) 718 | } 719 | } 720 | 721 | fun setButtonPadding(start: Int, top: Int, end: Int, bottom: Int) { 722 | buttonPaddings[0] = start 723 | buttonPaddings[1] = top 724 | buttonPaddings[2] = end 725 | buttonPaddings[3] = bottom 726 | slidingImage.setPadding( 727 | buttonPaddings[0], 728 | buttonPaddings[1], 729 | buttonPaddings[2], 730 | buttonPaddings[3] 731 | ) 732 | } 733 | 734 | fun setButtonMargin(start: Int, top: Int, end: Int, bottom: Int) { 735 | buttonMargins[0] = start 736 | buttonMargins[1] = top 737 | buttonMargins[2] = end 738 | buttonMargins[3] = bottom 739 | slidingImage.layoutParams.let { it as LayoutParams }.setMargins( 740 | buttonMargins[0], 741 | buttonMargins[1], 742 | buttonMargins[2], 743 | buttonMargins[3] 744 | ) 745 | } 746 | 747 | fun setOnStateChangeListener(listener: OnStateChangeListener?) { 748 | mStateListener = listener 749 | } 750 | 751 | fun setOnStateChangeListener(l: (active: Boolean) -> Unit) { 752 | this.setOnStateChangeListener(object : OnStateChangeListener { 753 | override fun onChange(active: Boolean) { 754 | l.invoke(active) 755 | } 756 | }) 757 | } 758 | 759 | fun setOnSlidingListener(listener: OnSlidingListener?) { 760 | slidingListener = listener 761 | } 762 | 763 | fun setOnSlidingListener(l: (progress: Float) -> Unit) { 764 | this.setOnSlidingListener(object : OnSlidingListener { 765 | override fun onSliding(progress: Float) { 766 | l.invoke(progress) 767 | } 768 | }) 769 | } 770 | 771 | override fun setActivated(activated: Boolean) { 772 | super.setActivated(activated) 773 | changeStateDrawablePreLollipop() 774 | } 775 | 776 | private fun changeStateDrawablePreLollipop() { 777 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return 778 | 779 | slidingImage.drawable.colorFilter = PorterDuffColorFilter( 780 | stateListIconTint.getColorForState( 781 | slidingImage.drawableState, 782 | stateListIconTint.defaultColor 783 | ), PorterDuff.Mode.SRC_IN 784 | ) 785 | 786 | trackBackgroundTint?.let { 787 | slidingIndicator.background?.colorFilter = PorterDuffColorFilter( 788 | it.getColorForState(slidingIndicator.drawableState, it.defaultColor), 789 | PorterDuff.Mode.SRC_IN 790 | ) 791 | } 792 | } 793 | 794 | interface OnStateChangeListener { 795 | fun onChange(active: Boolean) 796 | } 797 | 798 | interface OnSlidingListener { 799 | fun onSliding(progress: Float) 800 | } 801 | 802 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 803 | private class RoundedOutlineProvider(val radius: Float) : ViewOutlineProvider() { 804 | override fun getOutline(view: View, outline: Outline) { 805 | val rect = Rect(0, 0, view.width, view.height) 806 | outline.setRoundRect(rect, radius) 807 | view.clipToOutline = true 808 | } 809 | } 810 | 811 | @Keep 812 | enum class TrackExtended constructor(val value: Int) { 813 | CONTAINER(0), 814 | BUTTON(1) 815 | } 816 | } -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable-v21/default_sliding_indicator_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable-v21/default_slidingbutton_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable-v21/ic_default_slide_icon_new.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable/default_sliding_indicator_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable/default_slidingbutton_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable/default_slidingcontainer_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/drawable/ic_default_slide_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Source-Set-564/SlidingButton/8d5afe783b96b348623f8906fb7d2cf54417d804/slidingbutton/src/main/res/drawable/ic_default_slide_icon.png -------------------------------------------------------------------------------- /slidingbutton/src/main/res/layout/layout_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #E8ECEF 4 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 46dp 4 | 18sp 5 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/values/public.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Slide the button 4 | -------------------------------------------------------------------------------- /slidingbutton/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 18 | 19 | 22 | 23 | 26 | 27 | 31 | -------------------------------------------------------------------------------- /slidingbutton/src/test/java/id/ss564/lib/slidingbutton/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package id.ss564.lib.slidingbutton 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | --------------------------------------------------------------------------------