├── .gitattributes
├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── misc.xml
└── runConfigurations.xml
├── LICENSE
├── README.md
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── liquidswipedemo
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── liquidswipedemo
│ │ │ ├── CustomFragmentPagerAdapter.kt
│ │ │ ├── CustomPagerAdapter.kt
│ │ │ ├── DataValues.kt
│ │ │ ├── DummyFragment.kt
│ │ │ └── MainActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── fragment_dummy.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
│ │ ├── raw
│ │ ├── camera.json
│ │ ├── luggage.json
│ │ ├── map.json
│ │ ├── mountain.json
│ │ └── van.json
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── liquidswipedemo
│ └── ExampleUnitTest.kt
├── build.gradle
├── demo screenshots
├── LiquidSwipeDemo.gif
└── LiquidSwipeDemo_Touch_Interactive.gif
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── liquidswipe
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jem
│ │ └── liquidswipe
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── jem
│ │ │ └── liquidswipe
│ │ │ ├── LiquidSwipeViewPager.kt
│ │ │ ├── base
│ │ │ ├── ClipPathProvider.kt
│ │ │ └── LiquidSwipeLayout.kt
│ │ │ ├── clippathprovider
│ │ │ └── LiquidSwipeClipPathProvider.kt
│ │ │ ├── layout
│ │ │ ├── LiquidSwipeConstraintLayout.kt
│ │ │ ├── LiquidSwipeFrameLayout.kt
│ │ │ └── LiquidSwipeLinearLayout.kt
│ │ │ └── util
│ │ │ └── FixedSpeedScroller.kt
│ └── res
│ │ └── values
│ │ └── attrs.xml
│ └── test
│ └── java
│ └── com
│ └── jem
│ └── liquidswipe
│ └── ExampleUnitTest.kt
└── settings.gradle
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: set up JDK 1.8
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.8
16 | - name: Make Gradle executable
17 | run: chmod +x ./gradlew
18 | - name: Build with Gradle
19 | run: ./gradlew build
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 | *.aab
5 |
6 | # Files for the ART/Dalvik VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # Generated files
13 | bin/
14 | gen/
15 | out/
16 | # Uncomment the following line in case you need and you don't have the release build type files in your app
17 | # release/
18 |
19 | # Gradle files
20 | .gradle/
21 | build/
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # Log Files
30 | *.log
31 |
32 | # Android Studio Navigation editor temp files
33 | .navigation/
34 |
35 | # Android Studio captures folder
36 | captures/
37 |
38 | # IntelliJ
39 | *.iml
40 | .idea/workspace.xml
41 | .idea/tasks.xml
42 | .idea/gradle.xml
43 | .idea/assetWizardSettings.xml
44 | .idea/dictionaries
45 | .idea/libraries
46 | # Android Studio 3 in .gitignore file.
47 | .idea/caches
48 | .idea/modules.xml
49 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
50 | .idea/navEditor.xml
51 |
52 | # Keystore files
53 | # Uncomment the following lines if you do not want to check your keystore files in.
54 | #*.jks
55 | #*.keystore
56 |
57 | # External native build folder generated in Android Studio 2.2 and later
58 | .externalNativeBuild
59 |
60 | # Google Services (e.g. APIs or Firebase)
61 | # google-services.json
62 |
63 | # Freeline
64 | freeline.py
65 | freeline/
66 | freeline_project_description.json
67 |
68 | # fastlane
69 | fastlane/report.xml
70 | fastlane/Preview.html
71 | fastlane/screenshots
72 | fastlane/test_output
73 | fastlane/readme.md
74 |
75 | # Version control
76 | vcs.xml
77 |
78 | # lint
79 | lint/intermediates/
80 | lint/generated/
81 | lint/outputs/
82 | lint/tmp/
83 | # lint/reports/
84 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | xmlns:android
17 |
18 | ^$
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | xmlns:.*
28 |
29 | ^$
30 |
31 |
32 | BY_NAME
33 |
34 |
35 |
36 |
37 |
38 |
39 | .*:id
40 |
41 | http://schemas.android.com/apk/res/android
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | .*:name
51 |
52 | http://schemas.android.com/apk/res/android
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | name
62 |
63 | ^$
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | style
73 |
74 | ^$
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | .*
84 |
85 | ^$
86 |
87 |
88 | BY_NAME
89 |
90 |
91 |
92 |
93 |
94 |
95 | .*
96 |
97 | http://schemas.android.com/apk/res/android
98 |
99 |
100 | ANDROID_ATTRIBUTE_ORDER
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | .*
110 |
111 |
112 | BY_NAME
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Jem
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 | # LiquidSwipe
2 | Android LiquidSwipe Library
3 |
4 | [](https://opensource.org/licenses/MIT) [](https://jitpack.io/#Chrisvin/LiquidSwipe) [](https://android-arsenal.com/api?level=21)
5 |
6 | |
|
|
7 | | :-: | :-: |
8 | | [Default](#usage) | [Touch Interactive](#touch-interactive---making-the-liquidswipe-wave-center-y-value-match-the-touch-y-value) |
9 |
10 | LiquidSwipe is a viewpager library that can be used to make awesome onboarding designs. ([Default Demo apk](https://github.com/Chrisvin/LiquidSwipe/releases/download/1.1/LiquidSwipeDemo.-.Default.apk)) ([TouchInteractive Demo apk](https://github.com/Chrisvin/LiquidSwipe/releases/download/1.1/LiquidSwipeDemo.-.Touch_Interactive.apk))
11 |
12 | If you like this, you'll like [ConcentricOnboarding](https://github.com/Chrisvin/ConcentricOnboarding) as well.
13 |
14 | ## Demo app
15 | To run the demo project, clone the repository and run it via Android Studio.
16 | (OR)
17 | Download the latest demo apk from [releases](https://github.com/Chrisvin/LiquidSwipe/releases).
18 |
19 | ## Usage
20 | #### Set up the dependency
21 | 1. Add the JitPack repository to your root build.gradle at the end of repositories:
22 | ```
23 | allprojects {
24 | repositories {
25 | ...
26 | maven { url 'https://jitpack.io' }
27 | }
28 | }
29 | ```
30 | 2. Add the LiquidSwipe dependency in the build.gradle:
31 | ```
32 | implementation 'com.github.Chrisvin:LiquidSwipe:1.3'
33 | ```
34 |
35 | #### Use `LiquidSwipeViewPager` instead of the normal `ViewPager`
36 | ```
37 |
41 |
42 |
46 |
47 |
48 | ```
49 |
50 | #### Use a `LiquidSwipeLayout` as the base container in the fragment layouts
51 | ```
52 |
53 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | ```
67 | Note : [Dokka generated documentation on LiquidSwipeLayouts](https://chrisvin.github.io/LiquidSwipe/com.jem.liquidswipe.layout/)
68 |
69 | ### And you're done, easy-peasy. ^_^
70 |
71 | ## Touch Interactive - Making the LiquidSwipe wave center Y value match the touch Y value
72 |
73 | Rather than having the wave center Y value always be layout.height/2 , it would be more aesthetically pleasing for it to be the same as the touch Y value.
74 | The following code can be used to dynamically change the `waveCenterY` based on the touch position on the `LiquidSwipeViewPager`.
75 | (The reason this isn't done internally in the library is because the viewpager layouts don't get the touch events when said touch events are consumed directly by the viewpager)
76 |
77 | 1. In the `Activity`/`Fragment` class containing the `LiquidSwipeViewPager`
78 | ```kotlin
79 | // Create an array of LiquidSwipeCPP, one for each layout in the PagerAdapter
80 | val liquidSwipeClipPathProviders = Array(titleArray.count()) {
81 | LiquidSwipeClipPathProvider()
82 | }
83 |
84 | // Pass the LiquidSwipeCPP array to the adapter
85 | viewpager.adapter = CustomPagerAdapter(this, liquidSwipeClipPathProviders)
86 | // Similar logic can also be applied for your custom FragmentPagerAdapter/FragmentStatePagerAdapter
87 |
88 | // Listen to onTouch events on the viewpager and update the waveCenterY value of the LiquidSwipeCPPs
89 | viewpager.setOnTouchListener { _, event ->
90 | val waveCenterY = event.y
91 | liquidSwipeClipPathProviders.map {
92 | it.waveCenterY = waveCenterY
93 | }
94 | false
95 | }
96 | ```
97 | 2. In the `PagerAdapter`
98 | ```kotlin
99 | // Set the layout's clipPathProvider to the corresponding `LiquidSwipeClipPathProvider`
100 | (layout as? LiquidSwipeLayout)?.clipPathProvider = liquidSwipeClipPathProviders[position]
101 | ```
102 |
103 | The above code has been showcased in the demo app, feel free to look at it for reference.
104 |
105 | **Note**:
106 | This is not a perfect solution, in fact some artifacts might occur due to the quick waveCenterY value jumps.
107 | But for now, this is the cleanest solution I can think of.
108 | Anyone else with a better solution is welcome to fork and submit a pull request. :)
109 |
110 |
111 |
112 | ## Creating custom swipe animations
113 |
114 | The concept for the `ClipPathProvider` in LiquidSwipe is the same as that in the [EasyReveal library](https://github.com/Chrisvin/EasyReveal) (If you haven't already, then you should really check it out, infact the first version of LiquidSwipe used [EasyReveal](https://github.com/Chrisvin/EasyReveal) as a dependency).
115 |
116 | You can create your own swipe animation by extending the [ClipPathProvider](https://github.com/Chrisvin/LiquidSwipe/blob/master/liquidswipe/src/main/java/com/jem/liquidswipe/base/ClipPathProvider.kt) and implementing the `getPath()` method. `getPath()` provides the [Path](https://developer.android.com/reference/android/graphics/Path) for a given *percent* value on the provided *view*. The path gotten from `getPath()` is then used to clip the view using `canvas.clipPath(path, op)` (The `op` value is provided by the `ClipPathProvider` as well). You can then set your custom [ClipPathProvider](https://github.com/Chrisvin/LiquidSwipe/blob/master/liquidswipe/src/main/java/com/jem/liquidswipe/base/ClipPathProvider.kt) to your layouts.
117 |
118 | ## API Documentation
119 |
120 | Documentation generated using Dokka : [chrisvin.github.io/LiquidSwipe](https://chrisvin.github.io/LiquidSwipe/)
121 |
122 | ## Bugs and Feedback
123 | For bugs, questions and discussions please use the [Github Issues](https://github.com/Chrisvin/LiquidSwipe/issues).
124 |
125 | ## Credits
126 | 1. [Cuberto's liquid-swipe for iOS](https://github.com/Cuberto/liquid-swipe) - Source of inspiration
127 | 2. [Alvaro Fabre](https://lottiefiles.com/tomfabre) - Designer of the lottie animations in the demo app
128 |
129 | ## License
130 | ```
131 | MIT License
132 |
133 | Copyright (c) 2019 Jem
134 |
135 | Permission is hereby granted, free of charge, to any person obtaining a copy
136 | of this software and associated documentation files (the "Software"), to deal
137 | in the Software without restriction, including without limitation the rights
138 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
139 | copies of the Software, and to permit persons to whom the Software is
140 | furnished to do so, subject to the following conditions:
141 |
142 | The above copyright notice and this permission notice shall be included in all
143 | copies or substantial portions of the Software.
144 |
145 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
146 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
147 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
148 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
149 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
150 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
151 | SOFTWARE.
152 | ```
153 |
--------------------------------------------------------------------------------
/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.2"
8 |
9 | defaultConfig {
10 | applicationId "com.example.liquidswipedemo"
11 | minSdkVersion 21
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.1.0'
33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
34 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
35 | testImplementation 'junit:junit:4.12'
36 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
38 |
39 | implementation 'com.airbnb.android:lottie:3.2.0'
40 |
41 | implementation project(':liquidswipe')
42 | }
43 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/liquidswipedemo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
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("com.example.liquidswipedemo", 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/com/example/liquidswipedemo/CustomFragmentPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 |
7 | class CustomFragmentPagerAdapter(fm: FragmentManager) :
8 | FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
9 |
10 | override fun getItem(position: Int): Fragment {
11 | return DummyFragment.newInstance(
12 | backgroundColorArray[(position % titleArray.count())],
13 | resourceArray[(position % titleArray.count())],
14 | titleArray[(position % titleArray.count())]
15 | )
16 | }
17 |
18 | override fun getCount(): Int {
19 | return titleArray.count() * 20
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/liquidswipedemo/CustomPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.TextView
8 | import androidx.viewpager.widget.PagerAdapter
9 | import com.airbnb.lottie.LottieAnimationView
10 | import com.airbnb.lottie.LottieDrawable
11 | import com.jem.liquidswipe.clippathprovider.LiquidSwipeClipPathProvider
12 | import com.jem.liquidswipe.base.LiquidSwipeLayout
13 |
14 | class CustomPagerAdapter(
15 | private val context: Context,
16 | private val liquidSwipeClipPathProviders: Array
17 | ) : PagerAdapter() {
18 |
19 | override fun instantiateItem(container: ViewGroup, position: Int): Any {
20 | val layout = LayoutInflater.from(context).inflate(R.layout.fragment_dummy, container, false)
21 |
22 | layout.setBackgroundColor(backgroundColorArray[(position % titleArray.count())])
23 |
24 | layout.findViewById(R.id.lottieAnimationView).setAnimation(
25 | resourceArray[(position % titleArray.count())]
26 | )
27 | layout.findViewById(R.id.lottieAnimationView).repeatCount =
28 | LottieDrawable.INFINITE
29 | layout.findViewById(R.id.lottieAnimationView).repeatMode =
30 | LottieDrawable.REVERSE
31 | layout.findViewById(R.id.lottieAnimationView).playAnimation()
32 |
33 | layout.findViewById(R.id.fragment_textview).text =
34 | titleArray[(position % titleArray.count())]
35 |
36 | (layout as? LiquidSwipeLayout)?.clipPathProvider =
37 | liquidSwipeClipPathProviders[(position % titleArray.count())]
38 |
39 | container.addView(layout)
40 | return layout
41 | }
42 |
43 | override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
44 | container.removeView(`object` as View)
45 | }
46 |
47 | override fun isViewFromObject(view: View, `object`: Any): Boolean {
48 | return `object` == view
49 | }
50 |
51 | override fun getCount(): Int {
52 | return titleArray.count() * 20
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/liquidswipedemo/DataValues.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
2 |
3 | import android.graphics.Color
4 |
5 | val backgroundColorArray: ArrayList = arrayListOf(
6 | Color.parseColor("#6200EE"),
7 | Color.parseColor("#9FD29D"),
8 | Color.parseColor("#AFE1F0"),
9 | Color.parseColor("#F6D336"),
10 | Color.parseColor("#FA796B")
11 | )
12 | val resourceArray: ArrayList = arrayListOf(
13 | R.raw.mountain,
14 | R.raw.map,
15 | R.raw.luggage,
16 | R.raw.camera,
17 | R.raw.van
18 | )
19 | val titleArray: ArrayList = arrayListOf(
20 | "Hello fellow developer",
21 | "If you like this library",
22 | "Then do star it",
23 | "And check out my other libraries",
24 | "Cheers ^_^"
25 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/liquidswipedemo/DummyFragment.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.TextView
9 | import androidx.fragment.app.Fragment
10 | import com.airbnb.lottie.LottieAnimationView
11 | import com.airbnb.lottie.LottieDrawable
12 |
13 | private const val ARG_BACKGROUND_COLOR = "param1"
14 | private const val ARG_RESOURCE = "param2"
15 | private const val ARG_TITLE = "param3"
16 |
17 | class DummyFragment : Fragment() {
18 | private var param1: Int? = null
19 | private var param2: Int? = null
20 | private var param3: String? = null
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | arguments?.let {
25 | param1 = it.getInt(ARG_BACKGROUND_COLOR)
26 | param2 = it.getInt(ARG_RESOURCE)
27 | param3 = it.getString(ARG_TITLE)
28 | }
29 | }
30 |
31 | override fun onCreateView(
32 | inflater: LayoutInflater, container: ViewGroup?,
33 | savedInstanceState: Bundle?
34 | ): View? {
35 | // Inflate the layout for this fragment
36 | return inflater.inflate(R.layout.fragment_dummy, container, false).apply {
37 | setBackgroundColor(param1 ?: Color.RED)
38 |
39 | findViewById(R.id.lottieAnimationView).setAnimation(
40 | param2 ?: R.raw.mountain
41 | )
42 | findViewById(R.id.lottieAnimationView).repeatCount =
43 | LottieDrawable.INFINITE
44 | findViewById(R.id.lottieAnimationView).repeatMode =
45 | LottieDrawable.REVERSE
46 | findViewById(R.id.lottieAnimationView).playAnimation()
47 |
48 | findViewById(R.id.fragment_textview).text =
49 | param3 ?: "Hello fellow developer!"
50 | }
51 | }
52 |
53 | companion object {
54 | @JvmStatic
55 | fun newInstance(param1: Int, param2: Int, param3: String) =
56 | DummyFragment().apply {
57 | arguments = Bundle().apply {
58 | putInt(ARG_BACKGROUND_COLOR, param1)
59 | putInt(ARG_RESOURCE, param2)
60 | putString(ARG_TITLE, param3)
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/liquidswipedemo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 | import com.jem.liquidswipe.clippathprovider.LiquidSwipeClipPathProvider
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 |
14 | viewpager.adapter = CustomFragmentPagerAdapter(supportFragmentManager)
15 |
16 | /*
17 | // The following code can be used to dynamically change the waveCenterY
18 | // based on the touch position (on the LiquidSwipeViewPager).
19 |
20 | // The reason this isn't done internally is because,
21 | // sometimes the viewpager layouts don't get the touch events
22 | // when said touch events are consumed directly by the viewpager.
23 |
24 | // Create an array of LiquidSwipeCPP, one for each layout in the PagerAdapter
25 | val liquidSwipeClipPathProviders = Array(titleArray.count()) {
26 | LiquidSwipeClipPathProvider()
27 | }
28 |
29 | // Similar logic can also be applied for your custom FragmentPagerAdapter/FragmentStatePagerAdapter
30 | viewpager.adapter = CustomPagerAdapter(this, liquidSwipeClipPathProviders)
31 |
32 | // Listen to onTouch events on the viewpager and update the waveCenterY value of the LiquidSwipeCPPs
33 | viewpager.setOnTouchListener { _, event ->
34 | val waveCenterY = event.y
35 | liquidSwipeClipPathProviders.map {
36 | it.waveCenterY = waveCenterY
37 | }
38 | false
39 | }
40 | */
41 |
42 | // Note that this is not a perfect solution,
43 | // in fact some artifacts might occur due to the quick waveCenterY value jumps.
44 | // But for now, this is the cleanest solution I can think of.
45 | // Anyone else with a better solution is welcome to fork and submit a pull request. :)
46 |
47 | // Create 20 times the number of actual pages, and start in the middle.
48 | // This way users can swipe left or right from the start.
49 | // Definitely not a good idea for production, but good enough for a demo app.
50 | viewpager.setCurrentItem(titleArray.count() * 10, false)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_dummy.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
19 |
31 |
32 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LiquidSwipeDemo
3 |
4 |
5 | Hello blank fragment
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/liquidswipedemo/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.liquidswipedemo
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.50'
5 | repositories {
6 | google()
7 | jcenter()
8 | maven { url 'https://jitpack.io' }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.5.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | maven { url 'https://jitpack.io' }
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/demo screenshots/LiquidSwipeDemo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/demo screenshots/LiquidSwipeDemo.gif
--------------------------------------------------------------------------------
/demo screenshots/LiquidSwipeDemo_Touch_Interactive.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/demo screenshots/LiquidSwipeDemo_Touch_Interactive.gif
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Nov 16 16:42:20 IST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/liquidswipe/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | /* Dokka documentation plugin repositories & dependencies*/
6 | buildscript {
7 | repositories {
8 | jcenter()
9 | }
10 | dependencies {
11 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.18"
12 | }
13 | }
14 |
15 | repositories {
16 | maven { url 'https://dl.bintray.com/kotlin/dokka' }
17 | }
18 |
19 | apply plugin: 'org.jetbrains.dokka'
20 |
21 | android {
22 | compileSdkVersion 29
23 | buildToolsVersion "29.0.2"
24 |
25 | defaultConfig {
26 | minSdkVersion 21
27 | targetSdkVersion 29
28 | versionCode 1
29 | versionName "1.0"
30 |
31 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
32 | consumerProguardFiles 'consumer-rules.pro'
33 | }
34 |
35 | buildTypes {
36 | release {
37 | minifyEnabled false
38 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
39 | }
40 | }
41 |
42 | dokka {
43 | outputFormat = 'gfm'
44 | outputDirectory = "$buildDir/javadoc"
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation fileTree(dir: 'libs', include: ['*.jar'])
50 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
51 | implementation 'androidx.appcompat:appcompat:1.1.0'
52 | implementation 'androidx.core:core-ktx:1.1.0'
53 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
54 | testImplementation 'junit:junit:4.12'
55 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
57 | }
58 |
--------------------------------------------------------------------------------
/liquidswipe/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Chrisvin/LiquidSwipe/ae50e891c2803fa864ef3f2a48cafc43ee0a3b8e/liquidswipe/consumer-rules.pro
--------------------------------------------------------------------------------
/liquidswipe/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 |
--------------------------------------------------------------------------------
/liquidswipe/src/androidTest/java/com/jem/liquidswipe/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe
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("com.jem.liquidswipe.test", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/liquidswipe/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/LiquidSwipeViewPager.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.view.animation.DecelerateInterpolator
7 | import androidx.viewpager.widget.ViewPager
8 | import com.jem.liquidswipe.base.LiquidSwipeLayout
9 | import com.jem.liquidswipe.util.FixedSpeedScroller
10 | import kotlin.math.abs
11 |
12 | /**
13 | * `LiquidSwipeViewPager` is a custom [ViewPager] that uses a fixed scroller and uses a [LiquidSwipePageTransformer] as it's page transformer.
14 | *
15 | * Note: Setting another page transformer to [LiquidSwipeViewPager] would remove the [LiquidSwipePageTransformer].
16 | */
17 | class LiquidSwipeViewPager : ViewPager {
18 | constructor(context: Context) : super(context) {
19 | initialize(context, null)
20 | }
21 |
22 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
23 | initialize(context, attrs)
24 | }
25 |
26 | private fun initialize(context: Context, attrs: AttributeSet?) {
27 | setPageTransformer(true, LiquidSwipePageTransformer())
28 |
29 | var scrollerDuration = DEFAULT_SCROLLER_DURATION
30 | attrs?.let {
31 | val typedArray =
32 | context.obtainStyledAttributes(it, R.styleable.LiquidSwipeViewPager, 0, 0)
33 | typedArray.apply {
34 | scrollerDuration = getInt(
35 | R.styleable.LiquidSwipeViewPager_scrollerDuration,
36 | DEFAULT_SCROLLER_DURATION
37 | )
38 | }
39 | }
40 | setDuration(scrollerDuration)
41 | }
42 |
43 | /**
44 | * Sets the fixed scroller duration for the [LiquidSwipeViewPager].
45 | * @param duration Duration taken for viewpager to settle into position.
46 | */
47 | public fun setDuration(duration: Int) {
48 | val mScroller = ViewPager::class.java.getDeclaredField("mScroller")
49 | mScroller.isAccessible = true
50 | val scroller = FixedSpeedScroller(context, DecelerateInterpolator())
51 | scroller.scrollerDuration = duration
52 | mScroller.set(this, scroller)
53 | }
54 |
55 | companion object Constants {
56 | private const val DEFAULT_SCROLLER_DURATION = 1000
57 | }
58 |
59 | /**
60 | * `LiquidSwipePageTransformer` is a custom [ViewPager.PageTransformer] that is used for LiquidSwipe reveal.
61 | */
62 | class LiquidSwipePageTransformer : ViewPager.PageTransformer {
63 | override fun transformPage(page: View, position: Float) {
64 | if (page is LiquidSwipeLayout) {
65 | when {
66 | position < -1 -> {
67 | page.revealForPercentage(0f)
68 | }
69 | position < 0 -> {
70 | page.translationX = -(page.width * position)
71 | page.revealForPercentage(100 - abs(position * 100))
72 | }
73 | position <= 1 -> {
74 | page.revealForPercentage(100f)
75 | page.translationX = -(page.width * position)
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/base/ClipPathProvider.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.base
2 |
3 | import android.graphics.Path
4 | import android.graphics.Region
5 | import android.os.Build
6 | import android.view.View
7 |
8 | /** Abstract class that should be extended by other ClipPathProvider classes. */
9 | abstract class ClipPathProvider {
10 | /** Path variable that should be used to make the path to be returned in [getPath] function.*/
11 | protected var path: Path = Path()
12 | /**
13 | * Region.Op variable that is used in [android.graphics.Canvas.clipPath] in the EasyRevealLayouts
14 | * @throws IllegalArgumentException Will be thrown if values other than [Region.Op.INTERSECT] and
15 | * [Region.Op.DIFFERENCE] are provided when API level is greater than or equal to [Build.VERSION_CODES.P].
16 | */
17 | var op: Region.Op = Region.Op.INTERSECT
18 | set(value) {
19 | require(!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE)) { "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed" }
20 | field = value
21 | }
22 |
23 | /**
24 | * Generate the [Path] for the **percent** value to be applied in the **view**.
25 | * @param percent Reveal percentage
26 | * @param view View on which to apply clipPath for reveal
27 | * @return **path** that can be used to clip the view.
28 | */
29 | abstract fun getPath(percent: Float, view: View): Path
30 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/base/LiquidSwipeLayout.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.base
2 |
3 | /**
4 | * LiquidSwipeLayout is the base interface for all the other LiquidSwipeLayouts
5 | */
6 | interface LiquidSwipeLayout {
7 | /** ClipPathProvider provides the path used for clipping. */
8 | var clipPathProvider: ClipPathProvider
9 | /** Percentage of the view currently revealed. */
10 | var currentRevealPercent: Float
11 |
12 | /**
13 | * Update view to specified reveal percentage.
14 | * @param percent value should be between 0 and 100 (inclusive).
15 | */
16 | fun revealForPercentage(percent: Float): Unit
17 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/clippathprovider/LiquidSwipeClipPathProvider.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.clippathprovider
2 |
3 | import android.graphics.Path
4 | import android.view.View
5 | import com.jem.liquidswipe.base.ClipPathProvider
6 | import kotlin.math.cos
7 | import kotlin.math.exp
8 | import kotlin.math.pow
9 |
10 | /**
11 | * [ClipPathProvider] which provides LiquidSwipe path.
12 | */
13 | class LiquidSwipeClipPathProvider : ClipPathProvider() {
14 | /** Center of the wave */
15 | var waveCenterY: Float = 0f
16 | /** Initial horizontal radius of the wave */
17 | var initialHorizontalRadius = 0f
18 | /** Initial vertical radius of the wave */
19 | var initialVerticalRadius = 82f
20 | /** Initial revealed side width */
21 | var initialSideWidth = 0f
22 |
23 | private var waveHorizontalRadius: Float = 0f
24 | private var waveVerticalRadius: Float = 0f
25 | private var sideWidth: Float = 0f
26 |
27 | companion object Constants {
28 | private fun getMaxHorizontalRadius(view: View): Float {
29 | return view.width.toFloat() * 0.8f
30 | }
31 |
32 | private fun getMaxVerticalRadius(view: View): Float {
33 | return view.height.toFloat() * 0.9f
34 | }
35 |
36 | private fun getInitialWaveCenterY(view: View): Float {
37 | return view.height.toFloat() * 0.7167487685f
38 | }
39 | }
40 |
41 | override fun getPath(percent: Float, view: View): Path {
42 | waveCenterY = if (waveCenterY == 0f) view.height.toFloat() / 2 else waveCenterY
43 | waveHorizontalRadius = getWaveHorRadius(1 - (percent / 100), view)
44 | waveVerticalRadius = getWaveVertRadius(1 - (percent / 100), view)
45 | sideWidth = getSideWidth(1 - (percent / 100), view)
46 |
47 | path.reset()
48 | val maskWidth = view.width - sideWidth
49 | path.moveTo(maskWidth - sideWidth, 0f)
50 | path.lineTo(0f, 0f)
51 | path.lineTo(0f, view.height.toFloat())
52 | path.lineTo(maskWidth, view.height.toFloat())
53 |
54 | if (percent == 100f) {
55 | path.close()
56 | return path
57 | }
58 |
59 | val curveStartY = waveCenterY + waveVerticalRadius
60 | path.lineTo(maskWidth, curveStartY)
61 |
62 | path.cubicTo(
63 | maskWidth,
64 | (curveStartY - waveVerticalRadius * 0.1346194756).toFloat(),
65 | (maskWidth - waveHorizontalRadius * 0.05341339583).toFloat(),
66 | (curveStartY - waveVerticalRadius * 0.2412779634).toFloat(),
67 | (maskWidth - waveHorizontalRadius * 0.1561501458).toFloat(),
68 | (curveStartY - waveVerticalRadius * 0.3322374268).toFloat()
69 | )
70 | path.cubicTo(
71 | (maskWidth - waveHorizontalRadius * 0.2361659167).toFloat(),
72 | (curveStartY - waveVerticalRadius * 0.4030805244).toFloat(),
73 | (maskWidth - waveHorizontalRadius * 0.3305285625).toFloat(),
74 | (curveStartY - waveVerticalRadius * 0.4561193293).toFloat(),
75 | (maskWidth - waveHorizontalRadius * 0.5012484792).toFloat(),
76 | (curveStartY - waveVerticalRadius * 0.5350576951).toFloat()
77 | )
78 | path.cubicTo(
79 | (maskWidth - waveHorizontalRadius * 0.515878125).toFloat(),
80 | (curveStartY - waveVerticalRadius * 0.5418222317).toFloat(),
81 | (maskWidth - waveHorizontalRadius * 0.5664134792).toFloat(),
82 | (curveStartY - waveVerticalRadius * 0.5650349878).toFloat(),
83 | (maskWidth - waveHorizontalRadius * 0.574934875).toFloat(),
84 | (curveStartY - waveVerticalRadius * 0.5689655122).toFloat()
85 | )
86 | path.cubicTo(
87 | (maskWidth - waveHorizontalRadius * 0.7283715208).toFloat(),
88 | (curveStartY - waveVerticalRadius * 0.6397387195).toFloat(),
89 | (maskWidth - waveHorizontalRadius * 0.8086618958).toFloat(),
90 | (curveStartY - waveVerticalRadius * 0.6833456585).toFloat(),
91 | (maskWidth - waveHorizontalRadius * 0.8774032292).toFloat(),
92 | (curveStartY - waveVerticalRadius * 0.7399037439).toFloat()
93 | )
94 | path.cubicTo(
95 | (maskWidth - waveHorizontalRadius * 0.9653464583).toFloat(),
96 | (curveStartY - waveVerticalRadius * 0.8122605122).toFloat(),
97 | maskWidth - waveHorizontalRadius,
98 | (curveStartY - waveVerticalRadius * 0.8936183659).toFloat(),
99 | maskWidth - waveHorizontalRadius, curveStartY - waveVerticalRadius
100 | )
101 | path.cubicTo(
102 | maskWidth - waveHorizontalRadius,
103 | (curveStartY - waveVerticalRadius * 1.100142878).toFloat(),
104 | (maskWidth - waveHorizontalRadius * 0.9595746667).toFloat(),
105 | (curveStartY - waveVerticalRadius * 1.1887991951).toFloat(),
106 | (maskWidth - waveHorizontalRadius * 0.8608411667).toFloat(),
107 | (curveStartY - waveVerticalRadius * 1.270484439).toFloat()
108 | )
109 | path.cubicTo(
110 | (maskWidth - waveHorizontalRadius * 0.7852123333).toFloat(),
111 | (curveStartY - waveVerticalRadius * 1.3330544756).toFloat(),
112 | (maskWidth - waveHorizontalRadius * 0.703382125).toFloat(),
113 | (curveStartY - waveVerticalRadius * 1.3795848049).toFloat(),
114 | (maskWidth - waveHorizontalRadius * 0.5291125625).toFloat(),
115 | (curveStartY - waveVerticalRadius * 1.4665102805).toFloat()
116 | )
117 | path.cubicTo(
118 | (maskWidth - waveHorizontalRadius * 0.5241858333).toFloat(),
119 | (curveStartY - waveVerticalRadius * 1.4689677195).toFloat(),
120 | (maskWidth - waveHorizontalRadius * 0.505739125).toFloat(),
121 | (curveStartY - waveVerticalRadius * 1.4781625854).toFloat(),
122 | (maskWidth - waveHorizontalRadius * 0.5015305417).toFloat(),
123 | (curveStartY - waveVerticalRadius * 1.4802616098).toFloat()
124 | )
125 | path.cubicTo(
126 | (maskWidth - waveHorizontalRadius * 0.3187486042).toFloat(),
127 | (curveStartY - waveVerticalRadius * 1.5714239024).toFloat(),
128 | (maskWidth - waveHorizontalRadius * 0.2332057083).toFloat(),
129 | (curveStartY - waveVerticalRadius * 1.6204116463).toFloat(),
130 | (maskWidth - waveHorizontalRadius * 0.1541165417).toFloat(),
131 | (curveStartY - waveVerticalRadius * 1.687403).toFloat()
132 | )
133 | path.cubicTo(
134 | (maskWidth - waveHorizontalRadius * 0.0509933125).toFloat(),
135 | (curveStartY - waveVerticalRadius * 1.774752061).toFloat(),
136 | maskWidth, (curveStartY - waveVerticalRadius * 1.8709256829).toFloat(),
137 | maskWidth, curveStartY - waveVerticalRadius * 2
138 | )
139 |
140 | path.lineTo(maskWidth, 0f)
141 | path.close()
142 | return path
143 | }
144 |
145 | private fun getWaveHorRadius(percent: Float, view: View): Float {
146 | if (percent <= 0) {
147 | return initialHorizontalRadius
148 | }
149 | if (percent >= 1) {
150 | return 0f
151 | }
152 | val p1: Float = 0.4f
153 | if (percent <= p1) {
154 | return initialHorizontalRadius + percent / p1 * (getMaxHorizontalRadius(view) - initialHorizontalRadius)
155 | }
156 | val t: Float = ((percent - p1) / (1.0 - p1)).toFloat()
157 | val A: Float = getMaxHorizontalRadius(view)
158 | val r: Float = 40f
159 | val m: Float = 9.8f
160 | val beta: Float = r / (2 * m)
161 | val k: Float = 50f
162 | val omega0: Float = k / m
163 | val omega: Float = (-beta.pow(2) + omega0.pow(2)).pow(0.5f)
164 |
165 | return A * exp(-beta * t) * cos(omega * t)
166 | }
167 |
168 | private fun getWaveHorRadiusBack(percent: Float, view: View): Float {
169 | if (percent <= 0) {
170 | return initialHorizontalRadius
171 | }
172 | if (percent >= 1) {
173 | return 0f
174 | }
175 | val p1: Float = 0.4f
176 | if (percent <= p1) {
177 | return initialHorizontalRadius + percent / p1 * initialHorizontalRadius
178 | }
179 | val t: Float = ((percent - p1) / (1.0 - p1)).toFloat()
180 | val A: Float = 2 * initialHorizontalRadius
181 | val r: Float = 40f
182 | val m: Float = 9.8f
183 | val beta: Float = r / (2 * m)
184 | val k: Float = 50f
185 | val omega0: Float = k / m
186 | val omega: Float = (-beta.pow(2) + omega0.pow(2)).pow(0.5f)
187 |
188 | return A * exp(-beta * t) * cos(omega * t)
189 | }
190 |
191 | private fun getWaveVertRadius(percent: Float, view: View): Float {
192 | val p1: Float = 0.4f
193 | if (percent <= 0) {
194 | return initialVerticalRadius
195 | }
196 | if (percent >= p1) {
197 | return getMaxVerticalRadius(view)
198 | }
199 | return initialVerticalRadius + (getMaxVerticalRadius(view) - initialVerticalRadius) * percent / p1
200 | }
201 |
202 | private fun getSideWidth(percent: Float, view: View): Float {
203 | val p1: Float = 0.2f
204 | val p2: Float = 0.8f
205 | if (percent <= p1) {
206 | return initialSideWidth
207 | }
208 | if (percent >= p2) {
209 | return view.width.toFloat()
210 | }
211 | return initialSideWidth + (view.width - initialSideWidth) * ((percent - p1) / (p2 - p1))
212 | }
213 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/layout/LiquidSwipeConstraintLayout.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.layout
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Path
6 | import android.util.AttributeSet
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.jem.liquidswipe.base.ClipPathProvider
9 | import com.jem.liquidswipe.clippathprovider.LiquidSwipeClipPathProvider
10 | import com.jem.liquidswipe.base.LiquidSwipeLayout
11 |
12 | /**
13 | * `LiquidSwipeConstraintLayout` is a custom [ConstraintLayout] that implements [LiquidSwipeLayout].
14 | */
15 | open class LiquidSwipeConstraintLayout : ConstraintLayout, LiquidSwipeLayout {
16 | // Store path in local variable rather then getting it from ClipPathProvider each time
17 | private var path: Path? = null
18 |
19 | // Backing fields for LiquidSwipeLayout variables
20 | private var _clipPathProvider: ClipPathProvider = LiquidSwipeClipPathProvider()
21 | private var _currentRevealPercent: Float = 100f
22 |
23 | override var clipPathProvider = _clipPathProvider
24 | override var currentRevealPercent: Float
25 | get() = _currentRevealPercent
26 | set(value) {
27 | revealForPercentage(value)
28 | }
29 |
30 | constructor(context: Context?) : super(context)
31 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
32 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
33 | context,
34 | attrs,
35 | defStyleAttr
36 | )
37 |
38 | /**
39 | * Overriden from View
40 | */
41 | override fun draw(canvas: Canvas?) {
42 | try {
43 | canvas?.save()
44 | path?.let {
45 | canvas?.clipPath(it, clipPathProvider.op)
46 | }
47 | super.draw(canvas)
48 | } finally {
49 | canvas?.restore()
50 | }
51 | }
52 |
53 | override fun revealForPercentage(percent: Float) {
54 | if (percent == _currentRevealPercent) return
55 | _currentRevealPercent = percent
56 | path = clipPathProvider.getPath(percent, this@LiquidSwipeConstraintLayout)
57 | invalidate()
58 | }
59 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/layout/LiquidSwipeFrameLayout.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.layout
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Path
6 | import android.os.Build
7 | import android.util.AttributeSet
8 | import android.widget.FrameLayout
9 | import androidx.annotation.RequiresApi
10 | import com.jem.liquidswipe.base.ClipPathProvider
11 | import com.jem.liquidswipe.clippathprovider.LiquidSwipeClipPathProvider
12 | import com.jem.liquidswipe.base.LiquidSwipeLayout
13 |
14 | /**
15 | * `LiquidSwipeFrameLayout` is a custom [FrameLayout] that implements [LiquidSwipeLayout].
16 | */
17 | open class LiquidSwipeFrameLayout : FrameLayout, LiquidSwipeLayout {
18 | // Store path in local variable rather then getting it from ClipPathProvider each time
19 | private var path: Path? = null
20 |
21 | // Backing fields for LiquidSwipeLayout variables
22 | private var _clipPathProvider: ClipPathProvider = LiquidSwipeClipPathProvider()
23 | private var _currentRevealPercent: Float = 100f
24 |
25 | override var clipPathProvider = _clipPathProvider
26 | override var currentRevealPercent: Float
27 | get() = _currentRevealPercent
28 | set(value) {
29 | revealForPercentage(value)
30 | }
31 |
32 | constructor(context: Context) : super(context)
33 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
34 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
35 | context,
36 | attrs,
37 | defStyleAttr
38 | )
39 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
40 | constructor(
41 | context: Context,
42 | attrs: AttributeSet?,
43 | defStyleAttr: Int,
44 | defStyleRes: Int
45 | ) : super(context, attrs, defStyleAttr, defStyleRes)
46 |
47 | /**
48 | * Overriden from View
49 | */
50 | override fun draw(canvas: Canvas?) {
51 | try {
52 | canvas?.save()
53 | path?.let {
54 | canvas?.clipPath(it, clipPathProvider.op)
55 | }
56 | super.draw(canvas)
57 | } finally {
58 | canvas?.restore()
59 | }
60 | }
61 |
62 | override fun revealForPercentage(percent: Float) {
63 | if (percent == _currentRevealPercent) return
64 | _currentRevealPercent = percent
65 | path = clipPathProvider.getPath(percent, this@LiquidSwipeFrameLayout)
66 | invalidate()
67 | }
68 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/layout/LiquidSwipeLinearLayout.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.layout
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Path
6 | import android.os.Build
7 | import android.util.AttributeSet
8 | import android.widget.LinearLayout
9 | import androidx.annotation.RequiresApi
10 | import com.jem.liquidswipe.base.ClipPathProvider
11 | import com.jem.liquidswipe.clippathprovider.LiquidSwipeClipPathProvider
12 | import com.jem.liquidswipe.base.LiquidSwipeLayout
13 |
14 | /**
15 | * `LiquidSwipeLinearLayout` is a custom [LinearLayout] that implements [LiquidSwipeLayout].
16 | */
17 | open class LiquidSwipeLinearLayout : LinearLayout, LiquidSwipeLayout {
18 | // Store path in local variable rather then getting it from ClipPathProvider each time
19 | private var path: Path? = null
20 |
21 | // Backing fields for LiquidSwipeLayout variables
22 | private var _clipPathProvider: ClipPathProvider = LiquidSwipeClipPathProvider()
23 | private var _currentRevealPercent: Float = 100f
24 |
25 | override var clipPathProvider = _clipPathProvider
26 | override var currentRevealPercent: Float
27 | get() = _currentRevealPercent
28 | set(value) {
29 | revealForPercentage(value)
30 | }
31 |
32 | constructor(context: Context?) : super(context)
33 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
34 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
35 | context,
36 | attrs,
37 | defStyleAttr
38 | )
39 |
40 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
41 | constructor(
42 | context: Context?,
43 | attrs: AttributeSet?,
44 | defStyleAttr: Int,
45 | defStyleRes: Int
46 | ) : super(context, attrs, defStyleAttr, defStyleRes)
47 |
48 | /**
49 | * Overriden from View
50 | */
51 | override fun draw(canvas: Canvas?) {
52 | try {
53 | canvas?.save()
54 | path?.let {
55 | canvas?.clipPath(it, clipPathProvider.op)
56 | }
57 | super.draw(canvas)
58 | } finally {
59 | canvas?.restore()
60 | }
61 | }
62 |
63 | override fun revealForPercentage(percent: Float) {
64 | if (percent == _currentRevealPercent) return
65 | _currentRevealPercent = percent
66 | path = clipPathProvider.getPath(percent, this@LiquidSwipeLinearLayout)
67 | invalidate()
68 | }
69 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/java/com/jem/liquidswipe/util/FixedSpeedScroller.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe.util
2 |
3 | import android.content.Context
4 | import android.view.animation.Interpolator
5 | import android.widget.Scroller
6 |
7 | internal class FixedSpeedScroller : Scroller {
8 |
9 | var scrollerDuration = 1000
10 |
11 | constructor(context: Context?, interpolator: Interpolator?) : super(context, interpolator)
12 | constructor(context: Context?, interpolator: Interpolator?, duration: Int) : this(
13 | context,
14 | interpolator
15 | ) {
16 | this.scrollerDuration = duration
17 | }
18 |
19 | constructor(context: Context?, interpolator: Interpolator?, flywheel: Boolean) : super(
20 | context,
21 | interpolator,
22 | flywheel
23 | )
24 |
25 | override fun startScroll(
26 | startX: Int,
27 | startY: Int,
28 | dx: Int,
29 | dy: Int,
30 | duration: Int
31 | ) { // Ignore received duration, use fixed one instead
32 | super.startScroll(startX, startY, dx, dy, this.scrollerDuration)
33 | }
34 |
35 | override fun startScroll(
36 | startX: Int,
37 | startY: Int,
38 | dx: Int,
39 | dy: Int
40 | ) { // Ignore received duration, use fixed one instead
41 | super.startScroll(startX, startY, dx, dy, scrollerDuration)
42 | }
43 | }
--------------------------------------------------------------------------------
/liquidswipe/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/liquidswipe/src/test/java/com/jem/liquidswipe/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.jem.liquidswipe
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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='LiquidSwipeDemo'
2 | include ':app'
3 | include ':liquidswipe'
4 |
--------------------------------------------------------------------------------