├── .gitignore ├── README.md ├── assets ├── anim_color.gif ├── anim_drop.gif ├── anim_fill.gif ├── anim_none.gif ├── anim_scale.gif ├── anim_slide.gif ├── anim_swap.gif ├── anim_thin_worm.gif ├── anim_worm.gif ├── animation_worm.gif ├── attributes.gif └── preview_anim_drop.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pageindicatorview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── rd │ │ ├── IndicatorManager.java │ │ ├── PageIndicatorView.java │ │ ├── animation │ │ ├── AnimationManager.java │ │ ├── controller │ │ │ ├── AnimationController.java │ │ │ └── ValueController.java │ │ ├── data │ │ │ ├── AnimationValue.java │ │ │ ├── Value.java │ │ │ └── type │ │ │ │ ├── ColorAnimationValue.java │ │ │ │ ├── DropAnimationValue.java │ │ │ │ ├── FillAnimationValue.java │ │ │ │ ├── ScaleAnimationValue.java │ │ │ │ ├── SlideAnimationValue.java │ │ │ │ ├── SwapAnimationValue.java │ │ │ │ ├── ThinWormAnimationValue.java │ │ │ │ └── WormAnimationValue.java │ │ └── type │ │ │ ├── AnimationType.java │ │ │ ├── BaseAnimation.java │ │ │ ├── ColorAnimation.java │ │ │ ├── DropAnimation.java │ │ │ ├── FillAnimation.java │ │ │ ├── ScaleAnimation.java │ │ │ ├── ScaleDownAnimation.java │ │ │ ├── SlideAnimation.java │ │ │ ├── SwapAnimation.java │ │ │ ├── ThinWormAnimation.java │ │ │ └── WormAnimation.java │ │ ├── draw │ │ ├── DrawManager.java │ │ ├── controller │ │ │ ├── AttributeController.java │ │ │ ├── DrawController.java │ │ │ └── MeasureController.java │ │ ├── data │ │ │ ├── Indicator.java │ │ │ ├── Orientation.java │ │ │ ├── PositionSavedState.java │ │ │ └── RtlMode.java │ │ └── drawer │ │ │ ├── Drawer.java │ │ │ └── type │ │ │ ├── BaseDrawer.java │ │ │ ├── BasicDrawer.java │ │ │ ├── ColorDrawer.java │ │ │ ├── DropDrawer.java │ │ │ ├── FillDrawer.java │ │ │ ├── ScaleDownDrawer.java │ │ │ ├── ScaleDrawer.java │ │ │ ├── SlideDrawer.java │ │ │ ├── SwapDrawer.java │ │ │ ├── ThinWormDrawer.java │ │ │ └── WormDrawer.java │ │ └── utils │ │ ├── CoordinatesUtils.java │ │ ├── DensityUtils.java │ │ └── IdUtils.java │ └── res │ └── values │ └── attrs.xml ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── rd │ │ └── pageindicatorview │ │ ├── base │ │ └── BaseActivity.java │ │ ├── customize │ │ └── CustomizeActivity.java │ │ ├── data │ │ ├── Customization.java │ │ └── CustomizationConverter.java │ │ └── home │ │ ├── HomeActivity.java │ │ └── HomeAdapter.java │ └── res │ ├── drawable-v21 │ └── selector_black_10_transparent.xml │ ├── drawable │ └── selector_black_10_transparent.xml │ ├── layout │ ├── ac_customize.xml │ ├── ac_home.xml │ ├── item_spinner_drop_down.xml │ └── item_spinner_selected.xml │ ├── menu │ └── menu_customize.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | ### Android ### 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | ### Android Patch ### 33 | gen-external-apklibs 34 | 35 | 36 | ### Intellij ### 37 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 38 | 39 | *.iml 40 | 41 | ## Directory-based project format: 42 | .idea/ 43 | # if you remove the above rule, at least ignore the following: 44 | 45 | # User-specific stuff: 46 | # .idea/workspace.xml 47 | # .idea/tasks.xml 48 | # .idea/dictionaries 49 | 50 | # Sensitive or high-churn files: 51 | # .idea/dataSources.ids 52 | # .idea/dataSources.xml 53 | # .idea/sqlDataSources.xml 54 | # .idea/dynamic.xml 55 | # .idea/uiDesigner.xml 56 | 57 | # Gradle: 58 | # .idea/gradle.xml 59 | # .idea/libraries 60 | 61 | # Mongo Explorer plugin: 62 | # .idea/mongoSettings.xml 63 | 64 | ## File-based project format: 65 | *.ipr 66 | *.iws 67 | 68 | ## Plugin-specific files: 69 | 70 | # IntelliJ 71 | /out/ 72 | 73 | # mpeltonen/sbt-idea plugin 74 | .idea_modules/ 75 | 76 | # JIRA plugin 77 | atlassian-ide-plugin.xml 78 | 79 | # Crashlytics plugin (for Android Studio and IntelliJ) 80 | com_crashlytics_export_strings.xml 81 | crashlytics.properties 82 | crashlytics-build.properties 83 | *.asc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### **PageIndicatorView** 4 | [ ![Download](https://api.bintray.com/packages/romandanylyk/maven/pageindicatorview/images/download.svg) ](https://bintray.com/romandanylyk/maven/pageindicatorview/_latestVersion)[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-PageIndicatorView-green.svg?style=true)](https://android-arsenal.com/details/1/4555) 5 | [![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=14) 6 | 7 | 8 | `PageIndicatorView` is light library to indicate ViewPager's selected page with different animations and ability to customise it as you need. 9 | 10 | ![](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/preview_anim_drop.gif) 11 | 12 | ### **Integration** 13 | To add `pageindicatorview` to your project, first make sure in root `build.gradle` you have specified the following repository: 14 | ```groovy 15 | repositories { 16 | jcenter() 17 | } 18 | ``` 19 | >***Note***: by creating new project in Android Studio it will have `jcenter` repository specified by default, so you will not need to add it manually. 20 | 21 | Once you make sure you have `jcenter` repository in your project, all you need to do is to add the following line in `dependencies` section of your project `build.gradle`. 22 | 23 | See latest library version [ ![Download](https://api.bintray.com/packages/romandanylyk/maven/pageindicatorview/images/download.svg) ](https://bintray.com/romandanylyk/maven/pageindicatorview/_latestVersion) 24 | ```groovy 25 | implementation 'com.romandanylyk:pageindicatorview:X.X.X' 26 | ``` 27 | If your project already use `appcompat-v7` support library, you can omit `PageIndicatorView` dependencies by adding a single .aar file to your project, that will decrease total amount of methods used in your project. 28 | 29 | ```groovy 30 | implementation 'com.romandanylyk:pageindicatorview:X.X.X@aar' 31 | ``` 32 | 33 | Keep in mind, that `PageIndicatorView` has min [API level 14](https://developer.android.com/about/dashboards/index.html) and these dependencies: 34 | 35 | ```groovy 36 | implementation 'com.android.support:appcompat-v7:27.1.1' 37 | implementation 'com.android.support:recyclerview-v7:27.1.1' 38 | implementation 'com.android.support:support-core-ui:27.1.1' 39 | ``` 40 | 41 | ### **Usage Sample** 42 | Usage of `PageIndicatorView` is quite simple. All you need to do is to declare a view in your `layout.xml` and call `setSelection` method to select specific indicator - that's it! 43 | 44 | ```java 45 | PageIndicatorView pageIndicatorView = findViewById(R.id.pageIndicatorView); 46 | pageIndicatorView.setCount(5); // specify total count of indicators 47 | pageIndicatorView.setSelection(2); 48 | ``` 49 | 50 | 51 | But if you're as lazy as I'm - then there is another option to handle `PageIndicatorView` 52 | 53 | ```xml 54 | 67 | ``` 68 | All the `piv_` attributes here are specific for `PageIndicatorView` so you can customise it as you want with attributes - pretty handy. 69 | 70 | But what is more important here is `app:piv_viewPager="@id/viewPager"`. 71 | What it actually do is catch up your `ViewPager` and automatically handles all the event's to selected the right page - so you don't need to call `setSelection` method on your own. 72 | 73 | Another handy options here that works with your `ViewPager` as a whole is 74 | `app:piv_dynamicCount="true"` and ` app:piv_interactiveAnimation="true"` 75 | 76 | Dynamic count will automatically updates `PageIndicatorView` total count as you updates pages count in your `ViewPager` - so that's pretty useful. 77 | 78 | While interactive animation will progress the animation process within your swipe position, which makes animation more natural and responsive to end user. 79 | 80 | 81 | > ***Note***: Because `setViewPagerId` uses an instance of `ViewPager`, using it in recycler could lead to id conflicts, so `PageIndicatorView` will not know properly what is the right `ViewPager` to work with. Instead you should handle selected indicators on your own programatically. 82 | 83 | 84 | ```java 85 | pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 86 | @Override 87 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {/*empty*/} 88 | 89 | @Override 90 | public void onPageSelected(int position) { 91 | pageIndicatorView.setSelection(position); 92 | } 93 | 94 | @Override 95 | public void onPageScrollStateChanged(int state) {/*empty*/} 96 | }); 97 | ``` 98 | 99 | 100 | Here you can see all the animations `PageIndicatorView` support. 101 | 102 | Name| Support version| Preview 103 | -------- | --- | --- 104 | `AnimationType.NONE`| 0.0.1 | ![anim_none](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_none.gif) 105 | `AnimationType.COLOR`| 0.0.1 |![anim_color](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_color.gif) 106 | `AnimationType.SCALE`| 0.0.1 |![anim_scale](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_scale.gif) 107 | `AnimationType.SLIDE`| 0.0.1 |![anim_slide](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_slide.gif) 108 | `AnimationType.WORM`| 0.0.1 |![anim_worm](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_worm.gif) 109 | `AnimationType.FILL`| 0.0.6 |![anim_worm](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_fill.gif) 110 | `AnimationType.THIN_WORM`| 0.0.7 |![anim_thin_worm](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_thin_worm.gif) 111 | `AnimationType.DROP`| 0.1.0 |![anim_drop](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_drop.gif) 112 | `AnimationType.SWAP`| 0.1.1 |![anim_swap](https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/master/assets/anim_swap.gif) 113 | 114 | 115 | ### **Release Note** 116 | See release notes on [github releases](https://github.com/romandanylyk/PageIndicatorView/releases) or [Bintray release notes](https://bintray.com/romandanylyk/maven/pageindicatorview#release). 117 | 118 | ### **License** 119 | 120 | Copyright 2017 Roman Danylyk 121 | 122 | Licensed under the Apache License, Version 2.0 (the "License"); 123 | you may not use this file except in compliance with the License. 124 | You may obtain a copy of the License at 125 | 126 | http://www.apache.org/licenses/LICENSE-2.0 127 | 128 | Unless required by applicable law or agreed to in writing, software 129 | distributed under the License is distributed on an "AS IS" BASIS, 130 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131 | See the License for the specific language governing permissions and 132 | limitations under the License. 133 | 134 | -------------------------------------------------------------------------------- /assets/anim_color.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_color.gif -------------------------------------------------------------------------------- /assets/anim_drop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_drop.gif -------------------------------------------------------------------------------- /assets/anim_fill.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_fill.gif -------------------------------------------------------------------------------- /assets/anim_none.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_none.gif -------------------------------------------------------------------------------- /assets/anim_scale.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_scale.gif -------------------------------------------------------------------------------- /assets/anim_slide.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_slide.gif -------------------------------------------------------------------------------- /assets/anim_swap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_swap.gif -------------------------------------------------------------------------------- /assets/anim_thin_worm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_thin_worm.gif -------------------------------------------------------------------------------- /assets/anim_worm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/anim_worm.gif -------------------------------------------------------------------------------- /assets/animation_worm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/animation_worm.gif -------------------------------------------------------------------------------- /assets/attributes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/attributes.gif -------------------------------------------------------------------------------- /assets/preview_anim_drop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/assets/preview_anim_drop.gif -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | android.enableJetifier=false 20 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Apr 30 21:23:45 EEST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /pageindicatorview/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /pageindicatorview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | bintrayRepo = 'maven' 5 | bintrayName = 'pageindicatorview' 6 | 7 | publishedGroupId = 'com.romandanylyk' 8 | libraryName = 'PageIndicatorView' 9 | artifact = 'pageindicatorview' 10 | 11 | libraryDescription = 'Page indicator for android ViewPager' 12 | 13 | siteUrl = 'https://github.com/romandanylyk/PageIndicatorView' 14 | gitUrl = 'https://github.com/romandanylyk/PageIndicatorView.git' 15 | 16 | libraryVersion = '1.0.3' 17 | 18 | developerId = 'romandanylyk' 19 | developerName = 'Roman Danylyk' 20 | developerEmail = 'romandanylyk96@gmail.com' 21 | 22 | licenseName = 'The Apache Software License, Version 2.0' 23 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 24 | allLicenses = ["Apache-2.0"] 25 | } 26 | 27 | android { 28 | compileSdkVersion 28 29 | buildToolsVersion '28.0.3' 30 | 31 | defaultConfig { 32 | minSdkVersion 14 33 | targetSdkVersion 28 34 | versionCode 1 35 | versionName "1.0" 36 | 37 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 38 | 39 | } 40 | buildTypes { 41 | release { 42 | minifyEnabled false 43 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 44 | } 45 | } 46 | } 47 | 48 | dependencies { 49 | repositories { 50 | maven { url "https://maven.google.com" } 51 | } 52 | implementation 'androidx.annotation:annotation:1.0.0' 53 | implementation 'androidx.core:core:1.0.0' 54 | implementation 'androidx.viewpager:viewpager:1.0.0' 55 | } 56 | 57 | allprojects { 58 | tasks.withType(Javadoc) { 59 | options.addStringOption('Xdoclint:none', '-quiet') 60 | options.addStringOption('encoding', 'UTF-8') 61 | } 62 | 63 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 64 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' 65 | } 66 | -------------------------------------------------------------------------------- /pageindicatorview/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Work\IDE\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/IndicatorManager.java: -------------------------------------------------------------------------------- 1 | package com.rd; 2 | 3 | import androidx.annotation.Nullable; 4 | import com.rd.animation.AnimationManager; 5 | import com.rd.animation.controller.ValueController; 6 | import com.rd.animation.data.Value; 7 | import com.rd.draw.DrawManager; 8 | import com.rd.draw.data.Indicator; 9 | 10 | public class IndicatorManager implements ValueController.UpdateListener { 11 | 12 | private DrawManager drawManager; 13 | private AnimationManager animationManager; 14 | private Listener listener; 15 | 16 | interface Listener { 17 | void onIndicatorUpdated(); 18 | } 19 | 20 | IndicatorManager(@Nullable Listener listener) { 21 | this.listener = listener; 22 | this.drawManager = new DrawManager(); 23 | this.animationManager = new AnimationManager(drawManager.indicator(), this); 24 | } 25 | 26 | public AnimationManager animate() { 27 | return animationManager; 28 | } 29 | 30 | public Indicator indicator() { 31 | return drawManager.indicator(); 32 | } 33 | 34 | public DrawManager drawer() { 35 | return drawManager; 36 | } 37 | 38 | @Override 39 | public void onValueUpdated(@Nullable Value value) { 40 | drawManager.updateValue(value); 41 | if (listener != null) { 42 | listener.onIndicatorUpdated(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/AnimationManager.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.rd.animation.controller.AnimationController; 5 | import com.rd.animation.controller.ValueController; 6 | import com.rd.draw.data.Indicator; 7 | 8 | public class AnimationManager { 9 | 10 | private AnimationController animationController; 11 | 12 | public AnimationManager(@NonNull Indicator indicator, @NonNull ValueController.UpdateListener listener) { 13 | this.animationController = new AnimationController(indicator, listener); 14 | } 15 | 16 | public void basic() { 17 | if (animationController != null) { 18 | animationController.end(); 19 | animationController.basic(); 20 | } 21 | } 22 | 23 | public void interactive(float progress) { 24 | if (animationController != null) { 25 | animationController.interactive(progress); 26 | } 27 | } 28 | 29 | public void end() { 30 | if (animationController != null) { 31 | animationController.end(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/controller/AnimationController.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.controller; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.rd.animation.type.AnimationType; 5 | import com.rd.animation.type.BaseAnimation; 6 | import com.rd.draw.data.Indicator; 7 | import com.rd.draw.data.Orientation; 8 | import com.rd.utils.CoordinatesUtils; 9 | 10 | public class AnimationController { 11 | 12 | private ValueController valueController; 13 | private ValueController.UpdateListener listener; 14 | 15 | private BaseAnimation runningAnimation; 16 | private Indicator indicator; 17 | 18 | private float progress; 19 | private boolean isInteractive; 20 | 21 | public AnimationController(@NonNull Indicator indicator, @NonNull ValueController.UpdateListener listener) { 22 | this.valueController = new ValueController(listener); 23 | this.listener = listener; 24 | this.indicator = indicator; 25 | } 26 | 27 | public void interactive(float progress) { 28 | this.isInteractive = true; 29 | this.progress = progress; 30 | animate(); 31 | } 32 | 33 | public void basic() { 34 | this.isInteractive = false; 35 | this.progress = 0; 36 | animate(); 37 | } 38 | 39 | public void end() { 40 | if (runningAnimation != null) { 41 | runningAnimation.end(); 42 | } 43 | } 44 | 45 | private void animate() { 46 | AnimationType animationType = indicator.getAnimationType(); 47 | switch (animationType) { 48 | case NONE: 49 | listener.onValueUpdated(null); 50 | break; 51 | 52 | case COLOR: 53 | colorAnimation(); 54 | break; 55 | 56 | case SCALE: 57 | scaleAnimation(); 58 | break; 59 | 60 | case WORM: 61 | wormAnimation(); 62 | break; 63 | 64 | case FILL: 65 | fillAnimation(); 66 | break; 67 | 68 | case SLIDE: 69 | slideAnimation(); 70 | break; 71 | 72 | case THIN_WORM: 73 | thinWormAnimation(); 74 | break; 75 | 76 | case DROP: 77 | dropAnimation(); 78 | break; 79 | 80 | case SWAP: 81 | swapAnimation(); 82 | break; 83 | 84 | case SCALE_DOWN: 85 | scaleDownAnimation(); 86 | break; 87 | } 88 | } 89 | 90 | private void colorAnimation() { 91 | int selectedColor = indicator.getSelectedColor(); 92 | int unselectedColor = indicator.getUnselectedColor(); 93 | long animationDuration = indicator.getAnimationDuration(); 94 | 95 | BaseAnimation animation = valueController 96 | .color() 97 | .with(unselectedColor, selectedColor) 98 | .duration(animationDuration); 99 | 100 | if (isInteractive) { 101 | animation.progress(progress); 102 | } else { 103 | animation.start(); 104 | } 105 | 106 | runningAnimation = animation; 107 | } 108 | 109 | private void scaleAnimation() { 110 | int selectedColor = indicator.getSelectedColor(); 111 | int unselectedColor = indicator.getUnselectedColor(); 112 | int radiusPx = indicator.getRadius(); 113 | float scaleFactor = indicator.getScaleFactor(); 114 | long animationDuration = indicator.getAnimationDuration(); 115 | 116 | BaseAnimation animation = valueController 117 | .scale() 118 | .with(unselectedColor, selectedColor, radiusPx, scaleFactor) 119 | .duration(animationDuration); 120 | 121 | if (isInteractive) { 122 | animation.progress(progress); 123 | } else { 124 | animation.start(); 125 | } 126 | 127 | runningAnimation = animation; 128 | } 129 | 130 | private void wormAnimation() { 131 | int fromPosition = indicator.isInteractiveAnimation() ? indicator.getSelectedPosition() : indicator.getLastSelectedPosition(); 132 | int toPosition = indicator.isInteractiveAnimation() ? indicator.getSelectingPosition() : indicator.getSelectedPosition(); 133 | 134 | int from = CoordinatesUtils.getCoordinate(indicator, fromPosition); 135 | int to = CoordinatesUtils.getCoordinate(indicator, toPosition); 136 | boolean isRightSide = toPosition > fromPosition; 137 | 138 | int radiusPx = indicator.getRadius(); 139 | long animationDuration = indicator.getAnimationDuration(); 140 | 141 | BaseAnimation animation = valueController 142 | .worm() 143 | .with(from, to, radiusPx, isRightSide) 144 | .duration(animationDuration); 145 | 146 | if (isInteractive) { 147 | animation.progress(progress); 148 | } else { 149 | animation.start(); 150 | } 151 | 152 | runningAnimation = animation; 153 | } 154 | 155 | private void slideAnimation() { 156 | int fromPosition = indicator.isInteractiveAnimation() ? indicator.getSelectedPosition() : indicator.getLastSelectedPosition(); 157 | int toPosition = indicator.isInteractiveAnimation() ? indicator.getSelectingPosition() : indicator.getSelectedPosition(); 158 | 159 | int from = CoordinatesUtils.getCoordinate(indicator, fromPosition); 160 | int to = CoordinatesUtils.getCoordinate(indicator, toPosition); 161 | long animationDuration = indicator.getAnimationDuration(); 162 | 163 | BaseAnimation animation = valueController 164 | .slide() 165 | .with(from, to) 166 | .duration(animationDuration); 167 | 168 | if (isInteractive) { 169 | animation.progress(progress); 170 | } else { 171 | animation.start(); 172 | } 173 | 174 | runningAnimation = animation; 175 | } 176 | 177 | private void fillAnimation() { 178 | int selectedColor = indicator.getSelectedColor(); 179 | int unselectedColor = indicator.getUnselectedColor(); 180 | int radiusPx = indicator.getRadius(); 181 | int strokePx = indicator.getStroke(); 182 | long animationDuration = indicator.getAnimationDuration(); 183 | 184 | BaseAnimation animation = valueController 185 | .fill() 186 | .with(unselectedColor, selectedColor, radiusPx, strokePx) 187 | .duration(animationDuration); 188 | 189 | if (isInteractive) { 190 | animation.progress(progress); 191 | } else { 192 | animation.start(); 193 | } 194 | 195 | runningAnimation = animation; 196 | } 197 | 198 | private void thinWormAnimation() { 199 | int fromPosition = indicator.isInteractiveAnimation() ? indicator.getSelectedPosition() : indicator.getLastSelectedPosition(); 200 | int toPosition = indicator.isInteractiveAnimation() ? indicator.getSelectingPosition() : indicator.getSelectedPosition(); 201 | 202 | int from = CoordinatesUtils.getCoordinate(indicator, fromPosition); 203 | int to = CoordinatesUtils.getCoordinate(indicator, toPosition); 204 | boolean isRightSide = toPosition > fromPosition; 205 | 206 | int radiusPx = indicator.getRadius(); 207 | long animationDuration = indicator.getAnimationDuration(); 208 | 209 | BaseAnimation animation = valueController 210 | .thinWorm() 211 | .with(from, to, radiusPx, isRightSide) 212 | .duration(animationDuration); 213 | 214 | if (isInteractive) { 215 | animation.progress(progress); 216 | } else { 217 | animation.start(); 218 | } 219 | 220 | runningAnimation = animation; 221 | } 222 | 223 | private void dropAnimation() { 224 | int fromPosition = indicator.isInteractiveAnimation() ? indicator.getSelectedPosition() : indicator.getLastSelectedPosition(); 225 | int toPosition = indicator.isInteractiveAnimation() ? indicator.getSelectingPosition() : indicator.getSelectedPosition(); 226 | 227 | int widthFrom = CoordinatesUtils.getCoordinate(indicator, fromPosition); 228 | int widthTo = CoordinatesUtils.getCoordinate(indicator, toPosition); 229 | 230 | int paddingTop = indicator.getPaddingTop(); 231 | int paddingLeft = indicator.getPaddingLeft(); 232 | int padding = indicator.getOrientation() == Orientation.HORIZONTAL ? paddingTop : paddingLeft; 233 | 234 | int radius = indicator.getRadius(); 235 | int heightFrom = radius * 3 + padding; 236 | int heightTo = radius + padding; 237 | 238 | long animationDuration = indicator.getAnimationDuration(); 239 | 240 | BaseAnimation animation = valueController 241 | .drop() 242 | .duration(animationDuration) 243 | .with(widthFrom, widthTo, heightFrom, heightTo, radius); 244 | 245 | if (isInteractive) { 246 | animation.progress(progress); 247 | } else { 248 | animation.start(); 249 | } 250 | 251 | runningAnimation = animation; 252 | } 253 | 254 | private void swapAnimation() { 255 | int fromPosition = indicator.isInteractiveAnimation() ? indicator.getSelectedPosition() : indicator.getLastSelectedPosition(); 256 | int toPosition = indicator.isInteractiveAnimation() ? indicator.getSelectingPosition() : indicator.getSelectedPosition(); 257 | 258 | int from = CoordinatesUtils.getCoordinate(indicator, fromPosition); 259 | int to = CoordinatesUtils.getCoordinate(indicator, toPosition); 260 | long animationDuration = indicator.getAnimationDuration(); 261 | 262 | BaseAnimation animation = valueController 263 | .swap() 264 | .with(from, to) 265 | .duration(animationDuration); 266 | 267 | if (isInteractive) { 268 | animation.progress(progress); 269 | } else { 270 | animation.start(); 271 | } 272 | 273 | runningAnimation = animation; 274 | } 275 | 276 | private void scaleDownAnimation() { 277 | int selectedColor = indicator.getSelectedColor(); 278 | int unselectedColor = indicator.getUnselectedColor(); 279 | int radiusPx = indicator.getRadius(); 280 | float scaleFactor = indicator.getScaleFactor(); 281 | long animationDuration = indicator.getAnimationDuration(); 282 | 283 | BaseAnimation animation = valueController 284 | .scaleDown() 285 | .with(unselectedColor, selectedColor, radiusPx, scaleFactor) 286 | .duration(animationDuration); 287 | 288 | if (isInteractive) { 289 | animation.progress(progress); 290 | } else { 291 | animation.start(); 292 | } 293 | 294 | runningAnimation = animation; 295 | } 296 | } 297 | 298 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/controller/ValueController.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.controller; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import com.rd.animation.data.Value; 6 | import com.rd.animation.type.*; 7 | 8 | public class ValueController { 9 | 10 | private ColorAnimation colorAnimation; 11 | private ScaleAnimation scaleAnimation; 12 | private WormAnimation wormAnimation; 13 | private SlideAnimation slideAnimation; 14 | private FillAnimation fillAnimation; 15 | private ThinWormAnimation thinWormAnimation; 16 | private DropAnimation dropAnimation; 17 | private SwapAnimation swapAnimation; 18 | private ScaleDownAnimation scaleDownAnimation; 19 | 20 | private UpdateListener updateListener; 21 | 22 | public interface UpdateListener { 23 | void onValueUpdated(@Nullable Value value); 24 | } 25 | 26 | public ValueController(@Nullable UpdateListener listener) { 27 | updateListener = listener; 28 | } 29 | 30 | @NonNull 31 | public ColorAnimation color() { 32 | if (colorAnimation == null) { 33 | colorAnimation = new ColorAnimation(updateListener); 34 | } 35 | 36 | return colorAnimation; 37 | } 38 | 39 | @NonNull 40 | public ScaleAnimation scale() { 41 | if (scaleAnimation == null) { 42 | scaleAnimation = new ScaleAnimation(updateListener); 43 | } 44 | 45 | return scaleAnimation; 46 | } 47 | 48 | @NonNull 49 | public WormAnimation worm() { 50 | if (wormAnimation == null) { 51 | wormAnimation = new WormAnimation(updateListener); 52 | } 53 | 54 | return wormAnimation; 55 | } 56 | 57 | @NonNull 58 | public SlideAnimation slide() { 59 | if (slideAnimation == null) { 60 | slideAnimation = new SlideAnimation(updateListener); 61 | } 62 | 63 | return slideAnimation; 64 | } 65 | 66 | @NonNull 67 | public FillAnimation fill() { 68 | if (fillAnimation == null) { 69 | fillAnimation = new FillAnimation(updateListener); 70 | } 71 | 72 | return fillAnimation; 73 | } 74 | 75 | @NonNull 76 | public ThinWormAnimation thinWorm() { 77 | if (thinWormAnimation == null) { 78 | thinWormAnimation = new ThinWormAnimation(updateListener); 79 | } 80 | 81 | return thinWormAnimation; 82 | } 83 | 84 | @NonNull 85 | public DropAnimation drop() { 86 | if (dropAnimation == null) { 87 | dropAnimation = new DropAnimation(updateListener); 88 | } 89 | 90 | return dropAnimation; 91 | } 92 | 93 | @NonNull 94 | public SwapAnimation swap() { 95 | if (swapAnimation == null) { 96 | swapAnimation = new SwapAnimation(updateListener); 97 | } 98 | 99 | return swapAnimation; 100 | } 101 | 102 | @NonNull 103 | public ScaleDownAnimation scaleDown() { 104 | if (scaleDownAnimation == null) { 105 | scaleDownAnimation = new ScaleDownAnimation(updateListener); 106 | } 107 | 108 | return scaleDownAnimation; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/AnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.rd.animation.data.type.*; 5 | 6 | public class AnimationValue { 7 | 8 | private ColorAnimationValue colorAnimationValue; 9 | private ScaleAnimationValue scaleAnimationValue; 10 | private WormAnimationValue wormAnimationValue; 11 | private FillAnimationValue fillAnimationValue; 12 | private ThinWormAnimationValue thinWormAnimationValue; 13 | private DropAnimationValue dropAnimationValue; 14 | private SwapAnimationValue swapAnimationValue; 15 | 16 | @NonNull 17 | public ColorAnimationValue getColorAnimationValue() { 18 | if (colorAnimationValue == null) { 19 | colorAnimationValue = new ColorAnimationValue(); 20 | } 21 | return colorAnimationValue; 22 | } 23 | 24 | @NonNull 25 | public ScaleAnimationValue getScaleAnimationValue() { 26 | if (scaleAnimationValue == null) { 27 | scaleAnimationValue = new ScaleAnimationValue(); 28 | } 29 | return scaleAnimationValue; 30 | } 31 | 32 | @NonNull 33 | public WormAnimationValue getWormAnimationValue() { 34 | if (wormAnimationValue == null) { 35 | wormAnimationValue = new WormAnimationValue(); 36 | } 37 | return wormAnimationValue; 38 | } 39 | 40 | @NonNull 41 | public FillAnimationValue getFillAnimationValue() { 42 | if (fillAnimationValue == null) { 43 | fillAnimationValue = new FillAnimationValue(); 44 | } 45 | return fillAnimationValue; 46 | } 47 | 48 | @NonNull 49 | public ThinWormAnimationValue getThinWormAnimationValue() { 50 | if (thinWormAnimationValue == null) { 51 | thinWormAnimationValue = new ThinWormAnimationValue(); 52 | } 53 | return thinWormAnimationValue; 54 | } 55 | 56 | @NonNull 57 | public DropAnimationValue getDropAnimationValue() { 58 | if (dropAnimationValue == null) { 59 | dropAnimationValue = new DropAnimationValue(); 60 | } 61 | return dropAnimationValue; 62 | } 63 | 64 | @NonNull 65 | public SwapAnimationValue getSwapAnimationValue() { 66 | if (swapAnimationValue == null) { 67 | swapAnimationValue = new SwapAnimationValue(); 68 | } 69 | return swapAnimationValue; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/Value.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data; 2 | 3 | public interface Value {/*empty*/} 4 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/ColorAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class ColorAnimationValue implements Value { 6 | 7 | private int color; 8 | private int colorReverse; 9 | 10 | public int getColor() { 11 | return color; 12 | } 13 | 14 | public void setColor(int color) { 15 | this.color = color; 16 | } 17 | 18 | public int getColorReverse() { 19 | return colorReverse; 20 | } 21 | 22 | public void setColorReverse(int colorReverse) { 23 | this.colorReverse = colorReverse; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/DropAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class DropAnimationValue implements Value { 6 | 7 | private int width; 8 | private int height; 9 | private int radius; 10 | 11 | public int getWidth() { 12 | return width; 13 | } 14 | 15 | public void setWidth(int width) { 16 | this.width = width; 17 | } 18 | 19 | public int getHeight() { 20 | return height; 21 | } 22 | 23 | public void setHeight(int height) { 24 | this.height = height; 25 | } 26 | 27 | public int getRadius() { 28 | return radius; 29 | } 30 | 31 | public void setRadius(int radius) { 32 | this.radius = radius; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/FillAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class FillAnimationValue extends ColorAnimationValue implements Value { 6 | 7 | private int radius; 8 | private int radiusReverse; 9 | 10 | private int stroke; 11 | private int strokeReverse; 12 | 13 | public int getRadius() { 14 | return radius; 15 | } 16 | 17 | public void setRadius(int radius) { 18 | this.radius = radius; 19 | } 20 | 21 | public int getRadiusReverse() { 22 | return radiusReverse; 23 | } 24 | 25 | public void setRadiusReverse(int radiusReverse) { 26 | this.radiusReverse = radiusReverse; 27 | } 28 | 29 | public int getStroke() { 30 | return stroke; 31 | } 32 | 33 | public void setStroke(int stroke) { 34 | this.stroke = stroke; 35 | } 36 | 37 | public int getStrokeReverse() { 38 | return strokeReverse; 39 | } 40 | 41 | public void setStrokeReverse(int strokeReverse) { 42 | this.strokeReverse = strokeReverse; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/ScaleAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class ScaleAnimationValue extends ColorAnimationValue implements Value { 6 | 7 | private int radius; 8 | private int radiusReverse; 9 | 10 | public int getRadius() { 11 | return radius; 12 | } 13 | 14 | public void setRadius(int radius) { 15 | this.radius = radius; 16 | } 17 | 18 | public int getRadiusReverse() { 19 | return radiusReverse; 20 | } 21 | 22 | public void setRadiusReverse(int radiusReverse) { 23 | this.radiusReverse = radiusReverse; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/SlideAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class SlideAnimationValue implements Value { 6 | 7 | private int coordinate; 8 | 9 | public int getCoordinate() { 10 | return coordinate; 11 | } 12 | 13 | public void setCoordinate(int coordinate) { 14 | this.coordinate = coordinate; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/SwapAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class SwapAnimationValue implements Value { 6 | 7 | private int coordinate; 8 | private int coordinateReverse; 9 | 10 | public int getCoordinate() { 11 | return coordinate; 12 | } 13 | 14 | public void setCoordinate(int coordinate) { 15 | this.coordinate = coordinate; 16 | } 17 | 18 | public int getCoordinateReverse() { 19 | return coordinateReverse; 20 | } 21 | 22 | public void setCoordinateReverse(int coordinateReverse) { 23 | this.coordinateReverse = coordinateReverse; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/ThinWormAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class ThinWormAnimationValue extends WormAnimationValue implements Value { 6 | 7 | private int height; 8 | 9 | public int getHeight() { 10 | return height; 11 | } 12 | 13 | public void setHeight(int height) { 14 | this.height = height; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/data/type/WormAnimationValue.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.data.type; 2 | 3 | import com.rd.animation.data.Value; 4 | 5 | public class WormAnimationValue implements Value { 6 | 7 | private int rectStart; 8 | private int rectEnd; 9 | 10 | public int getRectStart() { 11 | return rectStart; 12 | } 13 | 14 | public void setRectStart(int rectStartEdge) { 15 | this.rectStart = rectStartEdge; 16 | } 17 | 18 | public int getRectEnd() { 19 | return rectEnd; 20 | } 21 | 22 | public void setRectEnd(int rectEnd) { 23 | this.rectEnd = rectEnd; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/AnimationType.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | public enum AnimationType {NONE, COLOR, SCALE, WORM, SLIDE, FILL, THIN_WORM, DROP, SWAP, SCALE_DOWN} 4 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/BaseAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | import com.rd.animation.controller.ValueController; 8 | 9 | public abstract class BaseAnimation { 10 | 11 | public static final int DEFAULT_ANIMATION_TIME = 350; 12 | protected long animationDuration = DEFAULT_ANIMATION_TIME; 13 | 14 | protected ValueController.UpdateListener listener; 15 | protected T animator; 16 | 17 | public BaseAnimation(@Nullable ValueController.UpdateListener listener) { 18 | this.listener = listener; 19 | animator = createAnimator(); 20 | } 21 | 22 | @NonNull 23 | public abstract T createAnimator(); 24 | 25 | public abstract BaseAnimation progress(float progress); 26 | 27 | public BaseAnimation duration(long duration) { 28 | animationDuration = duration; 29 | 30 | if (animator instanceof ValueAnimator) { 31 | animator.setDuration(animationDuration); 32 | } 33 | 34 | return this; 35 | } 36 | 37 | public void start() { 38 | if (animator != null && !animator.isRunning()) { 39 | animator.start(); 40 | } 41 | } 42 | 43 | public void end() { 44 | if (animator != null && animator.isStarted()) { 45 | animator.end(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/ColorAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.ArgbEvaluator; 4 | import android.animation.PropertyValuesHolder; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | import android.view.animation.AccelerateDecelerateInterpolator; 9 | import com.rd.animation.controller.ValueController; 10 | import com.rd.animation.data.type.ColorAnimationValue; 11 | 12 | public class ColorAnimation extends BaseAnimation { 13 | 14 | public static final String DEFAULT_UNSELECTED_COLOR = "#33ffffff"; 15 | public static final String DEFAULT_SELECTED_COLOR = "#ffffff"; 16 | 17 | static final String ANIMATION_COLOR_REVERSE = "ANIMATION_COLOR_REVERSE"; 18 | static final String ANIMATION_COLOR = "ANIMATION_COLOR"; 19 | 20 | private ColorAnimationValue value; 21 | 22 | int colorStart; 23 | int colorEnd; 24 | 25 | public ColorAnimation(@Nullable ValueController.UpdateListener listener) { 26 | super(listener); 27 | value = new ColorAnimationValue(); 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public ValueAnimator createAnimator() { 33 | ValueAnimator animator = new ValueAnimator(); 34 | animator.setDuration(BaseAnimation.DEFAULT_ANIMATION_TIME); 35 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 36 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 37 | @Override 38 | public void onAnimationUpdate(ValueAnimator animation) { 39 | onAnimateUpdated(animation); 40 | } 41 | }); 42 | 43 | return animator; 44 | } 45 | 46 | @Override 47 | public ColorAnimation progress(float progress) { 48 | if (animator != null) { 49 | long playTime = (long) (progress * animationDuration); 50 | 51 | if (animator.getValues() != null && animator.getValues().length > 0) { 52 | animator.setCurrentPlayTime(playTime); 53 | } 54 | } 55 | 56 | return this; 57 | } 58 | 59 | @NonNull 60 | public ColorAnimation with(int colorStart, int colorEnd) { 61 | if (animator != null && hasChanges(colorStart, colorEnd)) { 62 | 63 | this.colorStart = colorStart; 64 | this.colorEnd = colorEnd; 65 | 66 | PropertyValuesHolder colorHolder = createColorPropertyHolder(false); 67 | PropertyValuesHolder reverseColorHolder = createColorPropertyHolder(true); 68 | 69 | animator.setValues(colorHolder, reverseColorHolder); 70 | } 71 | 72 | return this; 73 | } 74 | 75 | PropertyValuesHolder createColorPropertyHolder(boolean isReverse) { 76 | String propertyName; 77 | int colorStart; 78 | int colorEnd; 79 | 80 | if (isReverse) { 81 | propertyName = ANIMATION_COLOR_REVERSE; 82 | colorStart = this.colorEnd; 83 | colorEnd = this.colorStart; 84 | 85 | } else { 86 | propertyName = ANIMATION_COLOR; 87 | colorStart = this.colorStart; 88 | colorEnd = this.colorEnd; 89 | } 90 | 91 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(propertyName, colorStart, colorEnd); 92 | holder.setEvaluator(new ArgbEvaluator()); 93 | 94 | return holder; 95 | } 96 | 97 | @SuppressWarnings("RedundantIfStatement") 98 | private boolean hasChanges(int colorStart, int colorEnd) { 99 | if (this.colorStart != colorStart) { 100 | return true; 101 | } 102 | 103 | if (this.colorEnd != colorEnd) { 104 | return true; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | private void onAnimateUpdated(@NonNull ValueAnimator animation) { 111 | int color = (int) animation.getAnimatedValue(ANIMATION_COLOR); 112 | int colorReverse = (int) animation.getAnimatedValue(ANIMATION_COLOR_REVERSE); 113 | 114 | value.setColor(color); 115 | value.setColorReverse(colorReverse); 116 | 117 | if (listener != null) { 118 | listener.onValueUpdated(value); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/DropAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import android.view.animation.AccelerateDecelerateInterpolator; 8 | import com.rd.animation.controller.ValueController; 9 | import com.rd.animation.data.type.DropAnimationValue; 10 | 11 | public class DropAnimation extends BaseAnimation { 12 | 13 | private int widthStart; 14 | private int widthEnd; 15 | private int heightStart; 16 | private int heightEnd; 17 | private int radius; 18 | 19 | private enum AnimationType {Width, Height, Radius} 20 | 21 | private DropAnimationValue value; 22 | 23 | public DropAnimation(@NonNull ValueController.UpdateListener listener) { 24 | super(listener); 25 | value = new DropAnimationValue(); 26 | } 27 | 28 | @NonNull 29 | @Override 30 | public AnimatorSet createAnimator() { 31 | AnimatorSet animator = new AnimatorSet(); 32 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 33 | 34 | return animator; 35 | } 36 | 37 | @Override 38 | public DropAnimation progress(float progress) { 39 | if (animator != null) { 40 | long playTimeLeft = (long) (progress * animationDuration); 41 | boolean isReverse = false; 42 | 43 | for (Animator anim : animator.getChildAnimations()) { 44 | ValueAnimator animator = (ValueAnimator) anim; 45 | long animDuration = animator.getDuration(); 46 | long currPlayTime = playTimeLeft; 47 | 48 | if (isReverse) { 49 | currPlayTime -= animDuration; 50 | } 51 | 52 | if (currPlayTime < 0) { 53 | continue; 54 | 55 | } else if (currPlayTime >= animDuration) { 56 | currPlayTime = animDuration; 57 | } 58 | 59 | if (animator.getValues() != null && animator.getValues().length > 0) { 60 | animator.setCurrentPlayTime(currPlayTime); 61 | } 62 | 63 | if (!isReverse && animDuration >= animationDuration) { 64 | isReverse = true; 65 | } 66 | } 67 | } 68 | 69 | return this; 70 | } 71 | 72 | @Override 73 | public DropAnimation duration(long duration) { 74 | super.duration(duration); 75 | return this; 76 | } 77 | 78 | @SuppressWarnings("UnnecessaryLocalVariable") 79 | public DropAnimation with(int widthStart, int widthEnd, int heightStart, int heightEnd, int radius) { 80 | if (hasChanges(widthStart, widthEnd, heightStart, heightEnd, radius)) { 81 | animator = createAnimator(); 82 | 83 | this.widthStart = widthStart; 84 | this.widthEnd = widthEnd; 85 | this.heightStart = heightStart; 86 | this.heightEnd = heightEnd; 87 | this.radius = radius; 88 | 89 | int fromRadius = radius; 90 | int toRadius = (int) (radius / 1.5); 91 | long halfDuration = animationDuration / 2; 92 | 93 | ValueAnimator widthAnimator = createValueAnimation(widthStart, widthEnd, animationDuration, AnimationType.Width); 94 | ValueAnimator heightForwardAnimator = createValueAnimation(heightStart, heightEnd, halfDuration, AnimationType.Height); 95 | ValueAnimator radiusForwardAnimator = createValueAnimation(fromRadius, toRadius, halfDuration, AnimationType.Radius); 96 | 97 | ValueAnimator heightBackwardAnimator = createValueAnimation(heightEnd, heightStart, halfDuration, AnimationType.Height); 98 | ValueAnimator radiusBackwardAnimator = createValueAnimation(toRadius, fromRadius, halfDuration, AnimationType.Radius); 99 | 100 | animator.play(heightForwardAnimator) 101 | .with(radiusForwardAnimator) 102 | .with(widthAnimator) 103 | .before(heightBackwardAnimator) 104 | .before(radiusBackwardAnimator); 105 | } 106 | 107 | return this; 108 | } 109 | 110 | private ValueAnimator createValueAnimation(int fromValue, int toValue, long duration, final AnimationType type) { 111 | ValueAnimator anim = ValueAnimator.ofInt(fromValue, toValue); 112 | anim.setInterpolator(new AccelerateDecelerateInterpolator()); 113 | anim.setDuration(duration); 114 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 115 | @Override 116 | public void onAnimationUpdate(ValueAnimator animation) { 117 | onAnimatorUpdate(animation, type); 118 | } 119 | }); 120 | 121 | return anim; 122 | } 123 | 124 | private void onAnimatorUpdate(@NonNull ValueAnimator animation, @NonNull AnimationType type) { 125 | int frameValue = (int) animation.getAnimatedValue(); 126 | 127 | switch (type) { 128 | case Width: 129 | value.setWidth(frameValue); 130 | break; 131 | 132 | case Height: 133 | value.setHeight(frameValue); 134 | break; 135 | 136 | case Radius: 137 | value.setRadius(frameValue); 138 | break; 139 | } 140 | 141 | if (listener != null) { 142 | listener.onValueUpdated(value); 143 | } 144 | } 145 | 146 | @SuppressWarnings("RedundantIfStatement") 147 | private boolean hasChanges(int widthStart, int widthEnd, int heightStart, int heightEnd, int radius) { 148 | if (this.widthStart != widthStart) { 149 | return true; 150 | } 151 | 152 | if (this.widthEnd != widthEnd) { 153 | return true; 154 | } 155 | 156 | if (this.heightStart != heightStart) { 157 | return true; 158 | } 159 | 160 | if (this.heightEnd != heightEnd) { 161 | return true; 162 | } 163 | 164 | if (this.radius != radius) { 165 | return true; 166 | } 167 | 168 | return false; 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/FillAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.IntEvaluator; 4 | import android.animation.PropertyValuesHolder; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import android.view.animation.AccelerateDecelerateInterpolator; 8 | import com.rd.animation.controller.ValueController; 9 | import com.rd.animation.data.type.FillAnimationValue; 10 | 11 | public class FillAnimation extends ColorAnimation { 12 | 13 | private static final String ANIMATION_RADIUS_REVERSE = "ANIMATION_RADIUS_REVERSE"; 14 | private static final String ANIMATION_RADIUS = "ANIMATION_RADIUS"; 15 | 16 | private static final String ANIMATION_STROKE_REVERSE = "ANIMATION_STROKE_REVERSE"; 17 | private static final String ANIMATION_STROKE = "ANIMATION_STROKE"; 18 | 19 | public static final int DEFAULT_STROKE_DP = 1; 20 | private FillAnimationValue value; 21 | 22 | private int radius; 23 | private int stroke; 24 | 25 | public FillAnimation(@NonNull ValueController.UpdateListener listener) { 26 | super(listener); 27 | value = new FillAnimationValue(); 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public ValueAnimator createAnimator() { 33 | ValueAnimator animator = new ValueAnimator(); 34 | animator.setDuration(BaseAnimation.DEFAULT_ANIMATION_TIME); 35 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 36 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 37 | @Override 38 | public void onAnimationUpdate(ValueAnimator animation) { 39 | onAnimateUpdated(animation); 40 | } 41 | }); 42 | 43 | return animator; 44 | } 45 | 46 | @NonNull 47 | public FillAnimation with(int colorStart, int colorEnd, int radius, int stroke) { 48 | if (animator != null && hasChanges(colorStart, colorEnd, radius, stroke)) { 49 | 50 | this.colorStart = colorStart; 51 | this.colorEnd = colorEnd; 52 | 53 | this.radius = radius; 54 | this.stroke = stroke; 55 | 56 | PropertyValuesHolder colorHolder = createColorPropertyHolder(false); 57 | PropertyValuesHolder reverseColorHolder = createColorPropertyHolder(true); 58 | 59 | PropertyValuesHolder radiusHolder = createRadiusPropertyHolder(false); 60 | PropertyValuesHolder radiusReverseHolder = createRadiusPropertyHolder(true); 61 | 62 | PropertyValuesHolder strokeHolder = createStrokePropertyHolder(false); 63 | PropertyValuesHolder strokeReverseHolder = createStrokePropertyHolder(true); 64 | 65 | animator.setValues( 66 | colorHolder, 67 | reverseColorHolder, 68 | 69 | radiusHolder, 70 | radiusReverseHolder, 71 | 72 | strokeHolder, 73 | strokeReverseHolder); 74 | } 75 | 76 | return this; 77 | } 78 | 79 | @NonNull 80 | private PropertyValuesHolder createRadiusPropertyHolder(boolean isReverse) { 81 | String propertyName; 82 | int startRadiusValue; 83 | int endRadiusValue; 84 | 85 | if (isReverse) { 86 | propertyName = ANIMATION_RADIUS_REVERSE; 87 | startRadiusValue = radius / 2; 88 | endRadiusValue = radius; 89 | } else { 90 | propertyName = ANIMATION_RADIUS; 91 | startRadiusValue = radius; 92 | endRadiusValue = radius / 2; 93 | } 94 | 95 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(propertyName, startRadiusValue, endRadiusValue); 96 | holder.setEvaluator(new IntEvaluator()); 97 | 98 | return holder; 99 | } 100 | 101 | @NonNull 102 | private PropertyValuesHolder createStrokePropertyHolder(boolean isReverse) { 103 | String propertyName; 104 | int startStrokeValue; 105 | int endStrokeValue; 106 | 107 | if (isReverse) { 108 | propertyName = ANIMATION_STROKE_REVERSE; 109 | startStrokeValue = radius; 110 | endStrokeValue = 0; 111 | } else { 112 | propertyName = ANIMATION_STROKE; 113 | startStrokeValue = 0; 114 | endStrokeValue = radius; 115 | } 116 | 117 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(propertyName, startStrokeValue, endStrokeValue); 118 | holder.setEvaluator(new IntEvaluator()); 119 | 120 | return holder; 121 | } 122 | 123 | private void onAnimateUpdated(@NonNull ValueAnimator animation) { 124 | int color = (int) animation.getAnimatedValue(ANIMATION_COLOR); 125 | int colorReverse = (int) animation.getAnimatedValue(ANIMATION_COLOR_REVERSE); 126 | 127 | int radius = (int) animation.getAnimatedValue(ANIMATION_RADIUS); 128 | int radiusReverse = (int) animation.getAnimatedValue(ANIMATION_RADIUS_REVERSE); 129 | 130 | int stroke = (int) animation.getAnimatedValue(ANIMATION_STROKE); 131 | int strokeReverse = (int) animation.getAnimatedValue(ANIMATION_STROKE_REVERSE); 132 | 133 | value.setColor(color); 134 | value.setColorReverse(colorReverse); 135 | 136 | value.setRadius(radius); 137 | value.setRadiusReverse(radiusReverse); 138 | 139 | value.setStroke(stroke); 140 | value.setStrokeReverse(strokeReverse); 141 | 142 | if (listener != null) { 143 | listener.onValueUpdated(value); 144 | } 145 | } 146 | 147 | @SuppressWarnings("RedundantIfStatement") 148 | private boolean hasChanges(int colorStart, int colorEnd, int radiusValue, int strokeValue) { 149 | if (this.colorStart != colorStart) { 150 | return true; 151 | } 152 | 153 | if (this.colorEnd != colorEnd) { 154 | return true; 155 | } 156 | 157 | if (radius != radiusValue) { 158 | return true; 159 | } 160 | 161 | if (stroke != strokeValue) { 162 | return true; 163 | } 164 | 165 | return false; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/ScaleAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.IntEvaluator; 4 | import android.animation.PropertyValuesHolder; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import android.view.animation.AccelerateDecelerateInterpolator; 8 | import com.rd.animation.controller.ValueController; 9 | import com.rd.animation.data.type.ScaleAnimationValue; 10 | 11 | public class ScaleAnimation extends ColorAnimation { 12 | 13 | public static final float DEFAULT_SCALE_FACTOR = 0.7f; 14 | public static final float MIN_SCALE_FACTOR = 0.3f; 15 | public static final float MAX_SCALE_FACTOR = 1; 16 | 17 | static final String ANIMATION_SCALE_REVERSE = "ANIMATION_SCALE_REVERSE"; 18 | static final String ANIMATION_SCALE = "ANIMATION_SCALE"; 19 | 20 | int radius; 21 | float scaleFactor; 22 | 23 | private ScaleAnimationValue value; 24 | 25 | public ScaleAnimation(@NonNull ValueController.UpdateListener listener) { 26 | super(listener); 27 | value = new ScaleAnimationValue(); 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public ValueAnimator createAnimator() { 33 | ValueAnimator animator = new ValueAnimator(); 34 | animator.setDuration(BaseAnimation.DEFAULT_ANIMATION_TIME); 35 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 36 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 37 | @Override 38 | public void onAnimationUpdate(ValueAnimator animation) { 39 | onAnimateUpdated(animation); 40 | } 41 | }); 42 | 43 | return animator; 44 | } 45 | 46 | @NonNull 47 | public ScaleAnimation with(int colorStart, int colorEnd, int radius, float scaleFactor) { 48 | if (animator != null && hasChanges(colorStart, colorEnd, radius, scaleFactor)) { 49 | 50 | this.colorStart = colorStart; 51 | this.colorEnd = colorEnd; 52 | 53 | this.radius = radius; 54 | this.scaleFactor = scaleFactor; 55 | 56 | PropertyValuesHolder colorHolder = createColorPropertyHolder(false); 57 | PropertyValuesHolder reverseColorHolder = createColorPropertyHolder(true); 58 | 59 | PropertyValuesHolder scaleHolder = createScalePropertyHolder(false); 60 | PropertyValuesHolder scaleReverseHolder = createScalePropertyHolder(true); 61 | 62 | animator.setValues(colorHolder, reverseColorHolder, scaleHolder, scaleReverseHolder); 63 | } 64 | 65 | return this; 66 | } 67 | 68 | private void onAnimateUpdated(@NonNull ValueAnimator animation) { 69 | int color = (int) animation.getAnimatedValue(ANIMATION_COLOR); 70 | int colorReverse = (int) animation.getAnimatedValue(ANIMATION_COLOR_REVERSE); 71 | 72 | int radius = (int) animation.getAnimatedValue(ANIMATION_SCALE); 73 | int radiusReverse = (int) animation.getAnimatedValue(ANIMATION_SCALE_REVERSE); 74 | 75 | value.setColor(color); 76 | value.setColorReverse(colorReverse); 77 | 78 | value.setRadius(radius); 79 | value.setRadiusReverse(radiusReverse); 80 | 81 | if (listener != null) { 82 | listener.onValueUpdated(value); 83 | } 84 | } 85 | 86 | @NonNull 87 | protected PropertyValuesHolder createScalePropertyHolder(boolean isReverse) { 88 | String propertyName; 89 | int startRadiusValue; 90 | int endRadiusValue; 91 | 92 | if (isReverse) { 93 | propertyName = ANIMATION_SCALE_REVERSE; 94 | startRadiusValue = radius; 95 | endRadiusValue = (int) (radius * scaleFactor); 96 | } else { 97 | propertyName = ANIMATION_SCALE; 98 | startRadiusValue = (int) (radius * scaleFactor); 99 | endRadiusValue = radius; 100 | } 101 | 102 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(propertyName, startRadiusValue, endRadiusValue); 103 | holder.setEvaluator(new IntEvaluator()); 104 | 105 | return holder; 106 | } 107 | 108 | @SuppressWarnings("RedundantIfStatement") 109 | private boolean hasChanges(int colorStart, int colorEnd, int radiusValue, float scaleFactorValue) { 110 | if (this.colorStart != colorStart) { 111 | return true; 112 | } 113 | 114 | if (this.colorEnd != colorEnd) { 115 | return true; 116 | } 117 | 118 | if (radius != radiusValue) { 119 | return true; 120 | } 121 | 122 | if (scaleFactor != scaleFactorValue) { 123 | return true; 124 | } 125 | 126 | return false; 127 | } 128 | } 129 | 130 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/ScaleDownAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.IntEvaluator; 4 | import android.animation.PropertyValuesHolder; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.controller.ValueController; 7 | 8 | public class ScaleDownAnimation extends ScaleAnimation { 9 | 10 | public ScaleDownAnimation(@NonNull ValueController.UpdateListener listener) { 11 | super(listener); 12 | } 13 | 14 | @NonNull 15 | @Override 16 | protected PropertyValuesHolder createScalePropertyHolder(boolean isReverse) { 17 | String propertyName; 18 | int startRadiusValue; 19 | int endRadiusValue; 20 | 21 | if (isReverse) { 22 | propertyName = ANIMATION_SCALE_REVERSE; 23 | startRadiusValue = (int) (radius * scaleFactor); 24 | endRadiusValue = radius; 25 | } else { 26 | propertyName = ANIMATION_SCALE; 27 | startRadiusValue = radius; 28 | endRadiusValue = (int) (radius * scaleFactor); 29 | } 30 | 31 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(propertyName, startRadiusValue, endRadiusValue); 32 | holder.setEvaluator(new IntEvaluator()); 33 | 34 | return holder; 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/SlideAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.IntEvaluator; 4 | import android.animation.PropertyValuesHolder; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import android.view.animation.AccelerateDecelerateInterpolator; 8 | import com.rd.animation.controller.ValueController; 9 | import com.rd.animation.data.type.SlideAnimationValue; 10 | 11 | public class SlideAnimation extends BaseAnimation { 12 | 13 | private static final String ANIMATION_COORDINATE = "ANIMATION_COORDINATE"; 14 | private static final int COORDINATE_NONE = -1; 15 | 16 | private SlideAnimationValue value; 17 | private int coordinateStart = COORDINATE_NONE; 18 | private int coordinateEnd = COORDINATE_NONE; 19 | 20 | public SlideAnimation(@NonNull ValueController.UpdateListener listener) { 21 | super(listener); 22 | value = new SlideAnimationValue(); 23 | } 24 | 25 | @NonNull 26 | @Override 27 | public ValueAnimator createAnimator() { 28 | ValueAnimator animator = new ValueAnimator(); 29 | animator.setDuration(BaseAnimation.DEFAULT_ANIMATION_TIME); 30 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 31 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 32 | @Override 33 | public void onAnimationUpdate(ValueAnimator animation) { 34 | onAnimateUpdated(animation); 35 | } 36 | }); 37 | 38 | return animator; 39 | } 40 | 41 | @Override 42 | public SlideAnimation progress(float progress) { 43 | if (animator != null) { 44 | long playTime = (long) (progress * animationDuration); 45 | 46 | if (animator.getValues() != null && animator.getValues().length > 0) { 47 | animator.setCurrentPlayTime(playTime); 48 | } 49 | } 50 | 51 | return this; 52 | } 53 | 54 | @NonNull 55 | public SlideAnimation with(int coordinateStart, int coordinateEnd) { 56 | if (animator != null && hasChanges(coordinateStart, coordinateEnd)) { 57 | 58 | this.coordinateStart = coordinateStart; 59 | this.coordinateEnd = coordinateEnd; 60 | 61 | PropertyValuesHolder holder = createSlidePropertyHolder(); 62 | animator.setValues(holder); 63 | } 64 | 65 | return this; 66 | } 67 | 68 | private PropertyValuesHolder createSlidePropertyHolder() { 69 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(ANIMATION_COORDINATE, coordinateStart, coordinateEnd); 70 | holder.setEvaluator(new IntEvaluator()); 71 | 72 | return holder; 73 | } 74 | 75 | private void onAnimateUpdated(@NonNull ValueAnimator animation) { 76 | int coordinate = (int) animation.getAnimatedValue(ANIMATION_COORDINATE); 77 | value.setCoordinate(coordinate); 78 | 79 | if (listener != null) { 80 | listener.onValueUpdated(value); 81 | } 82 | } 83 | 84 | @SuppressWarnings("RedundantIfStatement") 85 | private boolean hasChanges(int coordinateStart, int coordinateEnd) { 86 | if (this.coordinateStart != coordinateStart) { 87 | return true; 88 | } 89 | 90 | if (this.coordinateEnd != coordinateEnd) { 91 | return true; 92 | } 93 | 94 | return false; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/SwapAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.IntEvaluator; 4 | import android.animation.PropertyValuesHolder; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import android.view.animation.AccelerateDecelerateInterpolator; 8 | import com.rd.animation.controller.ValueController; 9 | import com.rd.animation.data.type.SwapAnimationValue; 10 | 11 | public class SwapAnimation extends BaseAnimation { 12 | 13 | private static final String ANIMATION_COORDINATE = "ANIMATION_COORDINATE"; 14 | private static final String ANIMATION_COORDINATE_REVERSE = "ANIMATION_COORDINATE_REVERSE"; 15 | private static final int COORDINATE_NONE = -1; 16 | 17 | private int coordinateStart = COORDINATE_NONE; 18 | private int coordinateEnd = COORDINATE_NONE; 19 | 20 | private SwapAnimationValue value; 21 | 22 | public SwapAnimation(@NonNull ValueController.UpdateListener listener) { 23 | super(listener); 24 | value = new SwapAnimationValue(); 25 | } 26 | 27 | @NonNull 28 | @Override 29 | public ValueAnimator createAnimator() { 30 | ValueAnimator animator = new ValueAnimator(); 31 | animator.setDuration(BaseAnimation.DEFAULT_ANIMATION_TIME); 32 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 33 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 34 | @Override 35 | public void onAnimationUpdate(ValueAnimator animation) { 36 | onAnimateUpdated(animation); 37 | } 38 | }); 39 | 40 | return animator; 41 | } 42 | 43 | @Override 44 | public SwapAnimation progress(float progress) { 45 | if (animator != null) { 46 | long playTime = (long) (progress * animationDuration); 47 | 48 | if (animator.getValues() != null && animator.getValues().length > 0) { 49 | animator.setCurrentPlayTime(playTime); 50 | } 51 | } 52 | 53 | return this; 54 | } 55 | 56 | @NonNull 57 | public SwapAnimation with(int coordinateStart, int coordinateEnd) { 58 | if (animator != null && hasChanges(coordinateStart, coordinateEnd)) { 59 | this.coordinateStart = coordinateStart; 60 | this.coordinateEnd = coordinateEnd; 61 | 62 | PropertyValuesHolder holder = createColorPropertyHolder(ANIMATION_COORDINATE, coordinateStart, coordinateEnd); 63 | PropertyValuesHolder holderReverse = createColorPropertyHolder(ANIMATION_COORDINATE_REVERSE, coordinateEnd, coordinateStart); 64 | animator.setValues(holder, holderReverse); 65 | } 66 | 67 | return this; 68 | } 69 | 70 | private PropertyValuesHolder createColorPropertyHolder(String propertyName, int startValue, int endValue) { 71 | PropertyValuesHolder holder = PropertyValuesHolder.ofInt(propertyName, startValue, endValue); 72 | holder.setEvaluator(new IntEvaluator()); 73 | 74 | return holder; 75 | } 76 | 77 | private void onAnimateUpdated(@NonNull ValueAnimator animation) { 78 | int coordinate = (int) animation.getAnimatedValue(ANIMATION_COORDINATE); 79 | int coordinateReverse = (int) animation.getAnimatedValue(ANIMATION_COORDINATE_REVERSE); 80 | 81 | value.setCoordinate(coordinate); 82 | value.setCoordinateReverse(coordinateReverse); 83 | 84 | if (listener != null) { 85 | listener.onValueUpdated(value); 86 | } 87 | } 88 | 89 | @SuppressWarnings("RedundantIfStatement") 90 | private boolean hasChanges(int coordinateStart, int coordinateEnd) { 91 | if (this.coordinateStart != coordinateStart) { 92 | return true; 93 | } 94 | 95 | if (this.coordinateEnd != coordinateEnd) { 96 | return true; 97 | } 98 | 99 | return false; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/ThinWormAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.ValueAnimator; 4 | import androidx.annotation.NonNull; 5 | import android.view.animation.AccelerateDecelerateInterpolator; 6 | import com.rd.animation.controller.ValueController; 7 | import com.rd.animation.data.type.ThinWormAnimationValue; 8 | 9 | public class ThinWormAnimation extends WormAnimation { 10 | 11 | private ThinWormAnimationValue value; 12 | 13 | public ThinWormAnimation(@NonNull ValueController.UpdateListener listener) { 14 | super(listener); 15 | value = new ThinWormAnimationValue(); 16 | } 17 | 18 | @Override 19 | public ThinWormAnimation duration(long duration) { 20 | super.duration(duration); 21 | return this; 22 | } 23 | 24 | @Override 25 | public WormAnimation with(int coordinateStart, int coordinateEnd, int radius, boolean isRightSide) { 26 | if (hasChanges(coordinateStart, coordinateEnd, radius, isRightSide)) { 27 | animator = createAnimator(); 28 | 29 | this.coordinateStart = coordinateStart; 30 | this.coordinateEnd = coordinateEnd; 31 | 32 | this.radius = radius; 33 | this.isRightSide = isRightSide; 34 | 35 | int height = radius * 2; 36 | rectLeftEdge = coordinateStart - radius; 37 | rectRightEdge = coordinateStart + radius; 38 | 39 | value.setRectStart(rectLeftEdge); 40 | value.setRectEnd(rectRightEdge); 41 | value.setHeight(height); 42 | 43 | RectValues rec = createRectValues(isRightSide); 44 | long sizeDuration = (long) (animationDuration * 0.8); 45 | long reverseDelay = (long) (animationDuration * 0.2); 46 | 47 | long heightDuration = (long) (animationDuration * 0.5); 48 | long reverseHeightDelay = (long) (animationDuration * 0.5); 49 | 50 | ValueAnimator straightAnimator = createWormAnimator(rec.fromX, rec.toX, sizeDuration, false, value); 51 | ValueAnimator reverseAnimator = createWormAnimator(rec.reverseFromX, rec.reverseToX, sizeDuration, true, value); 52 | reverseAnimator.setStartDelay(reverseDelay); 53 | 54 | ValueAnimator straightHeightAnimator = createHeightAnimator(height, radius, heightDuration); 55 | ValueAnimator reverseHeightAnimator = createHeightAnimator(radius, height, heightDuration); 56 | reverseHeightAnimator.setStartDelay(reverseHeightDelay); 57 | 58 | animator.playTogether(straightAnimator, reverseAnimator, straightHeightAnimator, reverseHeightAnimator); 59 | } 60 | return this; 61 | } 62 | 63 | private ValueAnimator createHeightAnimator(int fromHeight, int toHeight, long duration) { 64 | ValueAnimator anim = ValueAnimator.ofInt(fromHeight, toHeight); 65 | anim.setInterpolator(new AccelerateDecelerateInterpolator()); 66 | anim.setDuration(duration); 67 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 68 | @Override 69 | public void onAnimationUpdate(ValueAnimator animation) { 70 | onAnimateUpdated(animation); 71 | } 72 | }); 73 | 74 | return anim; 75 | } 76 | 77 | private void onAnimateUpdated(@NonNull ValueAnimator animation) { 78 | value.setHeight((int) animation.getAnimatedValue()); 79 | 80 | if (listener != null) { 81 | listener.onValueUpdated(value); 82 | } 83 | } 84 | 85 | @Override 86 | public ThinWormAnimation progress(float progress) { 87 | if (animator != null) { 88 | long progressDuration = (long) (progress * animationDuration); 89 | int size = animator.getChildAnimations().size(); 90 | 91 | for (int i = 0; i < size; i++) { 92 | ValueAnimator anim = (ValueAnimator) animator.getChildAnimations().get(i); 93 | 94 | long setDuration = progressDuration - anim.getStartDelay(); 95 | long duration = anim.getDuration(); 96 | 97 | if (setDuration > duration) { 98 | setDuration = duration; 99 | 100 | } else if (setDuration < 0) { 101 | setDuration = 0; 102 | } 103 | 104 | if (i == size - 1 && setDuration <= 0) { 105 | continue; 106 | } 107 | 108 | if (anim.getValues() != null && anim.getValues().length > 0) { 109 | anim.setCurrentPlayTime(setDuration); 110 | } 111 | } 112 | } 113 | 114 | return this; 115 | } 116 | } -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/animation/type/WormAnimation.java: -------------------------------------------------------------------------------- 1 | package com.rd.animation.type; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorSet; 5 | import android.animation.ValueAnimator; 6 | import androidx.annotation.NonNull; 7 | import android.view.animation.AccelerateDecelerateInterpolator; 8 | import com.rd.animation.controller.ValueController; 9 | import com.rd.animation.data.type.WormAnimationValue; 10 | 11 | public class WormAnimation extends BaseAnimation { 12 | 13 | int coordinateStart; 14 | int coordinateEnd; 15 | 16 | int radius; 17 | boolean isRightSide; 18 | 19 | int rectLeftEdge; 20 | int rectRightEdge; 21 | 22 | private WormAnimationValue value; 23 | 24 | public WormAnimation(@NonNull ValueController.UpdateListener listener) { 25 | super(listener); 26 | value = new WormAnimationValue(); 27 | } 28 | 29 | @NonNull 30 | @Override 31 | public AnimatorSet createAnimator() { 32 | AnimatorSet animator = new AnimatorSet(); 33 | animator.setInterpolator(new AccelerateDecelerateInterpolator()); 34 | 35 | return animator; 36 | } 37 | 38 | @Override 39 | public WormAnimation duration(long duration) { 40 | super.duration(duration); 41 | return this; 42 | } 43 | 44 | public WormAnimation with(int coordinateStart, int coordinateEnd, int radius, boolean isRightSide) { 45 | if (hasChanges(coordinateStart, coordinateEnd, radius, isRightSide)) { 46 | animator = createAnimator(); 47 | 48 | this.coordinateStart = coordinateStart; 49 | this.coordinateEnd = coordinateEnd; 50 | 51 | this.radius = radius; 52 | this.isRightSide = isRightSide; 53 | 54 | rectLeftEdge = coordinateStart - radius; 55 | rectRightEdge = coordinateStart + radius; 56 | 57 | value.setRectStart(rectLeftEdge); 58 | value.setRectEnd(rectRightEdge); 59 | 60 | RectValues rect = createRectValues(isRightSide); 61 | long duration = animationDuration / 2; 62 | 63 | ValueAnimator straightAnimator = createWormAnimator(rect.fromX, rect.toX, duration, false, value); 64 | ValueAnimator reverseAnimator = createWormAnimator(rect.reverseFromX, rect.reverseToX, duration, true, value); 65 | animator.playSequentially(straightAnimator, reverseAnimator); 66 | } 67 | return this; 68 | } 69 | 70 | @Override 71 | public WormAnimation progress(float progress) { 72 | if (animator == null) { 73 | return this; 74 | } 75 | 76 | long progressDuration = (long) (progress * animationDuration); 77 | for (Animator anim : animator.getChildAnimations()) { 78 | ValueAnimator animator = (ValueAnimator) anim; 79 | long duration = animator.getDuration(); 80 | long setDuration = progressDuration; 81 | 82 | if (setDuration > duration) { 83 | setDuration = duration; 84 | } 85 | 86 | animator.setCurrentPlayTime(setDuration); 87 | progressDuration -= setDuration; 88 | } 89 | 90 | return this; 91 | } 92 | 93 | ValueAnimator createWormAnimator( 94 | int fromValue, 95 | int toValue, 96 | long duration, 97 | final boolean isReverse, 98 | final WormAnimationValue value) { 99 | 100 | ValueAnimator anim = ValueAnimator.ofInt(fromValue, toValue); 101 | anim.setInterpolator(new AccelerateDecelerateInterpolator()); 102 | anim.setDuration(duration); 103 | anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 104 | @Override 105 | public void onAnimationUpdate(ValueAnimator animation) { 106 | onAnimateUpdated(value, animation, isReverse); 107 | } 108 | }); 109 | 110 | return anim; 111 | } 112 | 113 | private void onAnimateUpdated(@NonNull WormAnimationValue value, @NonNull ValueAnimator animation, final boolean isReverse) { 114 | int rectEdge = (int) animation.getAnimatedValue(); 115 | 116 | if (isRightSide) { 117 | if (!isReverse) { 118 | value.setRectEnd(rectEdge); 119 | } else { 120 | value.setRectStart(rectEdge); 121 | } 122 | 123 | } else { 124 | if (!isReverse) { 125 | value.setRectStart(rectEdge); 126 | } else { 127 | value.setRectEnd(rectEdge); 128 | } 129 | } 130 | 131 | if (listener != null) { 132 | listener.onValueUpdated(value); 133 | } 134 | } 135 | 136 | @SuppressWarnings("RedundantIfStatement") 137 | boolean hasChanges(int coordinateStart, int coordinateEnd, int radius, boolean isRightSide) { 138 | if (this.coordinateStart != coordinateStart) { 139 | return true; 140 | } 141 | 142 | if (this.coordinateEnd != coordinateEnd) { 143 | return true; 144 | } 145 | 146 | if (this.radius != radius) { 147 | return true; 148 | } 149 | 150 | if (this.isRightSide != isRightSide) { 151 | return true; 152 | } 153 | 154 | return false; 155 | } 156 | 157 | @NonNull 158 | RectValues createRectValues(boolean isRightSide) { 159 | int fromX; 160 | int toX; 161 | 162 | int reverseFromX; 163 | int reverseToX; 164 | 165 | if (isRightSide) { 166 | fromX = coordinateStart + radius; 167 | toX = coordinateEnd + radius; 168 | 169 | reverseFromX = coordinateStart - radius; 170 | reverseToX = coordinateEnd - radius; 171 | 172 | } else { 173 | fromX = coordinateStart - radius; 174 | toX = coordinateEnd - radius; 175 | 176 | reverseFromX = coordinateStart + radius; 177 | reverseToX = coordinateEnd + radius; 178 | } 179 | 180 | return new RectValues(fromX, toX, reverseFromX, reverseToX); 181 | } 182 | 183 | class RectValues { 184 | 185 | final int fromX; 186 | final int toX; 187 | 188 | final int reverseFromX; 189 | final int reverseToX; 190 | 191 | RectValues(int fromX, int toX, int reverseFromX, int reverseToX) { 192 | this.fromX = fromX; 193 | this.toX = toX; 194 | 195 | this.reverseFromX = reverseFromX; 196 | this.reverseToX = reverseToX; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/DrawManager.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | import android.util.AttributeSet; 8 | import android.util.Pair; 9 | import android.view.MotionEvent; 10 | import com.rd.animation.data.Value; 11 | import com.rd.draw.controller.AttributeController; 12 | import com.rd.draw.controller.DrawController; 13 | import com.rd.draw.controller.MeasureController; 14 | import com.rd.draw.data.Indicator; 15 | 16 | public class DrawManager { 17 | 18 | private Indicator indicator; 19 | private DrawController drawController; 20 | private MeasureController measureController; 21 | private AttributeController attributeController; 22 | 23 | public DrawManager() { 24 | this.indicator = new Indicator(); 25 | this.drawController = new DrawController(indicator); 26 | this.measureController = new MeasureController(); 27 | this.attributeController = new AttributeController(indicator); 28 | } 29 | 30 | @NonNull 31 | public Indicator indicator() { 32 | if (indicator == null) { 33 | indicator = new Indicator(); 34 | } 35 | 36 | return indicator; 37 | } 38 | 39 | public void setClickListener(@Nullable DrawController.ClickListener listener) { 40 | drawController.setClickListener(listener); 41 | } 42 | 43 | public void touch(@Nullable MotionEvent event) { 44 | drawController.touch(event); 45 | } 46 | 47 | public void updateValue(@Nullable Value value) { 48 | drawController.updateValue(value); 49 | } 50 | 51 | public void draw(@NonNull Canvas canvas) { 52 | drawController.draw(canvas); 53 | } 54 | 55 | public Pair measureViewSize(int widthMeasureSpec, int heightMeasureSpec) { 56 | return measureController.measureViewSize(indicator, widthMeasureSpec, heightMeasureSpec); 57 | } 58 | 59 | public void initAttributes(@NonNull Context context, @Nullable AttributeSet attrs) { 60 | attributeController.init(context, attrs); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/controller/AttributeController.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.controller; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | 11 | import com.rd.animation.type.AnimationType; 12 | import com.rd.animation.type.BaseAnimation; 13 | import com.rd.animation.type.ColorAnimation; 14 | import com.rd.animation.type.FillAnimation; 15 | import com.rd.animation.type.ScaleAnimation; 16 | import com.rd.draw.data.Indicator; 17 | import com.rd.draw.data.Orientation; 18 | import com.rd.draw.data.RtlMode; 19 | import com.rd.pageindicatorview.R; 20 | import com.rd.utils.DensityUtils; 21 | 22 | public class AttributeController { 23 | 24 | private Indicator indicator; 25 | 26 | private static final int DEFAULT_IDLE_DURATION = 3000; 27 | 28 | public AttributeController(@NonNull Indicator indicator) { 29 | this.indicator = indicator; 30 | } 31 | 32 | public void init(@NonNull Context context, @Nullable AttributeSet attrs) { 33 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PageIndicatorView, 0, 0); 34 | initCountAttribute(typedArray); 35 | initColorAttribute(typedArray); 36 | initAnimationAttribute(typedArray); 37 | initSizeAttribute(typedArray); 38 | typedArray.recycle(); 39 | } 40 | 41 | private void initCountAttribute(@NonNull TypedArray typedArray) { 42 | int viewPagerId = typedArray.getResourceId(R.styleable.PageIndicatorView_piv_viewPager, View.NO_ID); 43 | boolean autoVisibility = typedArray.getBoolean(R.styleable.PageIndicatorView_piv_autoVisibility, true); 44 | boolean dynamicCount = typedArray.getBoolean(R.styleable.PageIndicatorView_piv_dynamicCount, false); 45 | int count = typedArray.getInt(R.styleable.PageIndicatorView_piv_count, Indicator.COUNT_NONE); 46 | 47 | if (count == Indicator.COUNT_NONE) { 48 | count = Indicator.DEFAULT_COUNT; 49 | } 50 | 51 | int position = typedArray.getInt(R.styleable.PageIndicatorView_piv_select, 0); 52 | if (position < 0) { 53 | position = 0; 54 | } else if (count > 0 && position > count - 1) { 55 | position = count - 1; 56 | } 57 | 58 | indicator.setViewPagerId(viewPagerId); 59 | indicator.setAutoVisibility(autoVisibility); 60 | indicator.setDynamicCount(dynamicCount); 61 | indicator.setCount(count); 62 | 63 | indicator.setSelectedPosition(position); 64 | indicator.setSelectingPosition(position); 65 | indicator.setLastSelectedPosition(position); 66 | } 67 | 68 | private void initColorAttribute(@NonNull TypedArray typedArray) { 69 | int unselectedColor = typedArray.getColor(R.styleable.PageIndicatorView_piv_unselectedColor, Color.parseColor(ColorAnimation.DEFAULT_UNSELECTED_COLOR)); 70 | int selectedColor = typedArray.getColor(R.styleable.PageIndicatorView_piv_selectedColor, Color.parseColor(ColorAnimation.DEFAULT_SELECTED_COLOR)); 71 | 72 | indicator.setUnselectedColor(unselectedColor); 73 | indicator.setSelectedColor(selectedColor); 74 | } 75 | 76 | private void initAnimationAttribute(@NonNull TypedArray typedArray) { 77 | boolean interactiveAnimation = typedArray.getBoolean(R.styleable.PageIndicatorView_piv_interactiveAnimation, false); 78 | long animationDuration = (long) typedArray.getInt(R.styleable.PageIndicatorView_piv_animationDuration, BaseAnimation.DEFAULT_ANIMATION_TIME); 79 | if (animationDuration < 0) { 80 | animationDuration = 0; 81 | } 82 | 83 | int animIndex = typedArray.getInt(R.styleable.PageIndicatorView_piv_animationType, AnimationType.NONE.ordinal()); 84 | AnimationType animationType = getAnimationType(animIndex); 85 | 86 | int rtlIndex = typedArray.getInt(R.styleable.PageIndicatorView_piv_rtl_mode, RtlMode.Off.ordinal()); 87 | RtlMode rtlMode = getRtlMode(rtlIndex); 88 | 89 | boolean fadeOnIdle = typedArray.getBoolean(R.styleable.PageIndicatorView_piv_fadeOnIdle, false); 90 | long idleDuration = (long) typedArray.getInt(R.styleable.PageIndicatorView_piv_idleDuration, DEFAULT_IDLE_DURATION); 91 | 92 | indicator.setAnimationDuration(animationDuration); 93 | indicator.setInteractiveAnimation(interactiveAnimation); 94 | indicator.setAnimationType(animationType); 95 | indicator.setRtlMode(rtlMode); 96 | indicator.setFadeOnIdle(fadeOnIdle); 97 | indicator.setIdleDuration(idleDuration); 98 | } 99 | 100 | private void initSizeAttribute(@NonNull TypedArray typedArray) { 101 | int orientationIndex = typedArray.getInt(R.styleable.PageIndicatorView_piv_orientation, Orientation.HORIZONTAL.ordinal()); 102 | Orientation orientation; 103 | 104 | if (orientationIndex == 0) { 105 | orientation = Orientation.HORIZONTAL; 106 | } else { 107 | orientation = Orientation.VERTICAL; 108 | } 109 | 110 | int radius = (int) typedArray.getDimension(R.styleable.PageIndicatorView_piv_radius, DensityUtils.dpToPx(Indicator.DEFAULT_RADIUS_DP)); 111 | if (radius < 0) { 112 | radius = 0; 113 | } 114 | 115 | int padding = (int) typedArray.getDimension(R.styleable.PageIndicatorView_piv_padding, DensityUtils.dpToPx(Indicator.DEFAULT_PADDING_DP)); 116 | if (padding < 0) { 117 | padding = 0; 118 | } 119 | 120 | float scaleFactor = typedArray.getFloat(R.styleable.PageIndicatorView_piv_scaleFactor, ScaleAnimation.DEFAULT_SCALE_FACTOR); 121 | if (scaleFactor < ScaleAnimation.MIN_SCALE_FACTOR) { 122 | scaleFactor = ScaleAnimation.MIN_SCALE_FACTOR; 123 | 124 | } else if (scaleFactor > ScaleAnimation.MAX_SCALE_FACTOR) { 125 | scaleFactor = ScaleAnimation.MAX_SCALE_FACTOR; 126 | } 127 | 128 | int stroke = (int) typedArray.getDimension(R.styleable.PageIndicatorView_piv_strokeWidth, DensityUtils.dpToPx(FillAnimation.DEFAULT_STROKE_DP)); 129 | if (stroke > radius) { 130 | stroke = radius; 131 | } 132 | 133 | if (indicator.getAnimationType() != AnimationType.FILL) { 134 | stroke = 0; 135 | } 136 | 137 | indicator.setRadius(radius); 138 | indicator.setOrientation(orientation); 139 | indicator.setPadding(padding); 140 | indicator.setScaleFactor(scaleFactor); 141 | indicator.setStroke(stroke); 142 | } 143 | 144 | private AnimationType getAnimationType(int index) { 145 | switch (index) { 146 | case 0: 147 | return AnimationType.NONE; 148 | case 1: 149 | return AnimationType.COLOR; 150 | case 2: 151 | return AnimationType.SCALE; 152 | case 3: 153 | return AnimationType.WORM; 154 | case 4: 155 | return AnimationType.SLIDE; 156 | case 5: 157 | return AnimationType.FILL; 158 | case 6: 159 | return AnimationType.THIN_WORM; 160 | case 7: 161 | return AnimationType.DROP; 162 | case 8: 163 | return AnimationType.SWAP; 164 | case 9: 165 | return AnimationType.SCALE_DOWN; 166 | } 167 | 168 | return AnimationType.NONE; 169 | } 170 | 171 | private RtlMode getRtlMode(int index) { 172 | switch (index) { 173 | case 0: 174 | return RtlMode.On; 175 | case 1: 176 | return RtlMode.Off; 177 | case 2: 178 | return RtlMode.Auto; 179 | } 180 | 181 | return RtlMode.Auto; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/controller/DrawController.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.controller; 2 | 3 | import android.graphics.Canvas; 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | import android.view.MotionEvent; 7 | import com.rd.animation.data.Value; 8 | import com.rd.animation.type.AnimationType; 9 | import com.rd.draw.data.Indicator; 10 | import com.rd.draw.drawer.Drawer; 11 | import com.rd.utils.CoordinatesUtils; 12 | 13 | public class DrawController { 14 | 15 | private Value value; 16 | private Drawer drawer; 17 | private Indicator indicator; 18 | private ClickListener listener; 19 | 20 | public interface ClickListener { 21 | 22 | void onIndicatorClicked(int position); 23 | } 24 | 25 | public DrawController(@NonNull Indicator indicator) { 26 | this.indicator = indicator; 27 | this.drawer = new Drawer(indicator); 28 | } 29 | 30 | public void updateValue(@Nullable Value value) { 31 | this.value = value; 32 | } 33 | 34 | public void setClickListener(@Nullable ClickListener listener) { 35 | this.listener = listener; 36 | } 37 | 38 | public void touch(@Nullable MotionEvent event) { 39 | if (event == null) { 40 | return; 41 | } 42 | 43 | switch (event.getAction()) { 44 | case MotionEvent.ACTION_UP: 45 | onIndicatorTouched(event.getX(), event.getY()); 46 | break; 47 | default: 48 | } 49 | } 50 | 51 | private void onIndicatorTouched(float x, float y) { 52 | if (listener != null) { 53 | int position = CoordinatesUtils.getPosition(indicator, x, y); 54 | if (position >= 0) { 55 | listener.onIndicatorClicked(position); 56 | } 57 | } 58 | } 59 | 60 | public void draw(@NonNull Canvas canvas) { 61 | int count = indicator.getCount(); 62 | 63 | for (int position = 0; position < count; position++) { 64 | int coordinateX = CoordinatesUtils.getXCoordinate(indicator, position); 65 | int coordinateY = CoordinatesUtils.getYCoordinate(indicator, position); 66 | drawIndicator(canvas, position, coordinateX, coordinateY); 67 | } 68 | } 69 | 70 | private void drawIndicator( 71 | @NonNull Canvas canvas, 72 | int position, 73 | int coordinateX, 74 | int coordinateY) { 75 | 76 | boolean interactiveAnimation = indicator.isInteractiveAnimation(); 77 | int selectedPosition = indicator.getSelectedPosition(); 78 | int selectingPosition = indicator.getSelectingPosition(); 79 | int lastSelectedPosition = indicator.getLastSelectedPosition(); 80 | 81 | boolean selectedItem = !interactiveAnimation && (position == selectedPosition || position == lastSelectedPosition); 82 | boolean selectingItem = interactiveAnimation && (position == selectedPosition || position == selectingPosition); 83 | boolean isSelectedItem = selectedItem | selectingItem; 84 | drawer.setup(position, coordinateX, coordinateY); 85 | 86 | if (value != null && isSelectedItem) { 87 | drawWithAnimation(canvas); 88 | } else { 89 | drawer.drawBasic(canvas, isSelectedItem); 90 | } 91 | } 92 | 93 | private void drawWithAnimation(@NonNull Canvas canvas) { 94 | AnimationType animationType = indicator.getAnimationType(); 95 | switch (animationType) { 96 | case NONE: 97 | drawer.drawBasic(canvas, true); 98 | break; 99 | 100 | case COLOR: 101 | drawer.drawColor(canvas, value); 102 | break; 103 | 104 | case SCALE: 105 | drawer.drawScale(canvas, value); 106 | break; 107 | 108 | case WORM: 109 | drawer.drawWorm(canvas, value); 110 | break; 111 | 112 | case SLIDE: 113 | drawer.drawSlide(canvas, value); 114 | break; 115 | 116 | case FILL: 117 | drawer.drawFill(canvas, value); 118 | break; 119 | 120 | case THIN_WORM: 121 | drawer.drawThinWorm(canvas, value); 122 | break; 123 | 124 | case DROP: 125 | drawer.drawDrop(canvas, value); 126 | break; 127 | 128 | case SWAP: 129 | drawer.drawSwap(canvas, value); 130 | break; 131 | 132 | case SCALE_DOWN: 133 | drawer.drawScaleDown(canvas, value); 134 | break; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/controller/MeasureController.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.controller; 2 | 3 | import androidx.annotation.NonNull; 4 | import android.util.Pair; 5 | import android.view.View; 6 | import com.rd.animation.type.AnimationType; 7 | import com.rd.draw.data.Indicator; 8 | import com.rd.draw.data.Orientation; 9 | 10 | public class MeasureController { 11 | 12 | public Pair measureViewSize(@NonNull Indicator indicator, int widthMeasureSpec, int heightMeasureSpec) { 13 | int widthMode = View.MeasureSpec.getMode(widthMeasureSpec); 14 | int widthSize = View.MeasureSpec.getSize(widthMeasureSpec); 15 | 16 | int heightMode = View.MeasureSpec.getMode(heightMeasureSpec); 17 | int heightSize = View.MeasureSpec.getSize(heightMeasureSpec); 18 | 19 | int count = indicator.getCount(); 20 | int radius = indicator.getRadius(); 21 | int stroke = indicator.getStroke(); 22 | 23 | int padding = indicator.getPadding(); 24 | int paddingLeft = indicator.getPaddingLeft(); 25 | int paddingTop = indicator.getPaddingTop(); 26 | int paddingRight = indicator.getPaddingRight(); 27 | int paddingBottom = indicator.getPaddingBottom(); 28 | 29 | int circleDiameterPx = radius * 2; 30 | int desiredWidth = 0; 31 | int desiredHeight = 0; 32 | 33 | int width; 34 | int height; 35 | 36 | Orientation orientation = indicator.getOrientation(); 37 | if (count != 0) { 38 | int diameterSum = circleDiameterPx * count; 39 | int strokeSum = (stroke * 2) * count; 40 | 41 | int paddingSum = padding * (count - 1); 42 | int w = diameterSum + strokeSum + paddingSum; 43 | int h = circleDiameterPx + stroke; 44 | 45 | if (orientation == Orientation.HORIZONTAL) { 46 | desiredWidth = w; 47 | desiredHeight = h; 48 | 49 | } else { 50 | desiredWidth = h; 51 | desiredHeight = w; 52 | } 53 | } 54 | 55 | if (indicator.getAnimationType() == AnimationType.DROP) { 56 | if (orientation == Orientation.HORIZONTAL) { 57 | desiredHeight *= 2; 58 | } else { 59 | desiredWidth *= 2; 60 | } 61 | } 62 | 63 | int horizontalPadding = paddingLeft + paddingRight; 64 | int verticalPadding = paddingTop + paddingBottom; 65 | 66 | if (orientation == Orientation.HORIZONTAL) { 67 | desiredWidth += horizontalPadding; 68 | desiredHeight += verticalPadding; 69 | 70 | } else { 71 | desiredWidth += horizontalPadding; 72 | desiredHeight += verticalPadding; 73 | } 74 | 75 | if (widthMode == View.MeasureSpec.EXACTLY) { 76 | width = widthSize; 77 | } else if (widthMode == View.MeasureSpec.AT_MOST) { 78 | width = Math.min(desiredWidth, widthSize); 79 | } else { 80 | width = desiredWidth; 81 | } 82 | 83 | if (heightMode == View.MeasureSpec.EXACTLY) { 84 | height = heightSize; 85 | } else if (heightMode == View.MeasureSpec.AT_MOST) { 86 | height = Math.min(desiredHeight, heightSize); 87 | } else { 88 | height = desiredHeight; 89 | } 90 | 91 | if (width < 0) { 92 | width = 0; 93 | } 94 | 95 | if (height < 0) { 96 | height = 0; 97 | } 98 | 99 | indicator.setWidth(width); 100 | indicator.setHeight(height); 101 | 102 | return new Pair<>(width, height); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/data/Indicator.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.data; 2 | 3 | import androidx.annotation.NonNull; 4 | import android.view.View; 5 | import com.rd.animation.type.AnimationType; 6 | 7 | public class Indicator { 8 | 9 | public static final int DEFAULT_COUNT = 3; 10 | public static final int MIN_COUNT = 1; 11 | public static final int COUNT_NONE = -1; 12 | 13 | public static final int DEFAULT_RADIUS_DP = 6; 14 | public static final int DEFAULT_PADDING_DP = 8; 15 | public static final int IDLE_ANIMATION_DURATION = 250; 16 | 17 | private int height; 18 | private int width; 19 | private int radius; 20 | 21 | private int padding; 22 | private int paddingLeft; 23 | private int paddingTop; 24 | private int paddingRight; 25 | private int paddingBottom; 26 | 27 | private int stroke; //For "Fill" animation only 28 | private float scaleFactor; //For "Scale" animation only 29 | 30 | private int unselectedColor; 31 | private int selectedColor; 32 | 33 | private boolean interactiveAnimation; 34 | private boolean autoVisibility; 35 | private boolean dynamicCount; 36 | 37 | private boolean fadeOnIdle; 38 | private boolean isIdle; 39 | private long idleDuration; 40 | 41 | private long animationDuration; 42 | private int count = DEFAULT_COUNT; 43 | 44 | private int selectedPosition; 45 | private int selectingPosition; 46 | private int lastSelectedPosition; 47 | 48 | private int viewPagerId = View.NO_ID; 49 | 50 | private Orientation orientation; 51 | private AnimationType animationType; 52 | private RtlMode rtlMode; 53 | 54 | public int getHeight() { 55 | return height; 56 | } 57 | 58 | public void setHeight(int height) { 59 | this.height = height; 60 | } 61 | 62 | public int getWidth() { 63 | return width; 64 | } 65 | 66 | public void setWidth(int width) { 67 | this.width = width; 68 | } 69 | 70 | public int getRadius() { 71 | return radius; 72 | } 73 | 74 | public void setRadius(int radius) { 75 | this.radius = radius; 76 | } 77 | 78 | public int getPadding() { 79 | return padding; 80 | } 81 | 82 | public void setPadding(int padding) { 83 | this.padding = padding; 84 | } 85 | 86 | public int getPaddingLeft() { 87 | return paddingLeft; 88 | } 89 | 90 | public void setPaddingLeft(int paddingLeft) { 91 | this.paddingLeft = paddingLeft; 92 | } 93 | 94 | public int getPaddingTop() { 95 | return paddingTop; 96 | } 97 | 98 | public void setPaddingTop(int paddingTop) { 99 | this.paddingTop = paddingTop; 100 | } 101 | 102 | public int getPaddingRight() { 103 | return paddingRight; 104 | } 105 | 106 | public void setPaddingRight(int paddingRight) { 107 | this.paddingRight = paddingRight; 108 | } 109 | 110 | public int getPaddingBottom() { 111 | return paddingBottom; 112 | } 113 | 114 | public void setPaddingBottom(int paddingBottom) { 115 | this.paddingBottom = paddingBottom; 116 | } 117 | 118 | public int getStroke() { 119 | return stroke; 120 | } 121 | 122 | public void setStroke(int stroke) { 123 | this.stroke = stroke; 124 | } 125 | 126 | public float getScaleFactor() { 127 | return scaleFactor; 128 | } 129 | 130 | public void setScaleFactor(float scaleFactor) { 131 | this.scaleFactor = scaleFactor; 132 | } 133 | 134 | public int getUnselectedColor() { 135 | return unselectedColor; 136 | } 137 | 138 | public void setUnselectedColor(int unselectedColor) { 139 | this.unselectedColor = unselectedColor; 140 | } 141 | 142 | public int getSelectedColor() { 143 | return selectedColor; 144 | } 145 | 146 | public void setSelectedColor(int selectedColor) { 147 | this.selectedColor = selectedColor; 148 | } 149 | 150 | public boolean isInteractiveAnimation() { 151 | return interactiveAnimation; 152 | } 153 | 154 | public void setInteractiveAnimation(boolean interactiveAnimation) { 155 | this.interactiveAnimation = interactiveAnimation; 156 | } 157 | 158 | public boolean isAutoVisibility() { 159 | return autoVisibility; 160 | } 161 | 162 | public void setAutoVisibility(boolean autoVisibility) { 163 | this.autoVisibility = autoVisibility; 164 | } 165 | 166 | public boolean isDynamicCount() { 167 | return dynamicCount; 168 | } 169 | 170 | public void setDynamicCount(boolean dynamicCount) { 171 | this.dynamicCount = dynamicCount; 172 | } 173 | 174 | public boolean isFadeOnIdle() { 175 | return fadeOnIdle; 176 | } 177 | 178 | public void setFadeOnIdle(boolean fadeOnIdle) { 179 | this.fadeOnIdle = fadeOnIdle; 180 | } 181 | 182 | public boolean isIdle() { 183 | return isIdle; 184 | } 185 | 186 | public void setIdle(boolean idle) { 187 | isIdle = idle; 188 | } 189 | 190 | public long getIdleDuration() { 191 | return idleDuration; 192 | } 193 | 194 | public void setIdleDuration(long idleDuration) { 195 | this.idleDuration = idleDuration; 196 | } 197 | 198 | public long getAnimationDuration() { 199 | return animationDuration; 200 | } 201 | 202 | public void setAnimationDuration(long animationDuration) { 203 | this.animationDuration = animationDuration; 204 | } 205 | 206 | public int getCount() { 207 | return count; 208 | } 209 | 210 | public void setCount(int count) { 211 | this.count = count; 212 | } 213 | 214 | public int getSelectedPosition() { 215 | return selectedPosition; 216 | } 217 | 218 | public void setSelectedPosition(int selectedPosition) { 219 | this.selectedPosition = selectedPosition; 220 | } 221 | 222 | public int getSelectingPosition() { 223 | return selectingPosition; 224 | } 225 | 226 | public void setSelectingPosition(int selectingPosition) { 227 | this.selectingPosition = selectingPosition; 228 | } 229 | 230 | public int getLastSelectedPosition() { 231 | return lastSelectedPosition; 232 | } 233 | 234 | public void setLastSelectedPosition(int lastSelectedPosition) { 235 | this.lastSelectedPosition = lastSelectedPosition; 236 | } 237 | 238 | public int getViewPagerId() { 239 | return viewPagerId; 240 | } 241 | 242 | public void setViewPagerId(int viewPagerId) { 243 | this.viewPagerId = viewPagerId; 244 | } 245 | 246 | @NonNull 247 | public Orientation getOrientation() { 248 | if (orientation == null) { 249 | orientation = Orientation.HORIZONTAL; 250 | } 251 | return orientation; 252 | } 253 | 254 | public void setOrientation(Orientation orientation) { 255 | this.orientation = orientation; 256 | } 257 | 258 | @NonNull 259 | public AnimationType getAnimationType() { 260 | if (animationType == null) { 261 | animationType = AnimationType.NONE; 262 | } 263 | return animationType; 264 | } 265 | 266 | public void setAnimationType(AnimationType animationType) { 267 | this.animationType = animationType; 268 | } 269 | 270 | @NonNull 271 | public RtlMode getRtlMode() { 272 | if (rtlMode == null) { 273 | rtlMode = RtlMode.Off; 274 | } 275 | return rtlMode; 276 | } 277 | 278 | public void setRtlMode(RtlMode rtlMode) { 279 | this.rtlMode = rtlMode; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/data/Orientation.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.data; 2 | 3 | public enum Orientation {HORIZONTAL, VERTICAL} 4 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/data/PositionSavedState.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.data; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.view.View; 6 | 7 | public class PositionSavedState extends View.BaseSavedState { 8 | 9 | private int selectedPosition; 10 | private int selectingPosition; 11 | private int lastSelectedPosition; 12 | 13 | public PositionSavedState(Parcelable superState) { 14 | super(superState); 15 | } 16 | 17 | private PositionSavedState(Parcel in) { 18 | super(in); 19 | this.selectedPosition = in.readInt(); 20 | this.selectingPosition = in.readInt(); 21 | this.lastSelectedPosition = in.readInt(); 22 | } 23 | 24 | public int getSelectedPosition() { 25 | return selectedPosition; 26 | } 27 | 28 | public void setSelectedPosition(int selectedPosition) { 29 | this.selectedPosition = selectedPosition; 30 | } 31 | 32 | public int getSelectingPosition() { 33 | return selectingPosition; 34 | } 35 | 36 | public void setSelectingPosition(int selectingPosition) { 37 | this.selectingPosition = selectingPosition; 38 | } 39 | 40 | public int getLastSelectedPosition() { 41 | return lastSelectedPosition; 42 | } 43 | 44 | public void setLastSelectedPosition(int lastSelectedPosition) { 45 | this.lastSelectedPosition = lastSelectedPosition; 46 | } 47 | 48 | @Override 49 | public void writeToParcel(Parcel out, int flags) { 50 | super.writeToParcel(out, flags); 51 | out.writeInt(this.selectedPosition); 52 | out.writeInt(this.selectingPosition); 53 | out.writeInt(this.lastSelectedPosition); 54 | } 55 | 56 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 57 | public PositionSavedState createFromParcel(Parcel in) { 58 | return new PositionSavedState(in); 59 | } 60 | 61 | public PositionSavedState[] newArray(int size) { 62 | return new PositionSavedState[size]; 63 | } 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/data/RtlMode.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.data; 2 | 3 | public enum RtlMode {On, Off, Auto} -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/Drawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.draw.data.Indicator; 8 | import com.rd.draw.drawer.type.*; 9 | 10 | public class Drawer { 11 | 12 | private BasicDrawer basicDrawer; 13 | private ColorDrawer colorDrawer; 14 | private ScaleDrawer scaleDrawer; 15 | private WormDrawer wormDrawer; 16 | private SlideDrawer slideDrawer; 17 | private FillDrawer fillDrawer; 18 | private ThinWormDrawer thinWormDrawer; 19 | private DropDrawer dropDrawer; 20 | private SwapDrawer swapDrawer; 21 | private ScaleDownDrawer scaleDownDrawer; 22 | 23 | private int position; 24 | private int coordinateX; 25 | private int coordinateY; 26 | 27 | public Drawer(@NonNull Indicator indicator) { 28 | Paint paint = new Paint(); 29 | paint.setStyle(Paint.Style.FILL); 30 | paint.setAntiAlias(true); 31 | 32 | basicDrawer = new BasicDrawer(paint, indicator); 33 | colorDrawer = new ColorDrawer(paint, indicator); 34 | scaleDrawer = new ScaleDrawer(paint, indicator); 35 | wormDrawer = new WormDrawer(paint, indicator); 36 | slideDrawer = new SlideDrawer(paint, indicator); 37 | fillDrawer = new FillDrawer(paint, indicator); 38 | thinWormDrawer = new ThinWormDrawer(paint, indicator); 39 | dropDrawer = new DropDrawer(paint, indicator); 40 | swapDrawer = new SwapDrawer(paint, indicator); 41 | scaleDownDrawer = new ScaleDownDrawer(paint, indicator); 42 | } 43 | 44 | public void setup(int position, int coordinateX, int coordinateY) { 45 | this.position = position; 46 | this.coordinateX = coordinateX; 47 | this.coordinateY = coordinateY; 48 | } 49 | 50 | public void drawBasic(@NonNull Canvas canvas, boolean isSelectedItem) { 51 | if (colorDrawer != null) { 52 | basicDrawer.draw(canvas, position, isSelectedItem, coordinateX, coordinateY); 53 | } 54 | } 55 | 56 | public void drawColor(@NonNull Canvas canvas, @NonNull Value value) { 57 | if (colorDrawer != null) { 58 | colorDrawer.draw(canvas, value, position, coordinateX, coordinateY); 59 | } 60 | } 61 | 62 | public void drawScale(@NonNull Canvas canvas, @NonNull Value value) { 63 | if (scaleDrawer != null) { 64 | scaleDrawer.draw(canvas, value, position, coordinateX, coordinateY); 65 | } 66 | } 67 | 68 | public void drawWorm(@NonNull Canvas canvas, @NonNull Value value) { 69 | if (wormDrawer != null) { 70 | wormDrawer.draw(canvas, value, coordinateX, coordinateY); 71 | } 72 | } 73 | 74 | public void drawSlide(@NonNull Canvas canvas, @NonNull Value value) { 75 | if (slideDrawer != null) { 76 | slideDrawer.draw(canvas, value, coordinateX, coordinateY); 77 | } 78 | } 79 | 80 | public void drawFill(@NonNull Canvas canvas, @NonNull Value value) { 81 | if (fillDrawer != null) { 82 | fillDrawer.draw(canvas, value, position, coordinateX, coordinateY); 83 | } 84 | } 85 | 86 | public void drawThinWorm(@NonNull Canvas canvas, @NonNull Value value) { 87 | if (thinWormDrawer != null) { 88 | thinWormDrawer.draw(canvas, value, coordinateX, coordinateY); 89 | } 90 | } 91 | 92 | public void drawDrop(@NonNull Canvas canvas, @NonNull Value value) { 93 | if (dropDrawer != null) { 94 | dropDrawer.draw(canvas, value, coordinateX, coordinateY); 95 | } 96 | } 97 | 98 | public void drawSwap(@NonNull Canvas canvas, @NonNull Value value) { 99 | if (swapDrawer != null) { 100 | swapDrawer.draw(canvas, value, position, coordinateX, coordinateY); 101 | } 102 | } 103 | 104 | public void drawScaleDown(@NonNull Canvas canvas, @NonNull Value value) { 105 | if (scaleDownDrawer != null) { 106 | scaleDownDrawer.draw(canvas, value, position, coordinateX, coordinateY); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/BaseDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Paint; 4 | import androidx.annotation.NonNull; 5 | import com.rd.draw.data.Indicator; 6 | 7 | class BaseDrawer { 8 | 9 | Paint paint; 10 | Indicator indicator; 11 | 12 | BaseDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 13 | this.paint = paint; 14 | this.indicator = indicator; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/BasicDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.type.AnimationType; 7 | import com.rd.draw.data.Indicator; 8 | 9 | public class BasicDrawer extends BaseDrawer { 10 | 11 | private Paint strokePaint; 12 | 13 | public BasicDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 14 | super(paint, indicator); 15 | 16 | strokePaint = new Paint(); 17 | strokePaint.setStyle(Paint.Style.STROKE); 18 | strokePaint.setAntiAlias(true); 19 | strokePaint.setStrokeWidth(indicator.getStroke()); 20 | } 21 | 22 | public void draw( 23 | @NonNull Canvas canvas, 24 | int position, 25 | boolean isSelectedItem, 26 | int coordinateX, 27 | int coordinateY) { 28 | 29 | float radius = indicator.getRadius(); 30 | int strokePx = indicator.getStroke(); 31 | float scaleFactor = indicator.getScaleFactor(); 32 | 33 | int selectedColor = indicator.getSelectedColor(); 34 | int unselectedColor = indicator.getUnselectedColor(); 35 | int selectedPosition = indicator.getSelectedPosition(); 36 | AnimationType animationType = indicator.getAnimationType(); 37 | 38 | if (animationType == AnimationType.SCALE && !isSelectedItem) { 39 | radius *= scaleFactor; 40 | 41 | } else if (animationType == AnimationType.SCALE_DOWN && isSelectedItem) { 42 | radius *= scaleFactor; 43 | } 44 | 45 | int color = unselectedColor; 46 | if (position == selectedPosition) { 47 | color = selectedColor; 48 | } 49 | 50 | Paint paint; 51 | if (animationType == AnimationType.FILL && position != selectedPosition) { 52 | paint = strokePaint; 53 | paint.setStrokeWidth(strokePx); 54 | } else { 55 | paint = this.paint; 56 | } 57 | 58 | paint.setColor(color); 59 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/ColorDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.ColorAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | 10 | public class ColorDrawer extends BaseDrawer { 11 | 12 | public ColorDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 13 | super(paint, indicator); 14 | } 15 | 16 | public void draw(@NonNull Canvas canvas, 17 | @NonNull Value value, 18 | int position, 19 | int coordinateX, 20 | int coordinateY) { 21 | 22 | if (!(value instanceof ColorAnimationValue)) { 23 | return; 24 | } 25 | 26 | ColorAnimationValue v = (ColorAnimationValue) value; 27 | float radius = indicator.getRadius(); 28 | int color = indicator.getSelectedColor(); 29 | 30 | int selectedPosition = indicator.getSelectedPosition(); 31 | int selectingPosition = indicator.getSelectingPosition(); 32 | int lastSelectedPosition = indicator.getLastSelectedPosition(); 33 | 34 | if (indicator.isInteractiveAnimation()) { 35 | if (position == selectingPosition) { 36 | color = v.getColor(); 37 | 38 | } else if (position == selectedPosition) { 39 | color = v.getColorReverse(); 40 | } 41 | 42 | } else { 43 | if (position == selectedPosition) { 44 | color = v.getColor(); 45 | 46 | } else if (position == lastSelectedPosition) { 47 | color = v.getColorReverse(); 48 | } 49 | } 50 | 51 | paint.setColor(color); 52 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/DropDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.DropAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | import com.rd.draw.data.Orientation; 10 | 11 | public class DropDrawer extends BaseDrawer { 12 | 13 | public DropDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 14 | super(paint, indicator); 15 | } 16 | 17 | public void draw( 18 | @NonNull Canvas canvas, 19 | @NonNull Value value, 20 | int coordinateX, 21 | int coordinateY) { 22 | 23 | if (!(value instanceof DropAnimationValue)) { 24 | return; 25 | } 26 | 27 | DropAnimationValue v = (DropAnimationValue) value; 28 | int unselectedColor = indicator.getUnselectedColor(); 29 | int selectedColor = indicator.getSelectedColor(); 30 | float radius = indicator.getRadius(); 31 | 32 | paint.setColor(unselectedColor); 33 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 34 | 35 | paint.setColor(selectedColor); 36 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 37 | canvas.drawCircle(v.getWidth(), v.getHeight(), v.getRadius(), paint); 38 | } else { 39 | canvas.drawCircle(v.getHeight(), v.getWidth(), v.getRadius(), paint); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/FillDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.FillAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | 10 | public class FillDrawer extends BaseDrawer { 11 | 12 | private Paint strokePaint; 13 | 14 | public FillDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 15 | super(paint, indicator); 16 | 17 | strokePaint = new Paint(); 18 | strokePaint.setStyle(Paint.Style.STROKE); 19 | strokePaint.setAntiAlias(true); 20 | } 21 | 22 | public void draw( 23 | @NonNull Canvas canvas, 24 | @NonNull Value value, 25 | int position, 26 | int coordinateX, 27 | int coordinateY) { 28 | 29 | if (!(value instanceof FillAnimationValue)) { 30 | return; 31 | } 32 | 33 | FillAnimationValue v = (FillAnimationValue) value; 34 | int color = indicator.getUnselectedColor(); 35 | float radius = indicator.getRadius(); 36 | int stroke = indicator.getStroke(); 37 | 38 | int selectedPosition = indicator.getSelectedPosition(); 39 | int selectingPosition = indicator.getSelectingPosition(); 40 | int lastSelectedPosition = indicator.getLastSelectedPosition(); 41 | 42 | if (indicator.isInteractiveAnimation()) { 43 | if (position == selectingPosition) { 44 | color = v.getColor(); 45 | radius = v.getRadius(); 46 | stroke = v.getStroke(); 47 | 48 | } else if (position == selectedPosition) { 49 | color = v.getColorReverse(); 50 | radius = v.getRadiusReverse(); 51 | stroke = v.getStrokeReverse(); 52 | } 53 | 54 | } else { 55 | if (position == selectedPosition) { 56 | color = v.getColor(); 57 | radius = v.getRadius(); 58 | stroke = v.getStroke(); 59 | 60 | } else if (position == lastSelectedPosition) { 61 | color = v.getColorReverse(); 62 | radius = v.getRadiusReverse(); 63 | stroke = v.getStrokeReverse(); 64 | } 65 | } 66 | 67 | strokePaint.setColor(color); 68 | strokePaint.setStrokeWidth(indicator.getStroke()); 69 | canvas.drawCircle(coordinateX, coordinateY, indicator.getRadius(), strokePaint); 70 | 71 | strokePaint.setStrokeWidth(stroke); 72 | canvas.drawCircle(coordinateX, coordinateY, radius, strokePaint); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/ScaleDownDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.ScaleAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | 10 | public class ScaleDownDrawer extends BaseDrawer { 11 | 12 | public ScaleDownDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 13 | super(paint, indicator); 14 | } 15 | 16 | public void draw( 17 | @NonNull Canvas canvas, 18 | @NonNull Value value, 19 | int position, 20 | int coordinateX, 21 | int coordinateY) { 22 | 23 | if (!(value instanceof ScaleAnimationValue)) { 24 | return; 25 | } 26 | 27 | ScaleAnimationValue v = (ScaleAnimationValue) value; 28 | float radius = indicator.getRadius(); 29 | int color = indicator.getSelectedColor(); 30 | 31 | int selectedPosition = indicator.getSelectedPosition(); 32 | int selectingPosition = indicator.getSelectingPosition(); 33 | int lastSelectedPosition = indicator.getLastSelectedPosition(); 34 | 35 | if (indicator.isInteractiveAnimation()) { 36 | if (position == selectingPosition) { 37 | radius = v.getRadius(); 38 | color = v.getColor(); 39 | 40 | } else if (position == selectedPosition) { 41 | radius = v.getRadiusReverse(); 42 | color = v.getColorReverse(); 43 | } 44 | 45 | } else { 46 | if (position == selectedPosition) { 47 | radius = v.getRadius(); 48 | color = v.getColor(); 49 | 50 | } else if (position == lastSelectedPosition) { 51 | radius = v.getRadiusReverse(); 52 | color = v.getColorReverse(); 53 | } 54 | } 55 | 56 | paint.setColor(color); 57 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/ScaleDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.ScaleAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | 10 | public class ScaleDrawer extends BaseDrawer { 11 | 12 | public ScaleDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 13 | super(paint, indicator); 14 | } 15 | 16 | public void draw( 17 | @NonNull Canvas canvas, 18 | @NonNull Value value, 19 | int position, 20 | int coordinateX, 21 | int coordinateY) { 22 | 23 | if (!(value instanceof ScaleAnimationValue)) { 24 | return; 25 | } 26 | 27 | ScaleAnimationValue v = (ScaleAnimationValue) value; 28 | float radius = indicator.getRadius(); 29 | int color = indicator.getSelectedColor(); 30 | 31 | int selectedPosition = indicator.getSelectedPosition(); 32 | int selectingPosition = indicator.getSelectingPosition(); 33 | int lastSelectedPosition = indicator.getLastSelectedPosition(); 34 | 35 | if (indicator.isInteractiveAnimation()) { 36 | if (position == selectingPosition) { 37 | radius = v.getRadius(); 38 | color = v.getColor(); 39 | 40 | } else if (position == selectedPosition) { 41 | radius = v.getRadiusReverse(); 42 | color = v.getColorReverse(); 43 | } 44 | 45 | } else { 46 | if (position == selectedPosition) { 47 | radius = v.getRadius(); 48 | color = v.getColor(); 49 | 50 | } else if (position == lastSelectedPosition) { 51 | radius = v.getRadiusReverse(); 52 | color = v.getColorReverse(); 53 | } 54 | } 55 | 56 | paint.setColor(color); 57 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/SlideDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.SlideAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | import com.rd.draw.data.Orientation; 10 | 11 | public class SlideDrawer extends BaseDrawer { 12 | 13 | public SlideDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 14 | super(paint, indicator); 15 | } 16 | 17 | public void draw( 18 | @NonNull Canvas canvas, 19 | @NonNull Value value, 20 | int coordinateX, 21 | int coordinateY) { 22 | 23 | if (!(value instanceof SlideAnimationValue)) { 24 | return; 25 | } 26 | 27 | SlideAnimationValue v = (SlideAnimationValue) value; 28 | int coordinate = v.getCoordinate(); 29 | int unselectedColor = indicator.getUnselectedColor(); 30 | int selectedColor = indicator.getSelectedColor(); 31 | int radius = indicator.getRadius(); 32 | 33 | paint.setColor(unselectedColor); 34 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 35 | 36 | paint.setColor(selectedColor); 37 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 38 | canvas.drawCircle(coordinate, coordinateY, radius, paint); 39 | } else { 40 | canvas.drawCircle(coordinateX, coordinate, radius, paint); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/SwapDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.SwapAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | import com.rd.draw.data.Orientation; 10 | 11 | public class SwapDrawer extends BaseDrawer { 12 | 13 | public SwapDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 14 | super(paint, indicator); 15 | } 16 | 17 | public void draw( 18 | @NonNull Canvas canvas, 19 | @NonNull Value value, 20 | int position, 21 | int coordinateX, 22 | int coordinateY) { 23 | 24 | if (!(value instanceof SwapAnimationValue)) { 25 | return; 26 | } 27 | 28 | SwapAnimationValue v = (SwapAnimationValue) value; 29 | int selectedColor = indicator.getSelectedColor(); 30 | int unselectedColor = indicator.getUnselectedColor(); 31 | int radius = indicator.getRadius(); 32 | 33 | int selectedPosition = indicator.getSelectedPosition(); 34 | int selectingPosition = indicator.getSelectingPosition(); 35 | int lastSelectedPosition = indicator.getLastSelectedPosition(); 36 | 37 | int coordinate = v.getCoordinate(); 38 | int color = unselectedColor; 39 | 40 | if (indicator.isInteractiveAnimation()) { 41 | if (position == selectingPosition) { 42 | coordinate = v.getCoordinate(); 43 | color = selectedColor; 44 | 45 | } else if (position == selectedPosition) { 46 | coordinate = v.getCoordinateReverse(); 47 | color = unselectedColor; 48 | } 49 | 50 | } else { 51 | if (position == lastSelectedPosition) { 52 | coordinate = v.getCoordinate(); 53 | color = selectedColor; 54 | 55 | } else if (position == selectedPosition) { 56 | coordinate = v.getCoordinateReverse(); 57 | color = unselectedColor; 58 | } 59 | } 60 | 61 | paint.setColor(color); 62 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 63 | canvas.drawCircle(coordinate, coordinateY, radius, paint); 64 | } else { 65 | canvas.drawCircle(coordinateX, coordinate, radius, paint); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/ThinWormDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import androidx.annotation.NonNull; 6 | import com.rd.animation.data.Value; 7 | import com.rd.animation.data.type.ThinWormAnimationValue; 8 | import com.rd.draw.data.Indicator; 9 | import com.rd.draw.data.Orientation; 10 | 11 | public class ThinWormDrawer extends WormDrawer { 12 | 13 | public ThinWormDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 14 | super(paint, indicator); 15 | } 16 | 17 | public void draw( 18 | @NonNull Canvas canvas, 19 | @NonNull Value value, 20 | int coordinateX, 21 | int coordinateY) { 22 | 23 | if (!(value instanceof ThinWormAnimationValue)) { 24 | return; 25 | } 26 | 27 | ThinWormAnimationValue v = (ThinWormAnimationValue) value; 28 | int rectStart = v.getRectStart(); 29 | int rectEnd = v.getRectEnd(); 30 | int height = v.getHeight() / 2; 31 | 32 | int radius = indicator.getRadius(); 33 | int unselectedColor = indicator.getUnselectedColor(); 34 | int selectedColor = indicator.getSelectedColor(); 35 | 36 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 37 | rect.left = rectStart; 38 | rect.right = rectEnd; 39 | rect.top = coordinateY - height; 40 | rect.bottom = coordinateY + height; 41 | 42 | } else { 43 | rect.left = coordinateX - height; 44 | rect.right = coordinateX + height; 45 | rect.top = rectStart; 46 | rect.bottom = rectEnd; 47 | } 48 | 49 | paint.setColor(unselectedColor); 50 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 51 | 52 | paint.setColor(selectedColor); 53 | canvas.drawRoundRect(rect, radius, radius, paint); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/draw/drawer/type/WormDrawer.java: -------------------------------------------------------------------------------- 1 | package com.rd.draw.drawer.type; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.graphics.RectF; 6 | import androidx.annotation.NonNull; 7 | import com.rd.animation.data.Value; 8 | import com.rd.animation.data.type.WormAnimationValue; 9 | import com.rd.draw.data.Indicator; 10 | import com.rd.draw.data.Orientation; 11 | 12 | public class WormDrawer extends BaseDrawer { 13 | 14 | public RectF rect; 15 | 16 | public WormDrawer(@NonNull Paint paint, @NonNull Indicator indicator) { 17 | super(paint, indicator); 18 | rect = new RectF(); 19 | } 20 | 21 | public void draw( 22 | @NonNull Canvas canvas, 23 | @NonNull Value value, 24 | int coordinateX, 25 | int coordinateY) { 26 | 27 | if (!(value instanceof WormAnimationValue)) { 28 | return; 29 | } 30 | 31 | WormAnimationValue v = (WormAnimationValue) value; 32 | int rectStart = v.getRectStart(); 33 | int rectEnd = v.getRectEnd(); 34 | 35 | int radius = indicator.getRadius(); 36 | int unselectedColor = indicator.getUnselectedColor(); 37 | int selectedColor = indicator.getSelectedColor(); 38 | 39 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 40 | rect.left = rectStart; 41 | rect.right = rectEnd; 42 | rect.top = coordinateY - radius; 43 | rect.bottom = coordinateY + radius; 44 | 45 | } else { 46 | rect.left = coordinateX - radius; 47 | rect.right = coordinateX + radius; 48 | rect.top = rectStart; 49 | rect.bottom = rectEnd; 50 | } 51 | 52 | paint.setColor(unselectedColor); 53 | canvas.drawCircle(coordinateX, coordinateY, radius, paint); 54 | 55 | paint.setColor(selectedColor); 56 | canvas.drawRoundRect(rect, radius, radius, paint); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/utils/CoordinatesUtils.java: -------------------------------------------------------------------------------- 1 | package com.rd.utils; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import android.util.Pair; 6 | import com.rd.animation.type.AnimationType; 7 | import com.rd.draw.data.Indicator; 8 | import com.rd.draw.data.Orientation; 9 | 10 | public class CoordinatesUtils { 11 | 12 | @SuppressWarnings("UnnecessaryLocalVariable") 13 | public static int getCoordinate(@Nullable Indicator indicator, int position) { 14 | if (indicator == null) { 15 | return 0; 16 | } 17 | 18 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 19 | return getXCoordinate(indicator, position); 20 | } else { 21 | return getYCoordinate(indicator, position); 22 | } 23 | } 24 | 25 | @SuppressWarnings("UnnecessaryLocalVariable") 26 | public static int getXCoordinate(@Nullable Indicator indicator, int position) { 27 | if (indicator == null) { 28 | return 0; 29 | } 30 | 31 | int coordinate; 32 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 33 | coordinate = getHorizontalCoordinate(indicator, position); 34 | } else { 35 | coordinate = getVerticalCoordinate(indicator); 36 | } 37 | 38 | coordinate += indicator.getPaddingLeft(); 39 | return coordinate; 40 | } 41 | 42 | public static int getYCoordinate(@Nullable Indicator indicator, int position) { 43 | if (indicator == null) { 44 | return 0; 45 | } 46 | 47 | int coordinate; 48 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 49 | coordinate = getVerticalCoordinate(indicator); 50 | } else { 51 | coordinate = getHorizontalCoordinate(indicator, position); 52 | } 53 | 54 | coordinate += indicator.getPaddingTop(); 55 | return coordinate; 56 | } 57 | 58 | @SuppressWarnings("SuspiciousNameCombination") 59 | public static int getPosition(@Nullable Indicator indicator, float x, float y) { 60 | if (indicator == null) { 61 | return -1; 62 | } 63 | 64 | float lengthCoordinate; 65 | float heightCoordinate; 66 | 67 | if (indicator.getOrientation() == Orientation.HORIZONTAL) { 68 | lengthCoordinate = x; 69 | heightCoordinate = y; 70 | } else { 71 | lengthCoordinate = y; 72 | heightCoordinate = x; 73 | } 74 | 75 | return getFitPosition(indicator, lengthCoordinate, heightCoordinate); 76 | } 77 | 78 | private static int getFitPosition(@NonNull Indicator indicator, float lengthCoordinate, float heightCoordinate) { 79 | int count = indicator.getCount(); 80 | int radius = indicator.getRadius(); 81 | int stroke = indicator.getStroke(); 82 | int padding = indicator.getPadding(); 83 | 84 | int height = indicator.getOrientation() == Orientation.HORIZONTAL ? indicator.getHeight() : indicator.getWidth(); 85 | int length = 0; 86 | 87 | for (int i = 0; i < count; i++) { 88 | int indicatorPadding = i > 0 ? padding : padding / 2; 89 | int startValue = length; 90 | 91 | length += radius * 2 + (stroke / 2) + indicatorPadding; 92 | int endValue = length; 93 | 94 | boolean fitLength = lengthCoordinate >= startValue && lengthCoordinate <= endValue; 95 | boolean fitHeight = heightCoordinate >= 0 && heightCoordinate <= height; 96 | 97 | if (fitLength && fitHeight) { 98 | return i; 99 | } 100 | } 101 | 102 | return -1; 103 | } 104 | 105 | private static int getHorizontalCoordinate(@NonNull Indicator indicator, int position) { 106 | int count = indicator.getCount(); 107 | int radius = indicator.getRadius(); 108 | int stroke = indicator.getStroke(); 109 | int padding = indicator.getPadding(); 110 | 111 | int coordinate = 0; 112 | for (int i = 0; i < count; i++) { 113 | coordinate += radius + (stroke / 2); 114 | 115 | if (position == i) { 116 | return coordinate; 117 | } 118 | 119 | coordinate += radius + padding + (stroke / 2); 120 | } 121 | 122 | if (indicator.getAnimationType() == AnimationType.DROP) { 123 | coordinate += radius * 2; 124 | } 125 | 126 | return coordinate; 127 | } 128 | 129 | private static int getVerticalCoordinate(@NonNull Indicator indicator) { 130 | int radius = indicator.getRadius(); 131 | int coordinate; 132 | 133 | if (indicator.getAnimationType() == AnimationType.DROP) { 134 | coordinate = radius * 3; 135 | } else { 136 | coordinate = radius; 137 | } 138 | 139 | return coordinate; 140 | } 141 | 142 | public static Pair getProgress(@NonNull Indicator indicator, int position, float positionOffset, boolean isRtl) { 143 | int count = indicator.getCount(); 144 | int selectedPosition = indicator.getSelectedPosition(); 145 | 146 | if (isRtl) { 147 | position = (count - 1) - position; 148 | } 149 | 150 | if (position < 0) { 151 | position = 0; 152 | 153 | } else if (position > count - 1) { 154 | position = count - 1; 155 | } 156 | 157 | boolean isRightOverScrolled = position > selectedPosition; 158 | boolean isLeftOverScrolled; 159 | 160 | if (isRtl) { 161 | isLeftOverScrolled = position - 1 < selectedPosition; 162 | } else { 163 | isLeftOverScrolled = position + 1 < selectedPosition; 164 | } 165 | 166 | if (isRightOverScrolled || isLeftOverScrolled) { 167 | selectedPosition = position; 168 | indicator.setSelectedPosition(selectedPosition); 169 | } 170 | 171 | boolean slideToRightSide = selectedPosition == position && positionOffset != 0; 172 | int selectingPosition; 173 | float selectingProgress; 174 | 175 | if (slideToRightSide) { 176 | selectingPosition = isRtl ? position - 1 : position + 1; 177 | selectingProgress = positionOffset; 178 | 179 | } else { 180 | selectingPosition = position; 181 | selectingProgress = 1 - positionOffset; 182 | } 183 | 184 | if (selectingProgress > 1) { 185 | selectingProgress = 1; 186 | 187 | } else if (selectingProgress < 0) { 188 | selectingProgress = 0; 189 | } 190 | 191 | return new Pair<>(selectingPosition, selectingProgress); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/utils/DensityUtils.java: -------------------------------------------------------------------------------- 1 | package com.rd.utils; 2 | 3 | import android.content.res.Resources; 4 | import android.util.TypedValue; 5 | 6 | public class DensityUtils { 7 | 8 | public static int dpToPx(int dp) { 9 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); 10 | } 11 | 12 | public static int pxToDp(float px) { 13 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, px, Resources.getSystem().getDisplayMetrics()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pageindicatorview/src/main/java/com/rd/utils/IdUtils.java: -------------------------------------------------------------------------------- 1 | package com.rd.utils; 2 | 3 | import android.os.Build; 4 | import android.view.View; 5 | 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | public class IdUtils { 9 | 10 | private static final AtomicInteger nextGeneratedId = new AtomicInteger(1); 11 | 12 | public static int generateViewId(){ 13 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 14 | return generateId(); 15 | } else { 16 | return View.generateViewId(); 17 | } 18 | } 19 | 20 | /** 21 | * Generate a value suitable for use in #setId(int). 22 | * This value will not collide with ID values generated at build time by aapt for R.id. 23 | * 24 | * @return a generated ID value 25 | */ 26 | private static int generateId() { 27 | for (; ; ) { 28 | final int result = nextGeneratedId.get(); 29 | // aapt-generated IDs have the high byte nonzero; clamp to the range under that. 30 | int newValue = result + 1; 31 | if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. 32 | if (nextGeneratedId.compareAndSet(result, newValue)) { 33 | return result; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pageindicatorview/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 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion '28.0.3' 6 | 7 | defaultConfig { 8 | applicationId "com.rd.pageindicatorview" 9 | minSdkVersion 14 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(include: ['*.jar'], dir: 'libs') 24 | implementation 'androidx.appcompat:appcompat:1.0.1' 25 | implementation 'androidx.recyclerview:recyclerview:1.0.0' 26 | implementation project(':pageindicatorview') 27 | } 28 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Work\IDE\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sample/src/main/java/com/rd/pageindicatorview/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.rd.pageindicatorview.base; 2 | 3 | import androidx.appcompat.app.ActionBar; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import androidx.appcompat.widget.Toolbar; 6 | import android.view.MenuItem; 7 | import com.rd.pageindicatorview.sample.R; 8 | 9 | public class BaseActivity extends AppCompatActivity { 10 | 11 | private ActionBar toolbar; 12 | 13 | @Override 14 | public boolean onOptionsItemSelected(MenuItem item) { 15 | switch (item.getItemId()) { 16 | case android.R.id.home: 17 | onBackPressed(); 18 | break; 19 | } 20 | return super.onOptionsItemSelected(item); 21 | } 22 | 23 | public void initToolbar() { 24 | if (getSupportActionBar() == null) { 25 | setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); 26 | } 27 | 28 | toolbar = getSupportActionBar(); 29 | if (toolbar != null) { 30 | toolbar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE); 31 | } 32 | } 33 | 34 | public void displayBackButton(boolean display) { 35 | if (toolbar != null) { 36 | toolbar.setDisplayHomeAsUpEnabled(display); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/rd/pageindicatorview/customize/CustomizeActivity.java: -------------------------------------------------------------------------------- 1 | package com.rd.pageindicatorview.customize; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.PorterDuff; 6 | import android.os.Bundle; 7 | import androidx.annotation.NonNull; 8 | import androidx.annotation.Nullable; 9 | import android.view.View; 10 | import android.widget.*; 11 | import com.rd.pageindicatorview.base.BaseActivity; 12 | import com.rd.pageindicatorview.data.Customization; 13 | import com.rd.pageindicatorview.data.CustomizationConverter; 14 | import com.rd.pageindicatorview.sample.R; 15 | 16 | public class CustomizeActivity extends BaseActivity implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener { 17 | 18 | public static final String EXTRAS_CUSTOMIZATION = "EXTRAS_CUSTOMIZATION"; 19 | public static final int EXTRAS_CUSTOMIZATION_REQUEST_CODE = 1000; 20 | 21 | private Customization customization; 22 | 23 | public static void start(@NonNull Activity activity, @NonNull Customization customization) { 24 | Intent intent = new Intent(activity, CustomizeActivity.class); 25 | intent.putExtra(EXTRAS_CUSTOMIZATION, customization); 26 | activity.startActivityForResult(intent, EXTRAS_CUSTOMIZATION_REQUEST_CODE); 27 | } 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.ac_customize); 33 | 34 | initToolbar(); 35 | displayBackButton(true); 36 | initData(); 37 | initViews(); 38 | } 39 | 40 | @Override 41 | public void finish() { 42 | Intent intent = new Intent(); 43 | intent.putExtra(EXTRAS_CUSTOMIZATION, customization); 44 | setResult(Activity.RESULT_OK, intent); 45 | 46 | super.finish(); 47 | } 48 | 49 | @Override 50 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 51 | switch (parent.getId()) { 52 | case R.id.spinnerAnimationType: 53 | customization.setAnimationType(CustomizationConverter.getAnimationType(position)); 54 | break; 55 | 56 | case R.id.spinnerOrientation: 57 | customization.setOrientation(CustomizationConverter.getOrientation(position)); 58 | break; 59 | 60 | case R.id.spinnerRtl: 61 | customization.setRtlMode(CustomizationConverter.getRtlMode(position)); 62 | break; 63 | } 64 | } 65 | 66 | @Override 67 | public void onNothingSelected(AdapterView parent) {/*empty*/} 68 | 69 | @Override 70 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 71 | int id = buttonView.getId(); 72 | switch (id) { 73 | case R.id.switchInteractiveAnimation: 74 | customization.setInteractiveAnimation(isChecked); 75 | break; 76 | 77 | case R.id.switchAutoVisibility: 78 | customization.setAutoVisibility(isChecked); 79 | break; 80 | 81 | case R.id.switchFadeOnIdle: 82 | customization.setFadeOnIdle(isChecked); 83 | break; 84 | } 85 | } 86 | 87 | private void initData() { 88 | Intent intent = getIntent(); 89 | if (intent != null) { 90 | customization = intent.getParcelableExtra(EXTRAS_CUSTOMIZATION); 91 | } else { 92 | customization = new Customization(); 93 | } 94 | } 95 | 96 | private void initViews() { 97 | Spinner spinnerAnimationType = findViewById(R.id.spinnerAnimationType); 98 | setSpinnerAdapter(spinnerAnimationType, R.array.animation_type); 99 | spinnerAnimationType.setOnItemSelectedListener(this); 100 | spinnerAnimationType.setSelection(customization.getAnimationType().ordinal()); 101 | 102 | Spinner spinnerOrientation = findViewById(R.id.spinnerOrientation); 103 | setSpinnerAdapter(spinnerOrientation, R.array.orientation); 104 | spinnerOrientation.setOnItemSelectedListener(this); 105 | spinnerOrientation.setSelection(customization.getOrientation().ordinal()); 106 | 107 | Spinner spinnerRtl = findViewById(R.id.spinnerRtl); 108 | setSpinnerAdapter(spinnerRtl, R.array.rtl); 109 | spinnerRtl.setOnItemSelectedListener(this); 110 | spinnerRtl.setSelection(customization.getRtlMode().ordinal()); 111 | 112 | Switch switchInteractiveAnimation = findViewById(R.id.switchInteractiveAnimation); 113 | switchInteractiveAnimation.setOnCheckedChangeListener(this); 114 | switchInteractiveAnimation.setChecked(customization.isInteractiveAnimation()); 115 | 116 | Switch switchAutoVisibility = findViewById(R.id.switchAutoVisibility); 117 | switchAutoVisibility.setOnCheckedChangeListener(this); 118 | switchAutoVisibility.setChecked(customization.isAutoVisibility()); 119 | 120 | Switch switchFadeOnIdle = findViewById(R.id.switchFadeOnIdle); 121 | switchFadeOnIdle.setOnCheckedChangeListener(this); 122 | switchFadeOnIdle.setChecked(customization.isFadeOnIdle()); 123 | 124 | } 125 | 126 | private void setSpinnerAdapter(@Nullable Spinner spinner, int textArrayId) { 127 | if (spinner != null) { 128 | ArrayAdapter adapter = ArrayAdapter.createFromResource(this, textArrayId, R.layout.item_spinner_selected); 129 | adapter.setDropDownViewResource(R.layout.item_spinner_drop_down); 130 | spinner.setAdapter(adapter); 131 | spinner.getBackground().setColorFilter(getResources().getColor(R.color.gray_500), PorterDuff.Mode.SRC_ATOP); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /sample/src/main/java/com/rd/pageindicatorview/data/Customization.java: -------------------------------------------------------------------------------- 1 | package com.rd.pageindicatorview.data; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.rd.animation.type.AnimationType; 7 | import com.rd.draw.data.Orientation; 8 | import com.rd.draw.data.RtlMode; 9 | 10 | public class Customization implements Parcelable { 11 | 12 | private AnimationType animationType = AnimationType.NONE; 13 | private Orientation orientation = Orientation.HORIZONTAL; 14 | private RtlMode rtlMode = RtlMode.Off; 15 | 16 | private boolean interactiveAnimation = false; 17 | private boolean autoVisibility = true; 18 | private boolean fadeOnIdle = false; 19 | 20 | public AnimationType getAnimationType() { 21 | return animationType; 22 | } 23 | 24 | public void setAnimationType(AnimationType animationType) { 25 | this.animationType = animationType; 26 | } 27 | 28 | public Orientation getOrientation() { 29 | return orientation; 30 | } 31 | 32 | public void setOrientation(Orientation orientation) { 33 | this.orientation = orientation; 34 | } 35 | 36 | public RtlMode getRtlMode() { 37 | return rtlMode; 38 | } 39 | 40 | public void setRtlMode(RtlMode rtlMode) { 41 | this.rtlMode = rtlMode; 42 | } 43 | 44 | public boolean isInteractiveAnimation() { 45 | return interactiveAnimation; 46 | } 47 | 48 | public void setInteractiveAnimation(boolean interactiveAnimation) { 49 | this.interactiveAnimation = interactiveAnimation; 50 | } 51 | 52 | public boolean isAutoVisibility() { 53 | return autoVisibility; 54 | } 55 | 56 | public void setAutoVisibility(boolean autoVisibility) { 57 | this.autoVisibility = autoVisibility; 58 | } 59 | 60 | public boolean isFadeOnIdle() { 61 | return fadeOnIdle; 62 | } 63 | 64 | public void setFadeOnIdle(boolean fadeOnIdle) { 65 | this.fadeOnIdle = fadeOnIdle; 66 | } 67 | 68 | @Override 69 | public boolean equals(Object o) { 70 | if (this == o) return true; 71 | if (o == null || getClass() != o.getClass()) return false; 72 | 73 | Customization that = (Customization) o; 74 | 75 | if (interactiveAnimation != that.interactiveAnimation) return false; 76 | if (autoVisibility != that.autoVisibility) return false; 77 | if (animationType != that.animationType) return false; 78 | if (orientation != that.orientation) return false; 79 | if (fadeOnIdle != that.fadeOnIdle) return false; 80 | return rtlMode == that.rtlMode; 81 | 82 | } 83 | 84 | @Override 85 | public int hashCode() { 86 | int result = animationType != null ? animationType.hashCode() : 0; 87 | result = 31 * result + (orientation != null ? orientation.hashCode() : 0); 88 | result = 31 * result + (rtlMode != null ? rtlMode.hashCode() : 0); 89 | result = 31 * result + (interactiveAnimation ? 1 : 0); 90 | result = 31 * result + (autoVisibility ? 1 : 0); 91 | result = 31 * result + (fadeOnIdle ? 1 : 0); 92 | return result; 93 | } 94 | 95 | @Override 96 | public int describeContents() { 97 | return 0; 98 | } 99 | 100 | @Override 101 | public void writeToParcel(Parcel dest, int flags) { 102 | dest.writeInt(this.animationType == null ? -1 : this.animationType.ordinal()); 103 | dest.writeInt(this.orientation == null ? -1 : this.orientation.ordinal()); 104 | dest.writeInt(this.rtlMode == null ? -1 : this.rtlMode.ordinal()); 105 | dest.writeByte(this.interactiveAnimation ? (byte) 1 : (byte) 0); 106 | dest.writeByte(this.autoVisibility ? (byte) 1 : (byte) 0); 107 | dest.writeByte(this.fadeOnIdle ? (byte) 1 : (byte) 0); 108 | } 109 | 110 | public Customization() { 111 | } 112 | 113 | protected Customization(Parcel in) { 114 | int tmpAnimationType = in.readInt(); 115 | this.animationType = tmpAnimationType == -1 ? null : AnimationType.values()[tmpAnimationType]; 116 | int tmpOrientation = in.readInt(); 117 | this.orientation = tmpOrientation == -1 ? null : Orientation.values()[tmpOrientation]; 118 | int tmpRtlMode = in.readInt(); 119 | this.rtlMode = tmpRtlMode == -1 ? null : RtlMode.values()[tmpRtlMode]; 120 | this.interactiveAnimation = in.readByte() != 0; 121 | this.autoVisibility = in.readByte() != 0; 122 | this.fadeOnIdle = in.readByte() != 0; 123 | } 124 | 125 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 126 | @Override 127 | public Customization createFromParcel(Parcel source) { 128 | return new Customization(source); 129 | } 130 | 131 | @Override 132 | public Customization[] newArray(int size) { 133 | return new Customization[size]; 134 | } 135 | }; 136 | } 137 | -------------------------------------------------------------------------------- /sample/src/main/java/com/rd/pageindicatorview/data/CustomizationConverter.java: -------------------------------------------------------------------------------- 1 | package com.rd.pageindicatorview.data; 2 | 3 | import com.rd.animation.type.AnimationType; 4 | import com.rd.draw.data.Orientation; 5 | import com.rd.draw.data.RtlMode; 6 | 7 | public class CustomizationConverter { 8 | 9 | public static AnimationType getAnimationType(int position) { 10 | switch (position) { 11 | case 0: 12 | return AnimationType.NONE; 13 | 14 | case 1: 15 | return AnimationType.COLOR; 16 | 17 | case 2: 18 | return AnimationType.SCALE; 19 | 20 | case 3: 21 | return AnimationType.WORM; 22 | 23 | case 4: 24 | return AnimationType.SLIDE; 25 | 26 | case 5: 27 | return AnimationType.FILL; 28 | 29 | case 6: 30 | return AnimationType.THIN_WORM; 31 | 32 | case 7: 33 | return AnimationType.DROP; 34 | 35 | case 8: 36 | return AnimationType.SWAP; 37 | 38 | case 9: 39 | return AnimationType.SCALE_DOWN; 40 | 41 | default: 42 | return AnimationType.NONE; 43 | } 44 | } 45 | 46 | public static Orientation getOrientation(int position) { 47 | switch (position) { 48 | case 0: 49 | return Orientation.HORIZONTAL; 50 | 51 | case 1: 52 | return Orientation.VERTICAL; 53 | 54 | default: 55 | return Orientation.HORIZONTAL; 56 | } 57 | } 58 | 59 | public static RtlMode getRtlMode(int position) { 60 | switch (position) { 61 | case 0: 62 | return RtlMode.On; 63 | 64 | case 1: 65 | return RtlMode.Off; 66 | 67 | case 2: 68 | return RtlMode.Auto; 69 | 70 | default: 71 | return RtlMode.Off; 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /sample/src/main/java/com/rd/pageindicatorview/home/HomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.rd.pageindicatorview.home; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import androidx.annotation.NonNull; 6 | import androidx.viewpager.widget.ViewPager; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import com.rd.PageIndicatorView; 11 | import com.rd.pageindicatorview.base.BaseActivity; 12 | import com.rd.pageindicatorview.customize.CustomizeActivity; 13 | import com.rd.pageindicatorview.data.Customization; 14 | import com.rd.pageindicatorview.sample.R; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | 20 | public class HomeActivity extends BaseActivity { 21 | 22 | private PageIndicatorView pageIndicatorView; 23 | private Customization customization; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.ac_home); 29 | customization = new Customization(); 30 | 31 | initToolbar(); 32 | initViews(); 33 | } 34 | 35 | @Override 36 | protected void onActivityResult(int requestCode, int resultCode, Intent intent) { 37 | boolean customization = requestCode == CustomizeActivity.EXTRAS_CUSTOMIZATION_REQUEST_CODE && resultCode == RESULT_OK; 38 | if (customization && intent != null) { 39 | this.customization = intent.getParcelableExtra(CustomizeActivity.EXTRAS_CUSTOMIZATION); 40 | updateIndicator(); 41 | } 42 | } 43 | 44 | @Override 45 | public boolean onCreateOptionsMenu(Menu menu) { 46 | getMenuInflater().inflate(R.menu.menu_customize, menu); 47 | return true; 48 | } 49 | 50 | @Override 51 | public boolean onOptionsItemSelected(MenuItem item) { 52 | switch (item.getItemId()) { 53 | case R.id.actionCustomize: 54 | CustomizeActivity.start(this, customization); 55 | return true; 56 | 57 | default: 58 | return super.onOptionsItemSelected(item); 59 | } 60 | } 61 | 62 | @SuppressWarnings("ConstantConditions") 63 | private void initViews() { 64 | HomeAdapter adapter = new HomeAdapter(); 65 | adapter.setData(createPageList()); 66 | 67 | final ViewPager pager = findViewById(R.id.viewPager); 68 | pager.setAdapter(adapter); 69 | 70 | pageIndicatorView = findViewById(R.id.pageIndicatorView); 71 | } 72 | 73 | @NonNull 74 | private List createPageList() { 75 | List pageList = new ArrayList<>(); 76 | pageList.add(createPageView(R.color.google_red)); 77 | pageList.add(createPageView(R.color.google_blue)); 78 | pageList.add(createPageView(R.color.google_yellow)); 79 | pageList.add(createPageView(R.color.google_green)); 80 | 81 | return pageList; 82 | } 83 | 84 | @NonNull 85 | private View createPageView(int color) { 86 | View view = new View(this); 87 | view.setBackgroundColor(getResources().getColor(color)); 88 | 89 | return view; 90 | } 91 | 92 | private void updateIndicator() { 93 | if (customization == null) { 94 | return; 95 | } 96 | 97 | pageIndicatorView.setAnimationType(customization.getAnimationType()); 98 | pageIndicatorView.setOrientation(customization.getOrientation()); 99 | pageIndicatorView.setRtlMode(customization.getRtlMode()); 100 | pageIndicatorView.setInteractiveAnimation(customization.isInteractiveAnimation()); 101 | pageIndicatorView.setAutoVisibility(customization.isAutoVisibility()); 102 | pageIndicatorView.setFadeOnIdle(customization.isFadeOnIdle()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /sample/src/main/java/com/rd/pageindicatorview/home/HomeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.rd.pageindicatorview.home; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.viewpager.widget.PagerAdapter; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | class HomeAdapter extends PagerAdapter { 13 | 14 | private List viewList; 15 | 16 | HomeAdapter() { 17 | this.viewList = new ArrayList<>(); 18 | } 19 | 20 | @Override 21 | public Object instantiateItem(ViewGroup collection, int position) { 22 | View view = viewList.get(position); 23 | collection.addView(view); 24 | return view; 25 | } 26 | 27 | @Override 28 | public void destroyItem(ViewGroup collection, int position, Object view) { 29 | collection.removeView((View) view); 30 | } 31 | 32 | @Override 33 | public int getCount() { 34 | return viewList.size(); 35 | } 36 | 37 | @Override 38 | public boolean isViewFromObject(View view, Object object) { 39 | return view == object; 40 | } 41 | 42 | @Override 43 | public int getItemPosition(Object object) { 44 | return POSITION_NONE; 45 | } 46 | 47 | void setData(@Nullable List list) { 48 | this.viewList.clear(); 49 | if (list != null && !list.isEmpty()) { 50 | this.viewList.addAll(list); 51 | } 52 | 53 | notifyDataSetChanged(); 54 | } 55 | 56 | @NonNull 57 | List getData() { 58 | if (viewList == null) { 59 | viewList = new ArrayList<>(); 60 | } 61 | 62 | return viewList; 63 | } 64 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v21/selector_black_10_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/selector_black_10_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/ac_customize.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 19 | 20 | 24 | 25 | 31 | 32 | 39 | 40 | 45 | 46 | 47 | 51 | 52 | 56 | 57 | 63 | 64 | 71 | 72 | 77 | 78 | 79 | 83 | 84 | 90 | 91 | 98 | 99 | 104 | 105 | 106 | 107 | 111 | 112 | 121 | 122 | 126 | 127 | 136 | 137 | 141 | 142 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/ac_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 19 | 20 | 25 | 26 | 36 | 37 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/item_spinner_drop_down.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/item_spinner_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/menu_customize.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romandanylyk/PageIndicatorView/b1bad589b506add6842cc44ef6f2ca7e561dfebd/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #00000000 5 | 6 | #000000 7 | #1A000000 8 | 9 | #ffffff 10 | 11 | #FAFAFA 12 | #F5F5F5 13 | #EEEEEE 14 | #E0E0E0 15 | #BDBDBD 16 | #9E9E9E 17 | #757575 18 | #616161 19 | #424242 20 | #212121 21 | 22 | #E3F2FD 23 | #BBDEFB 24 | #90CAF9 25 | #64B5F6 26 | #42A5F5 27 | #2196F3 28 | #1E88E5 29 | #1976D2 30 | #1565C0 31 | #0D47A1 32 | 33 | #FFEBEE 34 | #FFCDD2 35 | #EF9A9A 36 | #E57373 37 | #EF5350 38 | #F44336 39 | #E53935 40 | #D32F2F 41 | #C62828 42 | #B71C1C 43 | 44 | #DBE6DA 45 | #BAD7B7 46 | #99C495 47 | #7AB273 48 | #66A55C 49 | #549948 50 | #4A8840 51 | #3F7736 52 | #35662D 53 | #35662D 54 | 55 | #ea5a4f 56 | #6099f5 57 | #edc855 58 | #5cb171 59 | 60 | 61 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PageIndicatorView 3 | Interactive animation 4 | Auto visibility 5 | Animation type 6 | Orientation 7 | Customize 8 | Home 9 | Right to left mode 10 | Fade on idle 11 | 12 | 13 | None 14 | Color 15 | Scale 16 | Worm 17 | Slide 18 | Fill 19 | Thin Worm 20 | Drop 21 | Swap 22 | Scale Down 23 | 24 | 25 | 26 | Horizontal 27 | Vertical 28 | 29 | 30 | 31 | On 32 | Off 33 | Auto 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':pageindicatorview' 2 | --------------------------------------------------------------------------------