├── settings.gradle ├── github ├── slideup_default.gif ├── gradient_actionbar.gif ├── menu_flip_and_fade.gif ├── slideup_anticipate.gif ├── animation_transition.gif └── menu_shrink_and_fade.gif ├── core ├── package-list ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ └── ids.xml │ │ └── layout │ │ │ └── menu_animation.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── kaichunlin │ │ └── transition │ │ ├── TransitionManagerListener.java │ │ ├── adapter │ │ ├── TransitionAdapter.java │ │ ├── MenuOptionConfiguration.java │ │ ├── AdapterState.java │ │ ├── MenuOptionHandler.java │ │ ├── MenuBaseAdapter.java │ │ ├── MenuAnimationAdapter.java │ │ ├── AbstractAdapter.java │ │ ├── DrawerListenerAdapter.java │ │ ├── DefaultMenuOptionHandler.java │ │ └── AnimationAdapter.java │ │ ├── animation │ │ ├── StateController.java │ │ ├── DefaultAnimationListener.java │ │ ├── AutoReverseAnimationListener.java │ │ ├── AnimationListener.java │ │ ├── Animation.java │ │ ├── AnimatorController.java │ │ ├── AnimationController.java │ │ ├── TransitionAnimation.java │ │ └── AbstractAnimation.java │ │ ├── transformer │ │ ├── ViewTransformer.java │ │ ├── ColorTransformer.java │ │ └── ScaledTransformer.java │ │ ├── TransitionConfig.java │ │ ├── internal │ │ ├── debug │ │ │ ├── TraceTransitionManagerListener.java │ │ │ └── TraceAnimationListener.java │ │ ├── CustomTransitionController.java │ │ └── TransitionController.java │ │ ├── TransitionOperation.java │ │ ├── TransitionManager.java │ │ ├── Transition.java │ │ ├── ViewTransition.java │ │ ├── util │ │ ├── TransitionUtil.java │ │ └── TransitionStateLogger.java │ │ ├── Cascade.java │ │ ├── DefaultTransitionManager.java │ │ └── AbstractTransition.java ├── module.properties ├── proguard-rules.pro └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── app ├── src │ └── main │ │ ├── res │ │ ├── drawable-xhdpi │ │ │ ├── bg.jpg │ │ │ ├── bg2.jpg │ │ │ ├── bg3.jpg │ │ │ ├── bg4.jpg │ │ │ ├── ic_action_new.png │ │ │ ├── drawer_shadow.9.png │ │ │ ├── ic_action_share.png │ │ │ ├── ic_action_download.png │ │ │ ├── ic_action_favorite.png │ │ │ ├── ic_action_refresh.png │ │ │ └── ic_action_search.png │ │ ├── drawable │ │ │ ├── android_icon.png │ │ │ └── gradient.xml │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-hdpi │ │ │ ├── ic_action_new.png │ │ │ ├── drawer_shadow.9.png │ │ │ ├── ic_action_search.png │ │ │ ├── ic_action_share.png │ │ │ ├── ic_action_download.png │ │ │ ├── ic_action_favorite.png │ │ │ └── ic_action_refresh.png │ │ ├── drawable-mdpi │ │ │ ├── ic_action_new.png │ │ │ ├── drawer_shadow.9.png │ │ │ ├── ic_action_search.png │ │ │ ├── ic_action_share.png │ │ │ ├── ic_action_download.png │ │ │ ├── ic_action_favorite.png │ │ │ └── ic_action_refresh.png │ │ ├── drawable-xxhdpi │ │ │ ├── ic_action_new.png │ │ │ ├── drawer_shadow.9.png │ │ │ ├── ic_action_refresh.png │ │ │ ├── ic_action_search.png │ │ │ ├── ic_action_share.png │ │ │ ├── ic_action_download.png │ │ │ └── ic_action_favorite.png │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ ├── menu │ │ │ ├── drawer.xml │ │ │ ├── main.xml │ │ │ ├── viewpager.xml │ │ │ └── sliding_up_panel.xml │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ ├── anim │ │ │ └── animator_set.xml │ │ ├── values-v21 │ │ │ └── styles.xml │ │ └── layout │ │ │ ├── fragment_view_pager.xml │ │ │ ├── activity_view_pager.xml │ │ │ ├── activity_slideup_actionbar_transparent.xml │ │ │ ├── activity_drawer_menuitem.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_drawer_view.xml │ │ │ ├── activity_drawer_gradient.xml │ │ │ └── activity_slideup_actionbar.xml │ │ ├── java │ │ └── com │ │ │ └── kaichunlin │ │ │ └── transition │ │ │ └── app │ │ │ ├── ViewPagerActivityFragment.java │ │ │ ├── DialogDrawerListener.java │ │ │ ├── DialogPanelSlideListener.java │ │ │ ├── SlidingUpPanelRotateActivity.java │ │ │ ├── ViewPagerActivity.java │ │ │ ├── DrawerGradientActivity.java │ │ │ ├── MainActivity.java │ │ │ └── DrawerViewActivity.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── slidinguppanel ├── module.properties ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── kaichunlin │ │ └── transition │ │ └── adapter │ │ └── SlidingUpPanelLayoutAdapter.java ├── proguard-rules.pro └── build.gradle ├── observablescrollview ├── module.properties ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── kaichunlin │ │ └── transition │ │ └── adapter │ │ └── ObservableScrollViewCallbacksAdapter.java ├── proguard-rules.pro └── build.gradle ├── .gitignore ├── gradlew.bat ├── upload.gradle └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':core', ':observablescrollview', ':slidinguppanel', ':transition_staging' 2 | -------------------------------------------------------------------------------- /github/slideup_default.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/github/slideup_default.gif -------------------------------------------------------------------------------- /core/package-list: -------------------------------------------------------------------------------- 1 | com.kaichunlin.transition 2 | com.kaichunlin.transition.adapter 3 | com.kaichunlin.transition.util 4 | -------------------------------------------------------------------------------- /github/gradient_actionbar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/github/gradient_actionbar.gif -------------------------------------------------------------------------------- /github/menu_flip_and_fade.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/github/menu_flip_and_fade.gif -------------------------------------------------------------------------------- /github/slideup_anticipate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/github/slideup_anticipate.gif -------------------------------------------------------------------------------- /github/animation_transition.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/github/animation_transition.gif -------------------------------------------------------------------------------- /github/menu_shrink_and_fade.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/github/menu_shrink_and_fade.gif -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/bg.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/bg2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/bg3.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/bg4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/bg4.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/android_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable/android_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/ic_action_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/ic_action_share.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/ic_action_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/ic_action_share.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/ic_action_share.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_new.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/ic_action_download.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/ic_action_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-hdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/ic_action_download.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/ic_action_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-mdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/ic_action_download.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/ic_action_favorite.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xhdpi/ic_action_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_share.png -------------------------------------------------------------------------------- /core/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_download.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaichunlin/android-transition/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_favorite.png -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /core/module.properties: -------------------------------------------------------------------------------- 1 | module.pom_name=Android Transition 2 | #module.artifactId=core 3 | module.bintray=android-transition 4 | module.desc=Allows the easy creation of animated transition effects when the state of Android UI has changed -------------------------------------------------------------------------------- /slidinguppanel/module.properties: -------------------------------------------------------------------------------- 1 | module.pom_name=Android Transition - SlidingUpPanel Adapter 2 | #module.artifactId=slidinguppanel 3 | module.bintray=android-transition:slidinguppanel 4 | module.desc=Android-Transition adapter for for SlidingUpPanel library -------------------------------------------------------------------------------- /slidinguppanel/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /observablescrollview/module.properties: -------------------------------------------------------------------------------- 1 | module.pom_name=Android Transition - ObservableScrollView Adapter 2 | #module.artifactId=observablescrollview 3 | module.bintray=android-transition:observablescrollview 4 | module.desc=Android-Transition adapter for for ObservableScrollView library -------------------------------------------------------------------------------- /observablescrollview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 07 09:19:25 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | transition_slidinguppanel/build/ 2 | transition_observablescrollview/build/ 3 | transition/build/ 4 | transition_staging/build/ 5 | app/build/ 6 | .gradle/ 7 | build/ 8 | .idea/ 9 | local.properties 10 | gradle.properties 11 | *.iml 12 | transition_staging/ 13 | signing/ 14 | video_and_screens/ 15 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/TransitionManagerListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | /** 4 | * Listens to transition state changes. 5 | */ 6 | public interface TransitionManagerListener { 7 | void onTransitionStart(TransitionManager manager); 8 | 9 | void onTransitionEnd(TransitionManager manager); 10 | } -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/TransitionAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import com.kaichunlin.transition.TransitionManager; 4 | 5 | /** 6 | */ 7 | public interface TransitionAdapter extends TransitionManager { 8 | AdapterState getAdapterState(); 9 | 10 | void setTransitionManager(TransitionManager transitionManager); 11 | 12 | TransitionManager getTransitionManager(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/drawer.xml: -------------------------------------------------------------------------------- 1 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 8 | 240dp 9 | 10 | -------------------------------------------------------------------------------- /core/src/main/res/layout/menu_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/StateController.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | /** 4 | * Controls the current animation state. 5 | */ 6 | interface StateController { 7 | void addAnimation(AbstractAnimation animation); 8 | 9 | void setAnimationDuration(long duration); 10 | 11 | void startController(); 12 | 13 | void pauseController(); 14 | 15 | void resumeController(); 16 | 17 | void endController(); 18 | 19 | void resetController(); 20 | 21 | void cancelController(); 22 | } -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/transformer/ViewTransformer.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.transformer; 2 | 3 | import android.view.View; 4 | 5 | import com.kaichunlin.transition.internal.TransitionController; 6 | 7 | /** 8 | * A generic interface providing arbitrary transformation to a View. 9 | */ 10 | public interface ViewTransformer { 11 | /** 12 | * 13 | * @param controller 14 | * @param target 15 | * @param progress 16 | */ 17 | void updateView(TransitionController controller, View target, float progress); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/anim/animator_set.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/DefaultAnimationListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | /** 4 | * Provides default (empty) implementation for {@link AnimationListener} 5 | */ 6 | public class DefaultAnimationListener implements AnimationListener { 7 | @Override 8 | public void onAnimationStart(Animation animation){} 9 | 10 | @Override 11 | public void onAnimationEnd(Animation animation){} 12 | 13 | @Override 14 | public void onAnimationCancel(Animation animation){} 15 | 16 | @Override 17 | public void onAnimationReset(Animation animation){} 18 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_view_pager.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/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:\Users\Kai\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /core/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:\Users\Kai\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /slidinguppanel/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:\Users\Kai\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /observablescrollview/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:\Users\Kai\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.0' 6 | 7 | lintOptions { 8 | abortOnError false; 9 | } 10 | 11 | defaultConfig { 12 | minSdkVersion 11 13 | targetSdkVersion 23 14 | versionCode 1 15 | versionName "1.0" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(include: ['*.jar'], dir: 'libs') 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | // compile 'com.android.support:support-annotations:25.3.1' 29 | compile 'com.nineoldandroids:library:2.4.0' 30 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/viewpager.xml: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /slidinguppanel/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | android { 6 | compileSdkVersion 25 7 | buildToolsVersion '25.0.0' 8 | 9 | defaultConfig { 10 | minSdkVersion 11 11 | targetSdkVersion 22 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | compile 'com.sothree.slidinguppanel:library:3.3.1' 26 | compile project(':core') 27 | } 28 | 29 | // This is the library version used when deploying the artifact 30 | version = "0.9.5" 31 | apply from: '../upload.gradle'; -------------------------------------------------------------------------------- /observablescrollview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | android { 6 | compileSdkVersion 25 7 | buildToolsVersion '25.0.0' 8 | 9 | defaultConfig { 10 | minSdkVersion 11 11 | targetSdkVersion 22 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(include: ['*.jar'], dir: 'libs') 25 | compile 'com.github.ksoichiro:android-observablescrollview:1.6.0' 26 | compile project(':core') 27 | } 28 | 29 | // This is the library version used when deploying the artifact 30 | version = "0.9.5" 31 | apply from: '../upload.gradle'; 32 | -------------------------------------------------------------------------------- /app/src/main/res/menu/sliding_up_panel.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/AutoReverseAnimationListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | /** 4 | * Auto play the animation in reverse right after the animation ended, currently only works when the initial animation is not set 5 | * with {@link Animation#setReverseAnimation(boolean)} as true. 6 | */ 7 | public class AutoReverseAnimationListener implements AnimationListener { 8 | @Override 9 | public void onAnimationStart(Animation animation) { 10 | } 11 | 12 | @Override 13 | public void onAnimationEnd(Animation animation) { 14 | if (!animation.isReverseAnimation()) { 15 | animation.setReverseAnimation(true); 16 | animation.startAnimation(); 17 | } 18 | } 19 | 20 | @Override 21 | public void onAnimationCancel(Animation animation) { 22 | } 23 | 24 | @Override 25 | public void onAnimationReset(Animation animation) { 26 | } 27 | } -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/TransitionConfig.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | /** 4 | * Framework-wide debug configuration. 5 | */ 6 | public class TransitionConfig { 7 | static boolean _debug; 8 | static boolean _printDebug; 9 | 10 | /** 11 | * @param debug set debugging 12 | */ 13 | public static void setDebug(boolean debug) { 14 | _debug = debug; 15 | } 16 | 17 | /** 18 | * @return is debugging enabled 19 | */ 20 | public static boolean isDebug() { 21 | return _debug; 22 | } 23 | 24 | /** 25 | * @param printDebug set auto printing transition detail messages on transition/animation end 26 | */ 27 | public static void setPrintDebug(boolean printDebug) { 28 | _printDebug = printDebug; 29 | } 30 | 31 | /** 32 | * @return is auto printing transition detail messages on transition/animation end enabled 33 | */ 34 | public static boolean isPrintDebug() { 35 | return _printDebug; 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_view_pager.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | #004993 19 | #003B77 20 | #009905 21 | #FFAB4C 22 | #FFC682 23 | 24 | #ff33B5E5 25 | #ffff0000 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/ViewPagerActivityFragment.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import kaichunlin.transition.app.R; 10 | 11 | 12 | /** 13 | * A placeholder fragment containing a simple view. 14 | */ 15 | public class ViewPagerActivityFragment extends Fragment { 16 | public static final String ID = "mId"; 17 | public static final String RES_ID = "res_id"; 18 | 19 | public ViewPagerActivityFragment() { 20 | } 21 | 22 | @Override 23 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 24 | Bundle savedInstanceState) { 25 | View rootView = inflater.inflate( 26 | R.layout.fragment_view_pager, container, false); 27 | Bundle args = getArguments(); 28 | rootView.findViewById(R.id.pager_img).setBackgroundResource( 29 | args.getInt(RES_ID)); 30 | return rootView; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/internal/debug/TraceTransitionManagerListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.internal.debug; 2 | 3 | import android.util.Log; 4 | 5 | import com.kaichunlin.transition.TransitionManager; 6 | import com.kaichunlin.transition.TransitionConfig; 7 | import com.kaichunlin.transition.TransitionManagerListener; 8 | 9 | /** 10 | * Uses Log.i to output the start and end of a transition. 11 | */ 12 | public class TraceTransitionManagerListener implements TransitionManagerListener { 13 | private long mStart; 14 | 15 | @Override 16 | public void onTransitionStart(TransitionManager manager) { 17 | mStart = System.currentTimeMillis(); 18 | if (TransitionConfig.isDebug()) { 19 | Log.i(getClass().getSimpleName(), "onTransitionStart: " + manager); 20 | } 21 | } 22 | 23 | @Override 24 | public void onTransitionEnd(TransitionManager manager) { 25 | if (TransitionConfig.isDebug()) { 26 | Log.i(getClass().getSimpleName(), "onTransitionEnd, duration=" + (System.currentTimeMillis() - mStart) + ": " + manager); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/MenuOptionConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.support.annotation.MenuRes; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.kaichunlin.transition.MenuItemTransition; 7 | 8 | /** 9 | * Stores the menu ID-MenuItemTransition pair. 10 | */ 11 | public class MenuOptionConfiguration { 12 | 13 | private final MenuItemTransition mTransition; 14 | private final int mMenuId; 15 | 16 | public MenuOptionConfiguration(@NonNull MenuItemTransition transition) { 17 | mTransition = transition; 18 | mMenuId = -1; 19 | } 20 | 21 | public MenuOptionConfiguration(@NonNull MenuItemTransition transition, @MenuRes int menuId) { 22 | mTransition = transition; 23 | mMenuId = menuId; 24 | } 25 | 26 | /** 27 | * 28 | * @return MenuItemTransition for the menu item 29 | */ 30 | public MenuItemTransition getTransition() { 31 | return mTransition; 32 | } 33 | 34 | /** 35 | * 36 | * @return the menu item's ID 37 | */ 38 | public int getMenuId() { 39 | return mMenuId; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/AdapterState.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.support.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Stores an adapter's open/close state when applicable. 10 | */ 11 | public class AdapterState { 12 | public static final int CLOSE = 0; 13 | public static final int OPEN = 1; 14 | 15 | @IntDef({OPEN, CLOSE}) 16 | @Retention(RetentionPolicy.SOURCE) 17 | @interface State { 18 | } 19 | 20 | private int mState; 21 | private boolean mTransiting; 22 | 23 | public void setState(@State int state) { 24 | mState = state; 25 | } 26 | 27 | @State 28 | public int getState() { 29 | return mState; 30 | } 31 | 32 | public boolean isOpen() { 33 | return mState == OPEN; 34 | } 35 | 36 | public boolean isClose() { 37 | return mState == CLOSE; 38 | } 39 | 40 | public boolean isTransiting() { 41 | return mTransiting; 42 | } 43 | 44 | public void setTransiting(boolean transiting) { 45 | mTransiting = transiting; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/TransitionOperation.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import android.support.annotation.UiThread; 4 | 5 | import com.kaichunlin.transition.adapter.TransitionAdapter; 6 | 7 | /** 8 | */ 9 | public interface TransitionOperation { 10 | /** 11 | * Starts the transition 12 | * 13 | * @return 14 | */ 15 | @UiThread 16 | boolean startTransition(); 17 | 18 | /** 19 | * Starts the transition, {@link TransitionAdapter#getAdapterState()}.isTransitioning() will always returns true after this method is executed. 20 | * 21 | * @param progress the starting transition progress, the valid value range depends on the specific Adapter implementation 22 | * @return true if the call caused the transition to be started, false if the transition is already in the started state 23 | */ 24 | @UiThread 25 | boolean startTransition(float progress); 26 | 27 | /** 28 | * Updates the transition progress 29 | * 30 | * @param progress 31 | */ 32 | void updateProgress(float progress); 33 | 34 | /** 35 | * Stops the transition 36 | */ 37 | @UiThread 38 | void stopTransition(); 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/AnimationListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | /** 4 | * Receives state notifications from an animation. 5 | */ 6 | public interface AnimationListener { 7 | /** 8 | * Notifies the start of the animation. 9 | * 10 | * @param animation The started animation. 11 | */ 12 | void onAnimationStart(Animation animation); 13 | 14 | /** 15 | * Notifies the end of the animation; the affected Views have been assigned their final states. 16 | * 17 | * @param animation The animation which reached its end. 18 | */ 19 | void onAnimationEnd(Animation animation); 20 | 21 | /** 22 | * Notifies the cancellation of the animation; the affected Views retain the states assigned in the last animated frame. 23 | * 24 | * @param animation The animation which was cancelled. 25 | */ 26 | void onAnimationCancel(Animation animation); 27 | 28 | /** 29 | * Notifies the animation's been reset; the affected Views have been reverted to the states when the animation was started. 30 | * 31 | * @param animation The animation which was reset. 32 | */ 33 | void onAnimationReset(Animation animation); 34 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.0' 6 | 7 | lintOptions { 8 | abortOnError false; 9 | } 10 | 11 | defaultConfig { 12 | applicationId 'kaichunlin.transition.app' 13 | minSdkVersion 11 14 | targetSdkVersion 21 15 | versionCode 8 16 | versionName "1.0.6" 17 | } 18 | 19 | //enable to build release version 20 | // signingConfigs { 21 | // release { 22 | // keyAlias project.property('keyalias') 23 | // keyPassword project.property('keypassword') 24 | // storeFile file(project.property('storefile')) 25 | // storePassword project.property('storepassword') 26 | // } 27 | // } 28 | // buildTypes { 29 | // release { 30 | // signingConfig signingConfigs.release 31 | // minifyEnabled false 32 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | // } 34 | // } 35 | 36 | dexOptions { 37 | incremental false 38 | } 39 | } 40 | 41 | dependencies { 42 | compile fileTree(include: ['*.jar'], dir: 'libs') 43 | compile project(':slidinguppanel') 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/DialogDrawerListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.support.v4.widget.DrawerLayout; 6 | import android.view.View; 7 | 8 | import kaichunlin.transition.app.R; 9 | 10 | /** 11 | * Created by Kai on 2015/5/28. 12 | */ 13 | public class DialogDrawerListener implements DrawerLayout.DrawerListener { 14 | private Activity mActivity; 15 | 16 | public DialogDrawerListener(Activity activity) { 17 | mActivity = activity; 18 | 19 | if (mActivity.getPreferences(0).getBoolean("dialog", true)) { 20 | new AlertDialog.Builder(mActivity).setMessage(R.string.dialog_slide_right).setNeutralButton(R.string.dialog_ok, null).create().show(); 21 | } 22 | } 23 | 24 | @Override 25 | public void onDrawerSlide(View drawerView, float slideOffset) { 26 | } 27 | 28 | @Override 29 | public void onDrawerOpened(View drawerView) { 30 | mActivity.getPreferences(0).edit().putBoolean("dialog", false).commit(); 31 | } 32 | 33 | @Override 34 | public void onDrawerClosed(View drawerView) { 35 | } 36 | 37 | @Override 38 | public void onDrawerStateChanged(int newState) { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/DialogPanelSlideListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.view.View; 6 | 7 | import com.sothree.slidinguppanel.SlidingUpPanelLayout; 8 | 9 | import kaichunlin.transition.app.R; 10 | 11 | /** 12 | * Created by Kai on 2015/5/28. 13 | */ 14 | public class DialogPanelSlideListener implements SlidingUpPanelLayout.PanelSlideListener { 15 | private Activity mActivity; 16 | 17 | public DialogPanelSlideListener(Activity activity) { 18 | mActivity = activity; 19 | 20 | if (mActivity.getPreferences(0).getBoolean("dialog", true)) { 21 | new AlertDialog.Builder(mActivity).setMessage(R.string.dialog_slide_up).setNeutralButton(R.string.dialog_ok, null).create().show(); 22 | } 23 | } 24 | 25 | @Override 26 | public void onPanelSlide(View view, float v) { 27 | } 28 | 29 | @Override 30 | public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) { 31 | if (newState == SlidingUpPanelLayout.PanelState.EXPANDED) { 32 | mActivity.getPreferences(0).edit().putBoolean("dialog", false).commit(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/TransitionManager.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Allows the adaption of different UI interactive components (e.g. Drawer) to support transition effects. 7 | */ 8 | public interface TransitionManager extends TransitionOperation { 9 | 10 | /** 11 | * Same as calling addTransition(transitionBuilder.build()). 12 | * 13 | * @param transitionBuilder 14 | */ 15 | void addTransition(AbstractTransitionBuilder transitionBuilder); 16 | 17 | /** 18 | * Adds a transition. 19 | * 20 | * @param transition 21 | */ 22 | void addTransition(Transition transition); 23 | 24 | void addAllTransitions(List transitionsList); 25 | 26 | /** 27 | * Removes a transition, should not be called while transition is in progress. 28 | * 29 | * @param transition 30 | * @return true if a transition is removed, false otherwise. 31 | */ 32 | boolean removeTransition(Transition transition); 33 | 34 | List getTransitions(); 35 | 36 | /** 37 | * Stops and clears all transitions. 38 | */ 39 | void removeAllTransitions(); 40 | 41 | void addTransitionListener(TransitionManagerListener listener); 42 | 43 | void removeTransitionListener(TransitionManagerListener listener); 44 | 45 | void notifyTransitionStart(); 46 | 47 | void notifyTransitionEnd(); 48 | } -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/internal/CustomTransitionController.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.internal; 2 | 3 | import android.support.annotation.CheckResult; 4 | 5 | import com.kaichunlin.transition.transformer.ViewTransformer; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Aggregates a set of {@link ViewTransformer}s. 12 | */ 13 | public class CustomTransitionController extends TransitionController implements Cloneable { 14 | private List mViewTransformerList = new ArrayList<>(); 15 | 16 | public CustomTransitionController() { 17 | super(null); 18 | updateProgressWidth(); 19 | } 20 | 21 | public void addViewTransformer(ViewTransformer mViewTransformer) { 22 | mViewTransformerList.add(mViewTransformer); 23 | } 24 | 25 | @Override 26 | public void updateProgress(float progress) { 27 | for (int i = 0, count = mViewTransformerList.size(); i < count; ++i) { 28 | mViewTransformerList.get(i).updateView(this, getTarget(), progress); 29 | } 30 | } 31 | 32 | @CheckResult 33 | @Override 34 | public CustomTransitionController clone() { 35 | CustomTransitionController newCopy = (CustomTransitionController) super.clone(); 36 | newCopy.mViewTransformerList = new ArrayList<>(mViewTransformerList.size()); 37 | newCopy.mViewTransformerList.addAll(mViewTransformerList); 38 | return newCopy; 39 | } 40 | 41 | protected CustomTransitionController self() { 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/SlidingUpPanelRotateActivity.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.support.v7.widget.Toolbar; 6 | import android.view.View; 7 | 8 | import com.kaichunlin.transition.ViewTransitionBuilder; 9 | import com.kaichunlin.transition.adapter.SlidingUpPanelLayoutAdapter; 10 | import com.sothree.slidinguppanel.SlidingUpPanelLayout; 11 | 12 | import kaichunlin.transition.app.R; 13 | 14 | 15 | public class SlidingUpPanelRotateActivity extends AppCompatActivity { 16 | 17 | private SlidingUpPanelLayoutAdapter mSlidingUpPanelLayoutAdapter; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | 23 | setContentView(R.layout.activity_slideup_actionbar_transparent); 24 | findViewById(R.id.content_bg).setVisibility(View.VISIBLE); 25 | findViewById(R.id.content_bg2).setVisibility(View.VISIBLE); 26 | 27 | final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 28 | setSupportActionBar(toolbar); 29 | 30 | final SlidingUpPanelLayout supl=((SlidingUpPanelLayout)findViewById(R.id.sliding_layout)); 31 | 32 | //code to transit view 33 | mSlidingUpPanelLayoutAdapter = new SlidingUpPanelLayoutAdapter(); 34 | ViewTransitionBuilder.transit(findViewById(R.id.content_bg)).rotationY(90).endRange(0.25f).id("BG").buildFor(mSlidingUpPanelLayoutAdapter); 35 | ViewTransitionBuilder.transit(findViewById(R.id.content_bg2)).rotationY(270, 360).range(0.25f, 0.5f).id("BG_2").buildFor(mSlidingUpPanelLayoutAdapter); 36 | ViewTransitionBuilder.transit(findViewById(R.id.toolbar)).alpha(0f).buildFor(mSlidingUpPanelLayoutAdapter); 37 | supl.addPanelSlideListener(mSlidingUpPanelLayoutAdapter); 38 | 39 | mSlidingUpPanelLayoutAdapter.setPanelSlideListener(new DialogPanelSlideListener(this)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/transformer/ColorTransformer.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.transformer; 2 | 3 | import android.view.View; 4 | 5 | import com.kaichunlin.transition.internal.TransitionController; 6 | 7 | /** 8 | * Helper to transform from one color to another, how this color is used depends on the subclass. 9 | */ 10 | public abstract class ColorTransformer extends ScaledTransformer { 11 | final int startColor; 12 | final int endColor; 13 | 14 | public ColorTransformer(int startColor, int endColor) { 15 | this.startColor = startColor; 16 | this.endColor = endColor; 17 | } 18 | 19 | @Override 20 | protected void updateViewScaled(TransitionController controller, View target, float scaledProgress) { 21 | updateViewWithColor(controller, target, evaluate(scaledProgress, startColor, endColor)); 22 | } 23 | 24 | /** 25 | * Similar to {@link #updateView(TransitionController, View, float)}, with the addition of the current color. 26 | * 27 | * @param controller 28 | * @param target 29 | * @param color The current color calculated using the starting color, the end color, and the current progress. 30 | */ 31 | protected abstract void updateViewWithColor(TransitionController controller, View target, int color); 32 | 33 | private int evaluate(float fraction, int startValue, int endValue) { 34 | int startA = (startValue >>> 24); 35 | int startR = (startValue >> 16) & 0xff; 36 | int startG = (startValue >> 8) & 0xff; 37 | int startB = startValue & 0xff; 38 | 39 | int endA = (endValue >>> 24); 40 | int endR = (endValue >> 16) & 0xff; 41 | int endG = (endValue >> 8) & 0xff; 42 | int endB = endValue & 0xff; 43 | 44 | return ((startA + (int) (fraction * (endA - startA))) << 24) | 45 | ((startR + (int) (fraction * (endR - startR))) << 16) | 46 | ((startG + (int) (fraction * (endG - startG))) << 8) | 47 | ((startB + (int) (fraction * (endB - startB)))); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /slidinguppanel/src/main/java/com/kaichunlin/transition/adapter/SlidingUpPanelLayoutAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.support.annotation.Nullable; 4 | import android.view.View; 5 | 6 | import com.sothree.slidinguppanel.SlidingUpPanelLayout; 7 | 8 | /** 9 | * Adapter for SlidingUpPanelLayout, the transition range goes from 0.0f to 1.0f, where 0.0f is the collapsed state and 1.0f is the expanded state. 10 | *

11 | * Created by Kai-Chun Lin on 2015/4/14. 12 | */ 13 | public class SlidingUpPanelLayoutAdapter extends MenuBaseAdapter implements SlidingUpPanelLayout.PanelSlideListener { 14 | private SlidingUpPanelLayout.PanelSlideListener mListener; 15 | 16 | /** 17 | * Sets a listener that can provide further customization, the respective calls to the listener is performed after the adapter has completed its own processing. 18 | * 19 | * @param listener 20 | */ 21 | public void setPanelSlideListener(@Nullable SlidingUpPanelLayout.PanelSlideListener listener) { 22 | mListener = listener; 23 | } 24 | 25 | @Override 26 | public void onPanelSlide(View panel, float slideOffset) { 27 | startTransition(); 28 | getTransitionManager().updateProgress(slideOffset); 29 | 30 | if (mListener != null) { 31 | mListener.onPanelSlide(panel, slideOffset); 32 | } 33 | } 34 | 35 | @Override 36 | public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) { 37 | if (newState == SlidingUpPanelLayout.PanelState.COLLAPSED) { 38 | getAdapterState().setState(AdapterState.CLOSE); 39 | } else if (newState == SlidingUpPanelLayout.PanelState.EXPANDED) { 40 | getAdapterState().setState(AdapterState.OPEN); 41 | } 42 | stopTransition(); 43 | mListener.onPanelStateChanged(panel, previousState, newState); 44 | } 45 | 46 | @Override 47 | protected MenuOptionHandler createMenuHandler() { 48 | return new DefaultMenuOptionHandler(this, getAdapterState()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 22 | 23 | 26 | 27 | 30 | 31 | 35 | 36 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 28 | 31 | 34 | 37 | 41 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/transformer/ScaledTransformer.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.transformer; 2 | 3 | import android.view.View; 4 | 5 | import com.kaichunlin.transition.internal.TransitionController; 6 | 7 | /** 8 | * The progress valued passed to @link {@link #updateView(TransitionController, View, float)} has taken the start and end range 9 | * into consideration. The method will only be called when the progress is within range, and the value passed has been scaled to be 10 | * always between 0f to 1f. 11 | */ 12 | public abstract class ScaledTransformer implements ViewTransformer { 13 | private final boolean updateOnceOutsideRange; 14 | private boolean updateMinProgress = true; 15 | private boolean updateMaxProgress = true; 16 | 17 | public ScaledTransformer() { 18 | this(false); 19 | } 20 | 21 | public ScaledTransformer(boolean updateOnceOutsideRange) { 22 | this.updateOnceOutsideRange = updateOnceOutsideRange; 23 | } 24 | 25 | @Override 26 | public final void updateView(TransitionController controller, View target, float progress) { 27 | final float start = controller.getStart(); 28 | final float end = controller.getEnd(); 29 | if (progress < start) { 30 | if (updateOnceOutsideRange && updateMinProgress) { 31 | updateViewScaled(controller, target, 0); 32 | } 33 | updateMinProgress = false; 34 | return; 35 | } 36 | updateMinProgress = true; 37 | if (progress > end) { 38 | if (updateOnceOutsideRange && updateMaxProgress) { 39 | updateViewScaled(controller, target, 1); 40 | } 41 | updateMaxProgress = false; 42 | return; 43 | } 44 | updateMaxProgress = true; 45 | updateViewScaled(controller, target, (progress - start) / (end - start)); 46 | } 47 | 48 | /** 49 | * @param controller 50 | * @param target 51 | * @param scaledProgress 52 | */ 53 | protected abstract void updateViewScaled(TransitionController controller, View target, float scaledProgress); 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/internal/debug/TraceAnimationListener.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.internal.debug; 2 | 3 | import com.kaichunlin.transition.animation.Animation; 4 | import com.kaichunlin.transition.animation.AnimationListener; 5 | import com.kaichunlin.transition.TransitionConfig; 6 | 7 | /** 8 | * Use Log.i to output the start and end of an animation 9 | */ 10 | public class TraceAnimationListener implements AnimationListener { 11 | private long mStart; 12 | 13 | @Override 14 | public void onAnimationStart(Animation animationManager) { 15 | mStart = System.currentTimeMillis(); 16 | if (TransitionConfig.isDebug()) { 17 | try { 18 | throw new IllegalStateException("Tracer: onAnimationStart: " + animationManager); 19 | } catch (IllegalStateException e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | } 24 | 25 | @Override 26 | public void onAnimationEnd(Animation animationManager) { 27 | if (TransitionConfig.isDebug()) { 28 | try { 29 | throw new IllegalStateException("Tracer: onAnimationEnd, duration=" + (System.currentTimeMillis() - mStart) + ": " + animationManager); 30 | } catch (IllegalStateException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | 36 | @Override 37 | public void onAnimationCancel(Animation animationManager) { 38 | if (TransitionConfig.isDebug()) { 39 | try { 40 | throw new IllegalStateException("Tracer: onAnimationCancel, duration=" + (System.currentTimeMillis() - mStart) + ": " + animationManager); 41 | } catch (IllegalStateException e) { 42 | e.printStackTrace(); 43 | } 44 | } 45 | } 46 | 47 | @Override 48 | public void onAnimationReset(Animation animationManager) { 49 | if (TransitionConfig.isDebug()) { 50 | try { 51 | throw new IllegalStateException("Tracer: onAnimationReset, duration=" + (System.currentTimeMillis() - mStart) + ": " + animationManager); 52 | } catch (IllegalStateException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_slideup_actionbar_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 20 | 21 | 28 | 29 | 36 | 37 | 47 | 48 | 49 | 56 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/MenuOptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.view.Menu; 7 | 8 | /** 9 | */ 10 | public interface MenuOptionHandler { 11 | AdapterState getAdapterState(); 12 | 13 | /** 14 | * Syncs current state of Menu with transitions 15 | * 16 | * @param activity 17 | * @param menu 18 | */ 19 | void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu); 20 | 21 | /** 22 | * Syncs current state of Menu with transitions 23 | * 24 | * @param activity 25 | * @param menu 26 | * @param adapterState a different {@link AdapterState} than the one retrieved by calling getAdapterState() 27 | */ 28 | void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu, @NonNull AdapterState adapterState); 29 | 30 | /** 31 | * Sets transitions to {@link android.view.MenuItem} in both the opened state and closed state with only 32 | * the opened state's transition specified, which is used to build menu transition in the closed state 33 | * 34 | * @param activity 35 | * @param openConfig 36 | */ 37 | void setupOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig); 38 | 39 | /** 40 | * Sets transitions to {@link android.view.MenuItem} in the opened state 41 | * 42 | * @param activity 43 | * @param openConfig 44 | */ 45 | void setupOpenOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig); 46 | 47 | /** 48 | * Sets transitions to {@link android.view.MenuItem} in the closed state 49 | * 50 | * @param activity 51 | * @param closeConfig 52 | */ 53 | void setupCloseOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration closeConfig); 54 | 55 | /** 56 | * Sets transitions to {@link android.view.MenuItem} in both the opened state and closed state 57 | * 58 | * @param activity 59 | * @param openConfig 60 | * @param closeConfig 61 | */ 62 | void setupOptions(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig, @Nullable MenuOptionConfiguration closeConfig); 63 | 64 | /** 65 | * Clears menu transition 66 | */ 67 | void clearOptions(); 68 | 69 | MenuOptionConfiguration getOpenConfig(); 70 | 71 | MenuOptionConfiguration getCloseConfig(); 72 | } 73 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/Transition.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import android.support.annotation.UiThread; 4 | import android.view.View; 5 | import android.view.animation.Interpolator; 6 | 7 | /** 8 | * Represent the operations that should be performed with the given progress, usually on a View. 9 | */ 10 | public interface Transition extends TransitionOperation, Cloneable { 11 | 12 | /** 13 | * Sets an ID the transition, used internally for debugging purpose. 14 | * 15 | * @param id 16 | * @return 17 | */ 18 | AbstractTransition setId(String id); 19 | 20 | /** 21 | * 22 | * @return ID assigned for the transition, used internally for debugging purpose. 23 | */ 24 | String getId(); 25 | 26 | /** 27 | * Sets the transition progress, many times in the range of 0.0f ~ 1.0f, but not necessarily so. 28 | * 29 | * @param progress 30 | */ 31 | @UiThread 32 | void setProgress(float progress); 33 | 34 | /** 35 | * Reverses the transition. 36 | * 37 | * @return itself 38 | */ 39 | Transition reverse(); 40 | 41 | /** 42 | * A {@link AbstractTransition.Setup} is an object that configures how a Transition for a specific effect. 43 | *

44 | * For examples see MenuItemTransitionBuilder.setupAnimation(MenuItem, TransitionControllerManager, int, int) 45 | * and ViewTransitionBuilder#setupAnimation(TransitionControllerManager). 46 | * 47 | * @param setup 48 | * @return itself 49 | */ 50 | Transition setSetup(T setup); 51 | 52 | /** 53 | * 54 | * @return A carbon clone of this object that can be used independently from the object it's cloned from. 55 | */ 56 | Transition clone(); 57 | 58 | /** 59 | * 60 | * @param interpolator The Interpolator that should be used. 61 | * @return itself 62 | */ 63 | Transition setInterpolator(Interpolator interpolator); 64 | 65 | /** 66 | * 67 | * @param target The View this transition would modify. 68 | */ 69 | void setTarget(View target); 70 | 71 | /** 72 | * 73 | * @return The View this transition would modify. 74 | */ 75 | View getTarget(); 76 | 77 | /** 78 | * 79 | * @param updateStateAfterUpdateProgress Whether or not to update a controller's enable state after 80 | * each {@link #updateProgress(float)} call. 81 | * @return itself 82 | */ 83 | Transition setUpdateStateAfterUpdateProgress(boolean updateStateAfterUpdateProgress); 84 | } 85 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_drawer_menuitem.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 18 | 19 | 20 | 25 | 26 | 31 | 35 | 39 | 43 | 44 | 45 | 46 | 47 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Transition 3 | 4 | View Pager (In Progress) 5 | Sliding Up Panel 6 | Rotate Image 7 | Drawer View Transition 8 | MenuItem Transition 9 | Shifting Gradient Drawer 10 | 11 | Flip and Fade 12 | Shrink and Fade 13 | Rotate 14 | 15 | Slowly slides your finger from the left edge of the screen towards right to see the transition. 16 | Press on "The Awesome Sliding Up Panel" and slowly slides your finger up to see the transition. 17 | OK 18 | 19 | The Awesome Sliding Up Panel 20 | Rotate and Slide 21 | Rotating ActionBar 22 | Sliding ActionBar and Subviews 23 | Fading ActionBar 24 | Change ActionBar Color 25 | Change ActionBar Color HSV 26 | Background to Grayscale 27 | Cascade Fading 28 | Cascade Rolling 29 | Rotate and Shrink View 30 | Sliding Subviews 31 | Sliding Background 32 | Animate 33 | Pause 34 | Resume 35 | Interpolator 36 | Default 37 | Linear 38 | Accelerate 39 | Decelerate 40 | Fast Out Linear In 41 | Anticipate 42 | 43 | Open navigation drawer 44 | Close navigation drawer 45 | 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/MenuBaseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.view.Menu; 7 | 8 | /** 9 | * Adds support for {@link android.view.MenuItem} transition. 10 | */ 11 | public abstract class MenuBaseAdapter extends AbstractAdapter implements MenuOptionHandler { 12 | MenuOptionHandler mMenuHandler; 13 | 14 | protected abstract MenuOptionHandler createMenuHandler(); 15 | 16 | @Override 17 | public void removeAllTransitions() { 18 | super.removeAllTransitions(); 19 | createMenuHandlerIfNecessary(); 20 | mMenuHandler.clearOptions(); 21 | } 22 | 23 | protected MenuOptionHandler getMenuOptionHandler() { 24 | return mMenuHandler; 25 | } 26 | 27 | private void createMenuHandlerIfNecessary() { 28 | if (mMenuHandler == null) { 29 | mMenuHandler = createMenuHandler(); 30 | } 31 | } 32 | 33 | public void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu) { 34 | createMenuHandlerIfNecessary(); 35 | mMenuHandler.onCreateOptionsMenu(activity, menu); 36 | } 37 | 38 | public void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu, AdapterState adapterState) { 39 | createMenuHandlerIfNecessary(); 40 | mMenuHandler.onCreateOptionsMenu(activity, menu, adapterState); 41 | } 42 | 43 | @Override 44 | public void setupOption(@NonNull Activity activity, MenuOptionConfiguration openConfig) { 45 | createMenuHandlerIfNecessary(); 46 | mMenuHandler.setupOption(activity, openConfig); 47 | } 48 | 49 | @Override 50 | public void setupOpenOption(@NonNull Activity activity, MenuOptionConfiguration openConfig) { 51 | createMenuHandlerIfNecessary(); 52 | mMenuHandler.setupOpenOption(activity, openConfig); 53 | } 54 | 55 | @Override 56 | public void setupCloseOption(@NonNull Activity activity, MenuOptionConfiguration closeConfig) { 57 | createMenuHandlerIfNecessary(); 58 | mMenuHandler.setupCloseOption(activity, closeConfig); 59 | } 60 | 61 | @Override 62 | public void setupOptions(@NonNull Activity activity, MenuOptionConfiguration openConfig, MenuOptionConfiguration closeConfig) { 63 | createMenuHandlerIfNecessary(); 64 | mMenuHandler.setupOptions(activity, openConfig, closeConfig); 65 | } 66 | 67 | @Override 68 | public void clearOptions() { 69 | mMenuHandler.clearOptions(); 70 | } 71 | 72 | @Nullable 73 | @Override 74 | public MenuOptionConfiguration getOpenConfig() { 75 | return mMenuHandler.getOpenConfig(); 76 | } 77 | 78 | @Nullable 79 | @Override 80 | public MenuOptionConfiguration getCloseConfig() { 81 | return mMenuHandler.getCloseConfig(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 25 | 26 | 32 | 33 | 37 | 38 | 42 | 43 | 47 | 48 | 52 | 53 | 57 | 58 | 62 | 63 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/Animation.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | import android.support.annotation.IntRange; 4 | import android.support.annotation.UiThread; 5 | 6 | /** 7 | * Interface for an Animation. 8 | */ 9 | @UiThread 10 | public interface Animation { 11 | 12 | /** 13 | * @param listener 14 | */ 15 | void addAnimationListener(AnimationListener listener); 16 | 17 | /** 18 | * @param listener 19 | */ 20 | void removeAnimationListener(AnimationListener listener); 21 | 22 | /** 23 | * 24 | * @param duration Length of the animation. 25 | */ 26 | void setDuration(@IntRange(from = 0) int duration); 27 | 28 | int getDuration(); 29 | 30 | /** 31 | * 32 | * @param reverse If true, the animation will be played backward. 33 | */ 34 | void setReverseAnimation(boolean reverse); 35 | 36 | /** 37 | * @return true if {@link #setReverseAnimation(boolean)} has been set to true. 38 | */ 39 | boolean isReverseAnimation(); 40 | 41 | /** 42 | * Starts the animation with the default duration (300 ms). 43 | */ 44 | void startAnimation(); 45 | 46 | /** 47 | * Starts the animation with the specified duration. 48 | * 49 | * @param duration 50 | */ 51 | void startAnimation(@IntRange(from = 0) int duration); 52 | 53 | /** 54 | * Same as {@link #startAnimation()}, but with an added delay before the animation is started. 55 | 56 | * @param delay 57 | */ 58 | void startAnimationDelayed(@IntRange(from = 0) int delay); 59 | 60 | /** 61 | * Same as {@link #startAnimation(int)}, but with an added delay before the animation is started. 62 | * 63 | * @param duration 64 | * @param delay 65 | */ 66 | void startAnimationDelayed(@IntRange(from = 0) int duration, @IntRange(from = 0) int delay); 67 | 68 | /** 69 | * 70 | * @return Is the Animation in the started state. 71 | */ 72 | boolean isAnimating(); 73 | 74 | /** 75 | * Cancels the animation, i.e. the affected Views will retain their current states but will no longer 76 | * advance the animation. A canceled animation cannot be resumed. 77 | */ 78 | void cancelAnimation(); 79 | 80 | /** 81 | * Pauses the animation, i.e. the affected Views will retain their current states but will no longer 82 | * advance the animation. {@link #resumeAnimation()} can be called afterwards. 83 | */ 84 | void pauseAnimation(); 85 | 86 | /** 87 | * Resumes the animation, i.e. the affected Views will continue the animations from the paused states. 88 | */ 89 | void resumeAnimation(); 90 | 91 | /** 92 | * Ends the animation, i.e. the affected Views will be assigned their final states. 93 | */ 94 | void endAnimation(); 95 | 96 | /** 97 | * Forces the animation's end state to be applied immediately even if the animation has not started. 98 | * If the animation has started, it will end immediately, the effect would be the same as calling 99 | * {@link #endAnimation()}. 100 | */ 101 | void forceEndState(); 102 | 103 | /** 104 | * Stops the animation, i.e. the affected Views will be reverted to their original states at the 105 | * beginning of the animation. 106 | */ 107 | void resetAnimation(); 108 | } 109 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/MenuAnimationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.IntRange; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.view.Menu; 8 | 9 | /** 10 | * Similar to {@link AnimationAdapter} with the addition of MenuItem animation support. 11 | */ 12 | public class MenuAnimationAdapter extends AnimationAdapter implements MenuOptionHandler { 13 | private MenuOptionHandler mMenuOptionHandler = new DefaultMenuOptionHandler(this, getAdapterState()); 14 | 15 | public MenuAnimationAdapter() { 16 | super(); 17 | } 18 | 19 | /** 20 | * Wraps an existing {@link MenuBaseAdapter} to reuse its onCreateOptionsMenu(...) logic and its transition effects 21 | * 22 | * @param adapter 23 | */ 24 | public MenuAnimationAdapter(@Nullable TransitionAdapter adapter) { 25 | super(adapter); 26 | } 27 | 28 | @Override 29 | public void startAnimation(@IntRange(from = 0) int duration) { 30 | AdapterState adapterState = getAdapterState(); 31 | adapterState.setState(adapterState.getState() == AdapterState.OPEN ? AdapterState.CLOSE : AdapterState.OPEN); 32 | 33 | setReverseAnimation(adapterState.isOpen()); 34 | super.startAnimation(duration); 35 | } 36 | 37 | public void setMenuOptionHandler(@NonNull MenuOptionHandler menuOptionHandler) { 38 | mMenuOptionHandler = menuOptionHandler; 39 | } 40 | 41 | private MenuOptionHandler getHandler() { 42 | return getAdapter() == null ? mMenuOptionHandler : (MenuOptionHandler) getAdapter(); 43 | } 44 | 45 | public void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu) { 46 | if (getAdapter() == null) { 47 | mMenuOptionHandler.onCreateOptionsMenu(activity, menu); 48 | } else { 49 | ((MenuBaseAdapter) getAdapter()).onCreateOptionsMenu(activity, menu, mMenuOptionHandler.getAdapterState()); 50 | } 51 | } 52 | 53 | @Override 54 | public void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu, @NonNull AdapterState adapterState) { 55 | getHandler().onCreateOptionsMenu(activity, menu, adapterState); 56 | } 57 | 58 | public void setupOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig) { 59 | getHandler().setupOption(activity, openConfig); 60 | } 61 | 62 | public void setupOpenOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig) { 63 | getHandler().setupOpenOption(activity, openConfig); 64 | } 65 | 66 | public void setupCloseOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration closeConfig) { 67 | getHandler().setupCloseOption(activity, closeConfig); 68 | } 69 | 70 | public void setupOptions(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig, @Nullable MenuOptionConfiguration closeConfig) { 71 | getHandler().setupOptions(activity, openConfig, closeConfig); 72 | } 73 | 74 | @Override 75 | public void clearOptions() { 76 | getHandler().clearOptions(); 77 | } 78 | 79 | @Override 80 | public MenuOptionConfiguration getOpenConfig() { 81 | return getHandler().getOpenConfig(); 82 | } 83 | 84 | @Override 85 | public MenuOptionConfiguration getCloseConfig() { 86 | return getHandler().getCloseConfig(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /upload.gradle: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * JCenter publishing logic courtesy of https://www.virag.si/2015/01/publishing-gradle-android-library-to-jcenter/ 5 | */ 6 | def siteUrl = 'https://github.com/kaichunlin/android-transition' // Homepage URL of the library 7 | def gitUrl = 'https://github.com/kaichunlin/android-transition.git' // Git repository URL 8 | group = "com.github.kaichunlin.transition" // Maven Group ID for the artifact 9 | 10 | Properties moduleProperties = new Properties() 11 | moduleProperties.load(project.file('module.properties').newDataInputStream()) 12 | 13 | ext.artifactId = moduleProperties.getProperty("module.name") 14 | 15 | install { 16 | repositories.mavenInstaller { 17 | // This generates POM.xml with proper parameters 18 | pom { 19 | project { 20 | packaging 'aar' 21 | 22 | // Add your description here 23 | name moduleProperties.getProperty("module.pom_name") 24 | // http://stackoverflow.com/questions/30475780/how-to-change-the-default-artifactid-of-maven-metadata-xml-when-uploading-to-bin/30476385#30476385 25 | // artifactId = moduleProperties.getProperty("module.artifactId") 26 | description moduleProperties.getProperty("module.desc") 27 | url siteUrl 28 | 29 | // Set your license 30 | licenses { 31 | license { 32 | name 'The Apache Software License, Version 2.0' 33 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 34 | } 35 | } 36 | developers { 37 | developer { 38 | id 'kaichunlin' 39 | name 'Kai-Chun Lin' 40 | email 'kaichun.lin@gmail.com' 41 | } 42 | } 43 | scm { 44 | connection gitUrl 45 | developerConnection gitUrl 46 | url siteUrl 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | task sourcesJar(type: Jar) { 54 | from android.sourceSets.main.java.srcDirs 55 | classifier = 'sources' 56 | } 57 | 58 | task javadoc(type: Javadoc) { 59 | source = android.sourceSets.main.java.srcDirs 60 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 61 | } 62 | 63 | task javadocJar(type: Jar, dependsOn: javadoc) { 64 | classifier = 'javadoc' 65 | from javadoc.destinationDir 66 | } 67 | 68 | artifacts { 69 | archives javadocJar 70 | archives sourcesJar 71 | } 72 | 73 | Properties properties = new Properties() 74 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 75 | 76 | def test = false 77 | 78 | bintray { 79 | user = properties.getProperty("bintray.user") 80 | key = properties.getProperty("bintray.apikey") 81 | 82 | configurations = ['archives'] 83 | dryRun = test //Whether to run this as dry-run, without deploying 84 | publish = !test //If version should be auto published after an upload 85 | pkg { 86 | repo = "maven" 87 | name = moduleProperties.getProperty("module.bintray") 88 | websiteUrl = siteUrl 89 | vcsUrl = gitUrl 90 | licenses = ["Apache-2.0"] 91 | publish = !test 92 | version { 93 | gpg { 94 | sign = true //Determines whether to GPG sign the files. The default is false 95 | // passphrase = properties.getProperty("bintray.gpg.password") //Optional. The passphrase for GPG signing' 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_drawer_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 22 | 27 | 28 | 32 | 33 | 38 | 39 | 45 | 46 | 51 | 52 | 57 | 58 | 63 | 64 | 69 | 70 | 71 | 72 | 73 | 74 | 84 | 85 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/AbstractAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.kaichunlin.transition.AbstractTransitionBuilder; 6 | import com.kaichunlin.transition.DefaultTransitionManager; 7 | import com.kaichunlin.transition.Transition; 8 | import com.kaichunlin.transition.TransitionManagerListener; 9 | import com.kaichunlin.transition.TransitionManager; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Provides implementation shared by most adapters. 15 | */ 16 | public abstract class AbstractAdapter implements TransitionAdapter { 17 | private TransitionManager mTransitionManager = new DefaultTransitionManager(); 18 | private AdapterState mAdapterState; 19 | 20 | public AbstractAdapter() { 21 | mAdapterState = new AdapterState(); 22 | } 23 | 24 | public AbstractAdapter(@NonNull AdapterState adapterState) { 25 | mAdapterState = adapterState; 26 | } 27 | 28 | public void setTransitionManager(TransitionManager manager) { 29 | mTransitionManager = manager; 30 | } 31 | 32 | public TransitionManager getTransitionManager() { 33 | return mTransitionManager; 34 | } 35 | 36 | @Override 37 | public AdapterState getAdapterState() { 38 | return mAdapterState; 39 | } 40 | 41 | @Override 42 | public void addTransition(@NonNull AbstractTransitionBuilder builder) { 43 | mTransitionManager.addTransition(builder); 44 | } 45 | 46 | @Override 47 | public void addTransition(@NonNull Transition transition) { 48 | mTransitionManager.addTransition(transition); 49 | } 50 | 51 | @Override 52 | public void addAllTransitions(@NonNull List transitionsList) { 53 | mTransitionManager.addAllTransitions(transitionsList); 54 | } 55 | 56 | @Override 57 | public boolean removeTransition(@NonNull Transition transition) { 58 | return mTransitionManager.removeTransition(transition); 59 | } 60 | 61 | @Override 62 | public void removeAllTransitions() { 63 | stopTransition(); 64 | mTransitionManager.removeAllTransitions(); 65 | } 66 | 67 | @Override 68 | public List getTransitions() { 69 | return mTransitionManager.getTransitions(); 70 | } 71 | 72 | @Override 73 | public boolean startTransition() { 74 | return startTransition(getAdapterState().isOpen()?1:0); 75 | } 76 | 77 | @Override 78 | public boolean startTransition(float progress) { 79 | if (mAdapterState.isTransiting()) { 80 | return false; 81 | } 82 | 83 | mTransitionManager.startTransition(progress); 84 | mAdapterState.setTransiting(true); 85 | return true; 86 | } 87 | 88 | @Override 89 | public void updateProgress(float value) { 90 | mTransitionManager.updateProgress(value); 91 | } 92 | 93 | @Override 94 | public void stopTransition() { 95 | if (!mAdapterState.isTransiting()) { 96 | return; 97 | } 98 | 99 | mTransitionManager.stopTransition(); 100 | mAdapterState.setTransiting(false); 101 | } 102 | 103 | @Override 104 | public void addTransitionListener(TransitionManagerListener listener) { 105 | mTransitionManager.addTransitionListener(listener); 106 | } 107 | 108 | @Override 109 | public void removeTransitionListener(TransitionManagerListener listener) { 110 | mTransitionManager.removeTransitionListener(listener); 111 | } 112 | 113 | @Override 114 | public void notifyTransitionStart() { 115 | mTransitionManager.notifyTransitionStart(); 116 | } 117 | 118 | @Override 119 | public void notifyTransitionEnd() { 120 | mTransitionManager.notifyTransitionEnd(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/DrawerListenerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.IdRes; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.v4.widget.DrawerLayout; 8 | import android.support.v7.app.ActionBarDrawerToggle; 9 | import android.view.View; 10 | 11 | /** 12 | * Adapter for DrawerListener, the transition range goes from 0.0f to 1.0f, where 0.0f is the closed state and 1.0f is the opened state. 13 | */ 14 | public class DrawerListenerAdapter extends MenuBaseAdapter implements DrawerLayout.DrawerListener { 15 | private final ActionBarDrawerToggle mDrawerToggle; 16 | private DrawerLayout.DrawerListener mDrawerListener; 17 | private int mDrawerId; 18 | private DrawerLayout mDrawerLayout; 19 | 20 | public DrawerListenerAdapter(@NonNull ActionBarDrawerToggle mDrawerToggle) { 21 | this(mDrawerToggle, 0); 22 | } 23 | 24 | public DrawerListenerAdapter(@NonNull ActionBarDrawerToggle mDrawerToggle, @IdRes int mDrawerId) { 25 | this.mDrawerToggle = mDrawerToggle; 26 | this.mDrawerId = mDrawerId; 27 | } 28 | 29 | /** 30 | * @param activity 31 | * @return is the drawer in the opened state 32 | */ 33 | public boolean isOpened(@NonNull Activity activity) { 34 | return mDrawerLayout.isDrawerOpen(activity.findViewById(mDrawerId)); 35 | } 36 | 37 | @Override 38 | public void onDrawerOpened(@NonNull View view) { 39 | mDrawerToggle.onDrawerOpened(view); 40 | getAdapterState().setState(AdapterState.OPEN); 41 | stopTransition(); 42 | 43 | if (mDrawerListener != null) { 44 | mDrawerListener.onDrawerOpened(view); 45 | } 46 | } 47 | 48 | @Override 49 | public void onDrawerClosed(@NonNull View view) { 50 | mDrawerToggle.onDrawerClosed(view); 51 | getAdapterState().setState(AdapterState.CLOSE); 52 | stopTransition(); 53 | 54 | if (mDrawerListener != null) { 55 | mDrawerListener.onDrawerClosed(view); 56 | } 57 | } 58 | 59 | @Override 60 | public void onDrawerStateChanged(int state) { 61 | mDrawerToggle.onDrawerStateChanged(state); 62 | switch (state) { 63 | case DrawerLayout.STATE_DRAGGING: 64 | case DrawerLayout.STATE_SETTLING: 65 | startTransition(); 66 | break; 67 | case DrawerLayout.STATE_IDLE: 68 | stopTransition(); 69 | break; 70 | } 71 | 72 | if (mDrawerListener != null) { 73 | mDrawerListener.onDrawerStateChanged(state); 74 | } 75 | } 76 | 77 | @Override 78 | public void onDrawerSlide(View view, float slideOffset) { 79 | getTransitionManager().updateProgress(slideOffset); 80 | 81 | if (view == null) { 82 | return; 83 | } 84 | mDrawerToggle.onDrawerSlide(view, slideOffset); 85 | 86 | if (mDrawerListener != null) { 87 | mDrawerListener.onDrawerSlide(view, slideOffset); 88 | } 89 | } 90 | 91 | /** 92 | * @param mDrawerListener 93 | */ 94 | public void setDrawerListener(@Nullable DrawerLayout.DrawerListener mDrawerListener) { 95 | this.mDrawerListener = mDrawerListener; 96 | } 97 | 98 | /** 99 | * @param drawerLayout 100 | */ 101 | public void setDrawerLayout(@NonNull DrawerLayout drawerLayout) { 102 | this.mDrawerLayout = drawerLayout; 103 | drawerLayout.setDrawerListener(this); 104 | } 105 | 106 | /** 107 | * @param drawerId Drawer view ID 108 | */ 109 | public void setDrawerId(@IdRes int drawerId) { 110 | mDrawerId = drawerId; 111 | } 112 | 113 | @Override 114 | protected MenuOptionHandler createMenuHandler() { 115 | return new DefaultMenuOptionHandler(this, getAdapterState()); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/ViewTransition.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import android.support.annotation.CheckResult; 4 | import android.support.annotation.Nullable; 5 | import android.support.annotation.UiThread; 6 | 7 | import com.kaichunlin.transition.internal.TransitionController; 8 | import com.kaichunlin.transition.internal.TransitionControllerManager; 9 | import com.kaichunlin.transition.util.TransitionStateLogger; 10 | 11 | /** 12 | * Provides transitions effects to all views other than one controlled by {@link android.view.MenuItem}. 13 | */ 14 | @UiThread 15 | public class ViewTransition extends AbstractTransition { 16 | private TransitionControllerManager transitionControllerManager; 17 | 18 | public ViewTransition() { 19 | this(null, null); 20 | } 21 | 22 | public ViewTransition(@Nullable Setup setup) { 23 | this(null, setup); 24 | } 25 | 26 | /** 27 | * @param id Unique ID that can identify the transition. 28 | * @param setup Creates the {@link TransitionController}'s when {@link #startTransition()} is called. 29 | */ 30 | public ViewTransition(@Nullable String id, @Nullable Setup setup) { 31 | super(id); 32 | setSetup(setup); 33 | } 34 | 35 | @Override 36 | public ViewTransition reverse() { 37 | super.reverse(); 38 | if (transitionControllerManager != null) { 39 | transitionControllerManager.reverse(); 40 | } 41 | return self(); 42 | } 43 | 44 | @Override 45 | public boolean startTransition() { 46 | //caches result 47 | if (transitionControllerManager == null) { 48 | transitionControllerManager = new TransitionControllerManager(getId()); 49 | if (mInterpolator != null) { 50 | transitionControllerManager.setInterpolator(mInterpolator); 51 | } 52 | if (TransitionConfig.isDebug()) { 53 | mTarget.setTag(R.id.debug_id, new TransitionStateLogger(getId())); 54 | } 55 | transitionControllerManager.setTarget(mTarget); 56 | transitionControllerManager.setUpdateStateAfterUpdateProgress(mUpdateStateAfterUpdateProgress); 57 | 58 | final int size = mSetupList.size(); 59 | for (int i = 0; i < size; i++) { 60 | mSetupList.get(i).setupAnimation(transitionControllerManager); 61 | } 62 | if (mReverse) { 63 | transitionControllerManager.reverse(); 64 | } 65 | } 66 | transitionControllerManager.start(); 67 | return true; 68 | } 69 | 70 | @Override 71 | public void updateProgress(float progress) { 72 | transitionControllerManager.updateProgress(progress); 73 | } 74 | 75 | @Override 76 | public void stopTransition() { 77 | if (transitionControllerManager != null) { 78 | transitionControllerManager.end(); 79 | } 80 | } 81 | 82 | @CheckResult 83 | @Override 84 | public ViewTransition clone() { 85 | ViewTransition newCopy = (ViewTransition) super.clone(); 86 | //set to null for now, equivalent to calling invalidate() 87 | newCopy.transitionControllerManager = null; 88 | return newCopy; 89 | } 90 | 91 | @Override 92 | protected void invalidate() { 93 | stopTransition(); 94 | transitionControllerManager = null; 95 | } 96 | 97 | @Override 98 | protected ViewTransition self() { 99 | return this; 100 | } 101 | 102 | /** 103 | * Creates the {@link TransitionController}'s when {@link #startTransition()} is called. 104 | */ 105 | public interface Setup extends AbstractTransition.Setup { 106 | /** 107 | * @param manager The {@link TransitionControllerManager} that the created {@link TransitionController} should be added to 108 | */ 109 | void setupAnimation(TransitionControllerManager manager); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/util/TransitionUtil.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.util; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.support.annotation.IdRes; 6 | import android.support.annotation.NonNull; 7 | import android.support.v7.view.menu.ActionMenuItemView; 8 | import android.support.v7.widget.ActionMenuView; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.ViewTreeObserver; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Support utility methods. 19 | */ 20 | public class TransitionUtil { 21 | 22 | /** 23 | * Get the list of visible MenuItems 24 | * 25 | * @param toolbar 26 | * @return the list of visible MenuItems 27 | */ 28 | public static List getVisibleMenuItemList(@NonNull Toolbar toolbar) { 29 | List list = new ArrayList<>(); 30 | for (int i = 0; i < toolbar.getChildCount(); i++) { 31 | final View v = toolbar.getChildAt(i); 32 | if (v instanceof ActionMenuView) { 33 | int childCount = ((ActionMenuView) v).getChildCount(); 34 | for (int j = 0; j < childCount; j++) { 35 | final View innerView = ((ActionMenuView) v).getChildAt(j); 36 | if (innerView instanceof ActionMenuItemView) { 37 | list.add(((ActionMenuItemView) innerView).getItemData()); 38 | } 39 | } 40 | } 41 | } 42 | return list; 43 | } 44 | 45 | /** 46 | * Search for a particular menu 47 | * 48 | * @param toolbar 49 | * @param menuId 50 | * @return the corresponding MenuItem, or null if not found 51 | */ 52 | public static MenuItem getMenuItem(@NonNull Toolbar toolbar, @IdRes int menuId) { 53 | View v; 54 | int childCount; 55 | View innerView; 56 | MenuItem menuItem; 57 | for (int i = 0; i < toolbar.getChildCount(); i++) { 58 | v = toolbar.getChildAt(i); 59 | if (v instanceof ActionMenuView) { 60 | childCount = ((ActionMenuView) v).getChildCount(); 61 | for (int j = 0; j < childCount; j++) { 62 | innerView = ((ActionMenuView) v).getChildAt(j); 63 | if (innerView instanceof ActionMenuItemView) { 64 | menuItem = ((ActionMenuItemView) innerView).getItemData(); 65 | if (menuItem.getItemId() == menuId) { 66 | return menuItem; 67 | } 68 | } 69 | } 70 | } 71 | } 72 | return null; 73 | } 74 | 75 | /** 76 | * Helper that removes the drudgery of using ViewTreeObserver 77 | * 78 | * @param view 79 | * @param listener 80 | */ 81 | public static void executeOnGlobalLayout(@NonNull final View view, @NonNull final ViewTreeObserver.OnGlobalLayoutListener listener) { 82 | ViewTreeObserver vto = view.getViewTreeObserver(); 83 | vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 84 | @Override 85 | public void onGlobalLayout() { 86 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 87 | view.getViewTreeObserver() 88 | .removeGlobalOnLayoutListener(this); 89 | } else { 90 | view.getViewTreeObserver() 91 | .removeOnGlobalLayoutListener(this); 92 | } 93 | 94 | listener.onGlobalLayout(); 95 | } 96 | }); 97 | } 98 | 99 | /** 100 | * Helper that removes the drudgery of using ViewTreeObserver 101 | * 102 | * @param activity used to retrieve a View that can be 103 | * @param listener 104 | */ 105 | public static void executeOnGlobalLayout(@NonNull final Activity activity, @NonNull final ViewTreeObserver.OnGlobalLayoutListener listener) { 106 | executeOnGlobalLayout(activity.getWindow().getDecorView().findViewById(android.R.id.content), listener); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/util/TransitionStateLogger.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.util; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.util.ArrayMap; 5 | import android.util.Log; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Calendar; 9 | import java.util.List; 10 | 11 | /** 12 | * Used to help trace the state of transition for debugging purpose. 13 | */ 14 | public class TransitionStateLogger { 15 | public final String mId; 16 | private Calendar mStartTime; 17 | private Calendar mEndTime; 18 | private ArrayMap> tranStateMap = new ArrayMap<>(); 19 | 20 | public TransitionStateLogger(@NonNull String id) { 21 | mId = id; 22 | } 23 | 24 | public void start() { 25 | mStartTime = Calendar.getInstance(); 26 | } 27 | 28 | public void end() { 29 | mEndTime = Calendar.getInstance(); 30 | } 31 | 32 | public void append(@NonNull String id, @NonNull Object appendingClass, @NonNull String message) { 33 | append(new TransitionState(id, appendingClass, message)); 34 | } 35 | 36 | public void append(@NonNull TransitionState transitionState) { 37 | List list = tranStateMap.get(transitionState.subId); 38 | if (list == null) { 39 | list = new ArrayList<>(); 40 | tranStateMap.put(transitionState.subId, list); 41 | } 42 | list.add(transitionState); 43 | } 44 | 45 | public void clear() { 46 | tranStateMap.clear(); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | StringBuilder sb = new StringBuilder(128); 52 | sb.append("---------- "); 53 | sb.append(mId); 54 | sb.append(" Start: "); 55 | sb.append(mStartTime); 56 | sb.append(" ----------"); 57 | 58 | int size = tranStateMap.size(); 59 | for (int i = 0; i < size; i++) { 60 | sb.append("\n"); 61 | sb.append(tranStateMap.valueAt(i)); 62 | } 63 | sb.append("\n--------------------------"); 64 | sb.append(mEndTime); 65 | sb.append("\n--------------------------"); 66 | return sb.toString(); 67 | } 68 | 69 | public void print() { 70 | Log.e(getClass().getSimpleName(), "------------- " + mId + " -------------"); 71 | boolean first; 72 | long baseTime = 0; 73 | final int size = tranStateMap.size(); 74 | List stateList; 75 | for (int i = 0; i < size; i++) { 76 | first = true; 77 | stateList = tranStateMap.valueAt(i); 78 | final int size2 = stateList.size(); 79 | for (int j = 0; j < size2; j++) { 80 | if (first) { 81 | baseTime = stateList.get(j).time; 82 | } 83 | Log.e(getClass().getSimpleName(), stateList.get(j).toString(baseTime)); 84 | } 85 | } 86 | Log.i(getClass().getSimpleName(), "-----------------------------"); 87 | } 88 | 89 | /** 90 | * The state of the transition for a given moment 91 | */ 92 | public static class TransitionState { 93 | private String subId; 94 | private Object mClass; 95 | private String message; 96 | private long time; 97 | 98 | private TransitionState(String subId, Object appendingClass, String message) { 99 | this.subId = subId; 100 | mClass = appendingClass; 101 | this.message = message; 102 | time = System.currentTimeMillis(); 103 | } 104 | 105 | public String toString(long baseTime) { 106 | StringBuilder sb = new StringBuilder(80); 107 | sb.append("\t<"); 108 | if (subId.contains("@")) { 109 | sb.append(subId.substring(subId.indexOf("@"))); 110 | } else { 111 | sb.append(subId); 112 | } 113 | sb.append(">, t= "); 114 | sb.append(time - baseTime); 115 | sb.append(", "); 116 | sb.append(mClass.getClass().getSimpleName()); 117 | // sb.append(" ["); 118 | // sb.append(mClass.hashCode()); 119 | // sb.append("]"); 120 | sb.append(":\t"); 121 | sb.append(message); 122 | return sb.toString(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_drawer_gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 26 | 27 | 31 | 32 | 33 | 42 | 43 | 44 | 49 | 50 | 57 | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 89 | 90 | 91 | 92 | 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/ViewPagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentStatePagerAdapter; 7 | import android.support.v4.view.ViewPager; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.Menu; 11 | import android.view.MenuInflater; 12 | import android.view.MenuItem; 13 | 14 | import com.kaichunlin.transition.adapter.OnPageChangeListenerAdapter; 15 | import com.kaichunlin.transition.internal.debug.TraceTransitionManagerListener; 16 | 17 | import kaichunlin.transition.app.R; 18 | 19 | 20 | public class ViewPagerActivity extends AppCompatActivity { 21 | // When requested, this adapter returns a DemoObjectFragment, 22 | // representing an object in the collection. 23 | private DemoCollectionPagerAdapter mDemoCollectionPagerAdapter; 24 | private ViewPager mViewPager; 25 | 26 | private OnPageChangeListenerAdapter adapter; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_view_pager); 32 | 33 | final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 34 | setSupportActionBar(toolbar); 35 | 36 | // ViewPager and its adapters use support library 37 | // fragments, so use getSupportFragmentManager. 38 | mDemoCollectionPagerAdapter = 39 | new DemoCollectionPagerAdapter( 40 | getSupportFragmentManager()); 41 | mViewPager = (ViewPager) findViewById(R.id.pager); 42 | mViewPager.setAdapter(mDemoCollectionPagerAdapter); 43 | 44 | // TransitionConfig.setPrintDebug(true); 45 | 46 | adapter = OnPageChangeListenerAdapter.bindWithRotationYTransition(mViewPager); 47 | 48 | //debug 49 | adapter.addTransitionListener(new TraceTransitionManagerListener()); 50 | } 51 | 52 | @Override 53 | public boolean onCreateOptionsMenu(Menu menu) { 54 | MenuInflater inflater = getMenuInflater(); 55 | inflater.inflate(R.menu.viewpager, menu); 56 | return super.onCreateOptionsMenu(menu); 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem menu) { 61 | super.onOptionsItemSelected(menu); 62 | 63 | switch (menu.getItemId()) { 64 | case R.id.trans_y: 65 | adapter.removeAllTransitions(); 66 | OnPageChangeListenerAdapter.bindWithRotationYTransition(adapter); 67 | break; 68 | case R.id.zoom: 69 | adapter.removeAllTransitions(); 70 | OnPageChangeListenerAdapter.bindWithZoomOutTransition(adapter); 71 | break; 72 | case R.id.depth: 73 | //has to rebuild adapter since the drawing order needs to be reversed 74 | adapter = OnPageChangeListenerAdapter.bindWithDepthTransition(mViewPager); 75 | break; 76 | case R.id.rotate: 77 | OnPageChangeListenerAdapter.bindWithRotate(adapter); 78 | break; 79 | } 80 | 81 | return true; 82 | } 83 | 84 | public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter { 85 | public DemoCollectionPagerAdapter(FragmentManager fm) { 86 | super(fm); 87 | } 88 | 89 | @Override 90 | public Fragment getItem(int i) { 91 | Fragment fragment = new ViewPagerActivityFragment(); 92 | Bundle args = new Bundle(); 93 | int id; 94 | switch (i) { 95 | case 0: 96 | id = R.drawable.bg; 97 | break; 98 | case 1: 99 | id = R.drawable.bg2; 100 | break; 101 | case 2: 102 | id = R.drawable.bg3; 103 | break; 104 | case 3: 105 | id = R.drawable.bg4; 106 | break; 107 | default: 108 | id = 0; 109 | break; 110 | } 111 | args.putInt(ViewPagerActivityFragment.ID, i); 112 | args.putInt(ViewPagerActivityFragment.RES_ID, id); 113 | fragment.setArguments(args); 114 | return fragment; 115 | } 116 | 117 | @Override 118 | public int getCount() { 119 | return 4; 120 | } 121 | 122 | @Override 123 | public CharSequence getPageTitle(int position) { 124 | return "Image " + (position + 1); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/DrawerGradientActivity.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.view.animation.FastOutLinearInInterpolator; 5 | import android.support.v4.widget.DrawerLayout; 6 | import android.support.v7.app.ActionBarDrawerToggle; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.View; 10 | import android.view.ViewTreeObserver; 11 | import android.view.animation.AccelerateInterpolator; 12 | import android.view.animation.AnticipateInterpolator; 13 | import android.view.animation.DecelerateInterpolator; 14 | import android.view.animation.LinearInterpolator; 15 | 16 | import com.kaichunlin.transition.ViewTransitionBuilder; 17 | import com.kaichunlin.transition.adapter.DrawerListenerAdapter; 18 | import com.kaichunlin.transition.internal.debug.TraceTransitionManagerListener; 19 | import com.kaichunlin.transition.util.TransitionUtil; 20 | 21 | import kaichunlin.transition.app.R; 22 | 23 | 24 | public class DrawerGradientActivity extends AppCompatActivity implements View.OnClickListener { 25 | 26 | private ActionBarDrawerToggle mDrawerToggle; 27 | private DrawerLayout mDrawerLayout; 28 | private DrawerListenerAdapter mDrawerListenerAdapter; 29 | private View mGradient; 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | 35 | setContentView(R.layout.activity_drawer_gradient); 36 | findViewById(R.id.interpolator_default).setOnClickListener(this); 37 | findViewById(R.id.interpolator_linear).setOnClickListener(this); 38 | findViewById(R.id.interpolator_accelerate).setOnClickListener(this); 39 | findViewById(R.id.interpolator_decelerate).setOnClickListener(this); 40 | findViewById(R.id.interpolator_fastout).setOnClickListener(this); 41 | findViewById(R.id.interpolator_anticipate).setOnClickListener(this); 42 | 43 | final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 44 | setSupportActionBar(toolbar); 45 | 46 | mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout); 47 | mDrawerToggle = new ActionBarDrawerToggle( 48 | this, /* host Activity */ 49 | mDrawerLayout, /* DrawerLayout object */ 50 | toolbar, 51 | R.string.navigation_drawer_open, 52 | R.string.navigation_drawer_close 53 | ); 54 | 55 | //code to transit view 56 | mDrawerListenerAdapter = new DrawerListenerAdapter(mDrawerToggle, R.id.drawerList); 57 | mDrawerListenerAdapter.setDrawerLayout(mDrawerLayout); 58 | mDrawerListenerAdapter.setDrawerListener(new DialogDrawerListener(this)); 59 | 60 | //debug 61 | mDrawerListenerAdapter.addTransitionListener(new TraceTransitionManagerListener()); 62 | 63 | mGradient = findViewById(R.id.gradient); 64 | TransitionUtil.executeOnGlobalLayout(mGradient, new ViewTreeObserver.OnGlobalLayoutListener() { 65 | @Override 66 | public void onGlobalLayout() { 67 | if (!mDrawerListenerAdapter.isOpened(DrawerGradientActivity.this)) { 68 | mGradient.setTranslationX(-mGradient.getWidth()); 69 | } 70 | updateTransition(findViewById(R.id.interpolator_default)); 71 | } 72 | }); 73 | } 74 | 75 | @Override 76 | public void onClick(View v) { 77 | updateTransition(v); 78 | } 79 | 80 | public void updateTransition(View v) { 81 | mDrawerListenerAdapter.removeAllTransitions(); 82 | 83 | ViewTransitionBuilder builder = ViewTransitionBuilder.transit(mGradient).translationX(-mGradient.getWidth(), 0); 84 | switch (v.getId()) { 85 | case R.id.interpolator_default: 86 | break; 87 | case R.id.interpolator_linear: 88 | builder.interpolator(new LinearInterpolator()); 89 | break; 90 | case R.id.interpolator_accelerate: 91 | builder.interpolator(new AccelerateInterpolator()); 92 | break; 93 | case R.id.interpolator_decelerate: 94 | builder.interpolator(new DecelerateInterpolator()); 95 | break; 96 | case R.id.interpolator_fastout: 97 | builder.interpolator(new FastOutLinearInInterpolator()); 98 | break; 99 | case R.id.interpolator_anticipate: 100 | builder.interpolator(new AnticipateInterpolator()); 101 | break; 102 | } 103 | mDrawerListenerAdapter.addTransition(builder); 104 | } 105 | 106 | @Override 107 | protected void onPostCreate(Bundle savedInstanceState) { 108 | super.onPostCreate(savedInstanceState); 109 | 110 | mDrawerToggle.syncState(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.View; 8 | import android.view.ViewTreeObserver; 9 | 10 | import com.kaichunlin.transition.Cascade; 11 | import com.kaichunlin.transition.TransitionConfig; 12 | import com.kaichunlin.transition.ViewTransitionBuilder; 13 | import com.kaichunlin.transition.animation.AnimationManager; 14 | import com.kaichunlin.transition.util.TransitionUtil; 15 | 16 | import kaichunlin.transition.app.R; 17 | 18 | 19 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | 25 | setContentView(R.layout.activity_main); 26 | 27 | final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 28 | setSupportActionBar(toolbar); 29 | 30 | findViewById(R.id.slideup_actionbar).setOnClickListener(this); 31 | findViewById(R.id.slideup_rotate_image).setOnClickListener(this); 32 | findViewById(R.id.gradient_actionbar).setOnClickListener(this); 33 | findViewById(R.id.menuitem).setOnClickListener(this); 34 | findViewById(R.id.drawer).setOnClickListener(this); 35 | findViewById(R.id.view_pager).setOnClickListener(this); 36 | 37 | //TODO not yet implemented 38 | findViewById(R.id.observable).setOnClickListener(this); 39 | findViewById(R.id.observable).setVisibility(View.GONE); 40 | 41 | ViewTransitionBuilder.transit(findViewById(R.id.toolbar)).animator(this, R.anim.animator_set).buildAnimation().startAnimation(1000); 42 | 43 | TransitionUtil.executeOnGlobalLayout(this, new ViewTreeObserver.OnGlobalLayoutListener() { 44 | @Override 45 | public void onGlobalLayout() { 46 | final AnimationManager am = new AnimationManager(); 47 | ViewTransitionBuilder builder = ViewTransitionBuilder.transit(findViewById(R.id.main_btns)); 48 | Cascade cascade = new Cascade(Cascade.STAGGERED, 0.6f); 49 | builder.transitViewGroup(new ViewTransitionBuilder.ViewGroupTransition() { 50 | @Override 51 | public void transit(ViewTransitionBuilder builder, ViewTransitionBuilder.ViewGroupTransitionConfig config) { 52 | float start = builder.getStartRange(); 53 | float end = builder.getEndRange(); 54 | float middleRange = start + builder.getRange() / 2; 55 | builder.scale(1f, 1.15f).range(start, middleRange).buildAnimationFor(am); 56 | builder.scale(1.15f, 1f).range(middleRange, end).buildAnimationFor(am); 57 | } 58 | }, cascade); 59 | am.startAnimation(1000); 60 | } 61 | }); 62 | 63 | TransitionConfig.setDebug(false); 64 | } 65 | 66 | @Override 67 | public void onClick(View v) { 68 | Intent i = null; 69 | boolean overridePendingTransition = false; 70 | switch (v.getId()) { 71 | case R.id.slideup_actionbar: 72 | i = new Intent(this, SlidingUpPanelActivity.class); 73 | break; 74 | case R.id.slideup_rotate_image: 75 | i = new Intent(this, SlidingUpPanelRotateActivity.class); 76 | break; 77 | case R.id.gradient_actionbar: 78 | i = new Intent(this, DrawerGradientActivity.class); 79 | break; 80 | case R.id.menuitem: 81 | i = new Intent(this, DrawerMenuItemActivity.class); 82 | break; 83 | case R.id.drawer: 84 | i = new Intent(this, DrawerViewActivity.class); 85 | overridePendingTransition = true; 86 | break; 87 | case R.id.view_pager: 88 | i = new Intent(this, ViewPagerActivity.class); 89 | break; 90 | case R.id.observable: 91 | //incomplete 92 | break; 93 | } 94 | AnimationManager am = new AnimationManager(); 95 | ViewTransitionBuilder.transit(v).scale(1f, 1.2f).range(0f, 0.5f).buildAnimationFor(am); 96 | ViewTransitionBuilder.transit(v).scale(1.2f, 1f).range(0.5f, 1f).buildAnimationFor(am); 97 | am.startAnimation(300); 98 | 99 | final Intent i2 = i; 100 | final boolean overridePendingTransition2 = overridePendingTransition; 101 | v.postDelayed(new Runnable() { 102 | @Override 103 | public void run() { 104 | startActivity(i2); 105 | if (overridePendingTransition2) { 106 | overridePendingTransition(0, 0); 107 | } 108 | } 109 | }, 300); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/AnimatorController.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.os.Build; 5 | import android.view.animation.LinearInterpolator; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Uses a {@link android.animation.ValueAnimator} as the control to drive the animation, used by 12 | * {@link AnimationManager} and {@link TransitionAnimation}. For some reason changing the layout with 13 | * ValueAnimator is very slow so {@link AnimationController} should be used instead, unless 14 | * {@link #pauseController()} and {@link #resumeController()} functions are needed. 15 | */ 16 | class AnimatorController extends ValueAnimator implements StateController, ValueAnimator.AnimatorUpdateListener, android.animation.Animator.AnimatorListener { 17 | private final List mAnimationList = new ArrayList<>(); 18 | private boolean mReset; 19 | private boolean mCancel; 20 | 21 | /** 22 | * 23 | * @param reverse Whether or not to reverse the animation. 24 | */ 25 | AnimatorController(boolean reverse) { 26 | setInterpolator(new LinearInterpolator()); 27 | if (reverse) { 28 | setFloatValues(1, 0); 29 | } else { 30 | setFloatValues(0, 1); 31 | } 32 | addUpdateListener(this); 33 | addListener(this); 34 | } 35 | 36 | @Override 37 | public void addAnimation(AbstractAnimation animatorStateListener) { 38 | mAnimationList.add(animatorStateListener); 39 | } 40 | 41 | @Override 42 | public void setAnimationDuration(long duration) { 43 | setDuration(duration); 44 | } 45 | 46 | @Override 47 | public void startController() { 48 | start(); 49 | mReset = false; 50 | mCancel = false; 51 | } 52 | 53 | @Override 54 | public void pauseController() { 55 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 56 | return; 57 | } 58 | pause(); 59 | } 60 | 61 | @Override 62 | public void resumeController() { 63 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 64 | return; 65 | } 66 | resume(); 67 | } 68 | 69 | @Override 70 | public void endController() { 71 | end(); 72 | } 73 | 74 | @Override 75 | public void resetController() { 76 | mReset = true; 77 | AbstractAnimation ani; 78 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 79 | ani = mAnimationList.get(i); 80 | ani.notifyAnimationReset(); 81 | } 82 | cancel(); 83 | 84 | //TODO optimize 85 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 86 | ani = mAnimationList.get(i); 87 | ani.getTransition().startTransition(); 88 | ani.getTransition().updateProgress(ani.isReverseAnimation() ? 1 : 0); 89 | ani.getTransition().stopTransition(); 90 | } 91 | } 92 | 93 | @Override 94 | public void cancelController() { 95 | cancel(); 96 | } 97 | 98 | @Override 99 | public void onAnimationUpdate(ValueAnimator animation) { 100 | AbstractAnimation ani; 101 | float progress = (Float) animation.getAnimatedValue(); 102 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 103 | ani = mAnimationList.get(i); 104 | ani.getTransition().updateProgress(progress); 105 | } 106 | } 107 | 108 | @Override 109 | public void onAnimationStart(android.animation.Animator animation) { 110 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 111 | mAnimationList.get(i).notifyAnimationStart(); 112 | } 113 | } 114 | 115 | @Override 116 | public void onAnimationEnd(android.animation.Animator animation) { 117 | if (mReset || mCancel) { 118 | return; 119 | } 120 | 121 | AbstractAnimation ani; 122 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 123 | ani = mAnimationList.get(i); 124 | ani.setAnimating(false); 125 | ani.notifyAnimationEnd(); 126 | ani.getTransition().stopTransition(); 127 | } 128 | } 129 | 130 | @Override 131 | public void onAnimationCancel(android.animation.Animator animation) { 132 | AbstractAnimation ani; 133 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 134 | ani = mAnimationList.get(i); 135 | if (mReset) { 136 | ani.notifyAnimationReset(); 137 | } else { 138 | ani.notifyAnimationCancel(); 139 | } 140 | ani.setAnimating(false); 141 | ani.getTransition().stopTransition(); 142 | } 143 | mCancel = true; 144 | } 145 | 146 | @Override 147 | public void onAnimationRepeat(android.animation.Animator animation) { 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /observablescrollview/src/main/java/com/kaichunlin/transition/adapter/ObservableScrollViewCallbacksAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.util.ArrayMap; 5 | 6 | import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; 7 | import com.github.ksoichiro.android.observablescrollview.ScrollState; 8 | import com.kaichunlin.transition.AbstractTransitionBuilder; 9 | import com.kaichunlin.transition.Transition; 10 | 11 | /** 12 | * TODO work in progress 13 | * 14 | * Created by Kai-Chun Lin on 2015/5/7. 15 | */ 16 | public class ObservableScrollViewCallbacksAdapter extends AbstractAdapter implements ObservableScrollViewCallbacks { 17 | 18 | private enum Direction { 19 | UNKOWN, 20 | UP, 21 | DOWN 22 | } 23 | 24 | private ArrayMap mRanges = new ArrayMap<>(); 25 | private ObservableScrollViewCallbacks mCallback; 26 | private int mStartY; 27 | private int mLastScrollY; 28 | private boolean mResetStartLocationOnChangeDirection; 29 | private Direction mDirection = Direction.UNKOWN; 30 | private OnChangeDirectionListener mOnChangeDirectionListener; 31 | 32 | @Override 33 | public void addTransition(AbstractTransitionBuilder builder) { 34 | //TODO guess/retrieve range 35 | throw new UnsupportedOperationException(); 36 | } 37 | 38 | /** 39 | * 40 | * @param transitionBuilder 41 | * @param range 42 | */ 43 | public void addTransition(@NonNull AbstractTransitionBuilder transitionBuilder, int range) { 44 | addTransition(transitionBuilder.build(), range); 45 | } 46 | 47 | @Override 48 | public void addTransition(Transition transition) { 49 | //TODO guess/retrieve range 50 | throw new UnsupportedOperationException(); 51 | } 52 | 53 | /** 54 | * 55 | * @param transition 56 | * @param range 57 | */ 58 | public void addTransition(@NonNull Transition transition, int range) { 59 | super.addTransition(transition); 60 | mRanges.put(transition, range); 61 | } 62 | 63 | @Override 64 | public boolean removeTransition(Transition transition) { 65 | boolean result = super.removeTransition(transition); 66 | mRanges.remove(transition); 67 | return result; 68 | } 69 | 70 | /** 71 | * 72 | * @param resetStartLocationOnChangeDirection 73 | */ 74 | public void setResetStartLocationOnChangeDirection(boolean resetStartLocationOnChangeDirection) { 75 | mResetStartLocationOnChangeDirection = resetStartLocationOnChangeDirection; 76 | } 77 | 78 | @Override 79 | public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { 80 | if (firstScroll) { 81 | mStartY = scrollY; 82 | startTransition(); 83 | return; 84 | } 85 | if (mLastScrollY < scrollY && mDirection != Direction.DOWN) { 86 | mDirection = Direction.DOWN; 87 | if (mOnChangeDirectionListener != null) { 88 | mOnChangeDirectionListener.onDown(); 89 | } 90 | if (mResetStartLocationOnChangeDirection) { 91 | mStartY = scrollY; 92 | } 93 | } else if (mLastScrollY > scrollY && mDirection != Direction.UP) { 94 | mDirection = Direction.UP; 95 | if (mOnChangeDirectionListener != null) { 96 | mOnChangeDirectionListener.onUp(); 97 | } 98 | if (mResetStartLocationOnChangeDirection) { 99 | mStartY = scrollY; 100 | } 101 | } 102 | 103 | getTransitionManager().updateProgress(scrollY - mStartY); 104 | 105 | mLastScrollY = scrollY; 106 | 107 | if (mCallback != null) { 108 | mCallback.onScrollChanged(scrollY, firstScroll, dragging); 109 | } 110 | } 111 | 112 | @Override 113 | public void onDownMotionEvent() { 114 | 115 | if (mCallback != null) { 116 | mCallback.onDownMotionEvent(); 117 | } 118 | } 119 | 120 | @Override 121 | public void onUpOrCancelMotionEvent(ScrollState scrollState) { 122 | stopTransition(); 123 | 124 | if (mCallback != null) { 125 | mCallback.onUpOrCancelMotionEvent(scrollState); 126 | } 127 | } 128 | 129 | /** 130 | * 131 | * @param callback 132 | */ 133 | public void setCallBack(ObservableScrollViewCallbacks callback) { 134 | this.mCallback = callback; 135 | } 136 | 137 | /** 138 | * 139 | * @return 140 | */ 141 | public OnChangeDirectionListener getOnChangeDirectionListener() { 142 | return mOnChangeDirectionListener; 143 | } 144 | 145 | /** 146 | * 147 | * @param onChangeDirectionListener 148 | */ 149 | public void setOnChangeDirectionListener(OnChangeDirectionListener onChangeDirectionListener) { 150 | this.mOnChangeDirectionListener = onChangeDirectionListener; 151 | } 152 | 153 | /** 154 | * 155 | */ 156 | public interface OnChangeDirectionListener { 157 | void onUp(); 158 | 159 | void onDown(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/DefaultMenuOptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.view.Menu; 7 | 8 | import com.kaichunlin.transition.MenuItemTransition; 9 | import com.kaichunlin.transition.TransitionManager; 10 | 11 | /** 12 | * Provides default support for handling MenuItem transitions. 13 | */ 14 | public class DefaultMenuOptionHandler implements MenuOptionHandler { 15 | private final TransitionManager mTransitionManager; 16 | private final AdapterState mAdapterState; 17 | private MenuOptionConfiguration mOpenConfig; 18 | private MenuOptionConfiguration mCloseConfig; 19 | 20 | public DefaultMenuOptionHandler(@NonNull TransitionManager transitionManager, @NonNull AdapterState adapterState) { 21 | mTransitionManager = transitionManager; 22 | mAdapterState = adapterState; 23 | } 24 | 25 | @Override 26 | public AdapterState getAdapterState() { 27 | return mAdapterState; 28 | } 29 | 30 | @Override 31 | public void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu) { 32 | onCreateOptionsMenu(activity, menu, mAdapterState); 33 | } 34 | 35 | @Override 36 | public void onCreateOptionsMenu(@NonNull Activity activity, @NonNull Menu menu, @NonNull AdapterState adapterState) { 37 | if (adapterState.isOpen()) { 38 | boolean hasOpen = mOpenConfig != null; 39 | if (hasOpen && mOpenConfig.getMenuId() > 0) { 40 | activity.getMenuInflater().inflate(mOpenConfig.getMenuId(), menu); 41 | } 42 | if (mCloseConfig != null) { 43 | if (hasOpen) { 44 | // TODO may cause issues when mOpenConfig.getTransition() has been merged 45 | mTransitionManager.removeTransition(mOpenConfig.getTransition()); 46 | } 47 | mTransitionManager.addTransition(mCloseConfig.getTransition()); 48 | } 49 | } else { 50 | boolean hasClose = mCloseConfig != null; 51 | if (hasClose && mCloseConfig.getMenuId() > 0) { 52 | activity.getMenuInflater().inflate(mCloseConfig.getMenuId(), menu); 53 | } 54 | if (mOpenConfig != null) { 55 | if (hasClose) { 56 | mTransitionManager.removeTransition(mCloseConfig.getTransition()); 57 | } 58 | mTransitionManager.addTransition(mOpenConfig.getTransition()); 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public void setupOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig) { 65 | MenuItemTransition transition = openConfig.getTransition(); 66 | setupOptions(activity, openConfig, new MenuOptionConfiguration(transition.clone().reverse(), openConfig.getMenuId())); 67 | } 68 | 69 | @Override 70 | public void setupOpenOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig) { 71 | MenuItemTransition transition = openConfig.getTransition(); 72 | setupOptions(activity, openConfig, new MenuOptionConfiguration(transition.clone().reverse(), -1)); 73 | } 74 | 75 | @Override 76 | public void setupCloseOption(@NonNull Activity activity, @Nullable MenuOptionConfiguration closeConfig) { 77 | MenuItemTransition transition = closeConfig.getTransition(); 78 | setupOptions(activity, new MenuOptionConfiguration(transition.clone().reverse(), -1), closeConfig); 79 | } 80 | 81 | @Override 82 | public void setupOptions(@NonNull Activity activity, @Nullable MenuOptionConfiguration openConfig, @Nullable MenuOptionConfiguration closeConfig) { 83 | if (mOpenConfig != null) { 84 | mTransitionManager.removeTransition(mOpenConfig.getTransition()); 85 | } 86 | if (mCloseConfig != null) { 87 | mTransitionManager.removeTransition(mCloseConfig.getTransition()); 88 | } 89 | mOpenConfig = openConfig; 90 | mCloseConfig = closeConfig; 91 | 92 | if (activity != null && mAdapterState.isOpen()) { 93 | if (mCloseConfig != null) { 94 | if (mCloseConfig.getTransition().isInvalidateOptionOnStopTransition()) { 95 | activity.invalidateOptionsMenu(); 96 | } 97 | mTransitionManager.addTransition(mCloseConfig.getTransition()); 98 | } 99 | } else { 100 | if (mOpenConfig != null) { 101 | if (mOpenConfig.getTransition().isInvalidateOptionOnStopTransition()) { 102 | activity.invalidateOptionsMenu(); 103 | } 104 | mTransitionManager.addTransition(mOpenConfig.getTransition()); 105 | } 106 | } 107 | } 108 | 109 | @Override 110 | public void clearOptions() { 111 | setupOptions(null, null, null); 112 | } 113 | 114 | @Nullable 115 | @Override 116 | public MenuOptionConfiguration getOpenConfig() { 117 | return mOpenConfig; 118 | } 119 | 120 | @Nullable 121 | @Override 122 | public MenuOptionConfiguration getCloseConfig() { 123 | return mCloseConfig; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/adapter/AnimationAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.adapter; 2 | 3 | import android.support.annotation.IntRange; 4 | import android.support.annotation.Nullable; 5 | import android.support.annotation.UiThread; 6 | 7 | import com.kaichunlin.transition.TransitionManagerListener; 8 | import com.kaichunlin.transition.TransitionManager; 9 | import com.kaichunlin.transition.animation.Animation; 10 | import com.kaichunlin.transition.animation.AnimationListener; 11 | import com.kaichunlin.transition.animation.TransitionAnimation; 12 | 13 | /** 14 | * This adapter integrates traditional animations using the same methods and logic as the rest of the framework. It can be configured 15 | * to reuse transitions configured for another {@link AbstractAdapter} so transition effects can be easily shared between progress-based 16 | * adapters and time-based adapters. 17 | *
18 | * However it must be noted that when AnimationAdapter reuses transitions configured for another {@link AbstractAdapter}, any changes made 19 | * to the other {@link AbstractAdapter} may effect AnimationAdapter and cause strange errors. For more complex case, {@link UnifiedAdapter} 20 | * should be used instead. 21 | */ 22 | @UiThread 23 | public class AnimationAdapter extends AbstractAdapter implements Animation, TransitionManagerListener { 24 | private final TransitionAdapter mAdapter; 25 | private Animation mAnimation; 26 | 27 | public AnimationAdapter() { 28 | this(null); 29 | } 30 | 31 | /** 32 | * Wraps an existing {@link AbstractAdapter} to reuse its onCreateOptionsMenu(...) logic and its transition effects 33 | * 34 | * @param adapter 35 | */ 36 | public AnimationAdapter(@Nullable TransitionAdapter adapter) { 37 | super(adapter == null ? new AdapterState() : adapter.getAdapterState()); 38 | mAdapter = adapter; 39 | mAnimation = new TransitionAnimation(mAdapter == null ? this : mAdapter); 40 | } 41 | 42 | @Override 43 | public void addTransitionListener(TransitionManagerListener listener) { 44 | super.addTransitionListener(listener); 45 | 46 | if (mAdapter != null) { 47 | mAdapter.addTransitionListener(this); 48 | } 49 | } 50 | 51 | @Override 52 | public void removeTransitionListener(TransitionManagerListener listener) { 53 | super.removeTransitionListener(listener); 54 | 55 | if (mAdapter != null) { 56 | mAdapter.removeTransitionListener(this); 57 | } 58 | } 59 | 60 | @Override 61 | public void addAnimationListener(AnimationListener animationListener) { 62 | 63 | } 64 | 65 | @Override 66 | public void removeAnimationListener(AnimationListener animationListener) { 67 | 68 | } 69 | 70 | @Override 71 | public void onTransitionStart(TransitionManager manager) { 72 | notifyTransitionStart(); 73 | } 74 | 75 | @Override 76 | public void onTransitionEnd(TransitionManager manager) { 77 | notifyTransitionEnd(); 78 | } 79 | 80 | @Override 81 | public void setDuration(@IntRange(from = 0) int duration) { 82 | mAnimation.setDuration(duration); 83 | } 84 | 85 | @Override 86 | public int getDuration() { 87 | return mAnimation.getDuration(); 88 | } 89 | 90 | @Override 91 | public void setReverseAnimation(boolean reverse) { 92 | mAnimation.setReverseAnimation(reverse); 93 | } 94 | 95 | @Override 96 | public boolean isReverseAnimation() { 97 | return mAnimation.isReverseAnimation(); 98 | } 99 | 100 | @Override 101 | public void startAnimation() { 102 | mAnimation.startAnimation(); 103 | } 104 | 105 | @Override 106 | public void startAnimation(@IntRange(from = 0) int duration) { 107 | mAnimation.startAnimation(duration); 108 | } 109 | 110 | public void startAnimationDelayed(@IntRange(from = 0) int delay) { 111 | mAnimation.startAnimationDelayed(delay); 112 | } 113 | 114 | public void startAnimationDelayed(@IntRange(from = 0) final int duration, @IntRange(from = 0) int delay) { 115 | mAnimation.startAnimationDelayed(duration, delay); 116 | } 117 | 118 | public boolean isAnimating() { 119 | return mAnimation.isAnimating(); 120 | } 121 | 122 | @Override 123 | public void cancelAnimation() { 124 | mAnimation.cancelAnimation(); 125 | } 126 | 127 | @Override 128 | public void pauseAnimation() { 129 | mAnimation.pauseAnimation(); 130 | } 131 | 132 | @Override 133 | public void resumeAnimation() { 134 | mAnimation.resumeAnimation(); 135 | } 136 | 137 | @Override 138 | public void endAnimation() { 139 | mAnimation.endAnimation(); 140 | } 141 | 142 | @Override 143 | public void forceEndState() { 144 | mAnimation.forceEndState(); 145 | } 146 | 147 | @Override 148 | public void resetAnimation() { 149 | mAnimation.resetAnimation(); 150 | } 151 | 152 | @Override 153 | public void stopTransition() { 154 | if (mAdapter == null) { 155 | super.stopTransition(); 156 | } else { 157 | mAdapter.stopTransition(); 158 | } 159 | } 160 | 161 | @Nullable 162 | protected TransitionAdapter getAdapter() { 163 | return mAdapter; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/animation/AnimationController.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.animation; 2 | 3 | import android.view.View; 4 | import android.view.animation.LinearInterpolator; 5 | import android.view.animation.Transformation; 6 | 7 | import com.kaichunlin.transition.TransitionOperation; 8 | 9 | import java.lang.ref.WeakReference; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * The default {@link StateController} used by {@link AnimationManager} and {@link TransitionAnimation} 15 | * to control animation state. 16 | */ 17 | class AnimationController extends android.view.animation.Animation implements StateController, android.view.animation.Animation.AnimationListener { 18 | private static final int STOP_END = 0x01; 19 | private static final int STOP_CANCEL = 0x02; 20 | private static final int STOP_RESET = 0x04; 21 | private final List mAnimationList = new ArrayList<>(); 22 | private WeakReference mTargetRef; 23 | private boolean mReverse; 24 | private List mTransitionList; 25 | private int mStopType; 26 | private boolean mEnded; 27 | 28 | AnimationController(View target, boolean reverse, final TransitionOperation transition) { 29 | this(target, reverse, new ArrayList() {{ 30 | add(transition); 31 | }}); 32 | } 33 | 34 | AnimationController(View target, boolean reverse, List transitionList) { 35 | mTargetRef = new WeakReference<>(target); 36 | mReverse = reverse; 37 | mTransitionList = transitionList; 38 | setAnimationListener(this); 39 | setInterpolator(new LinearInterpolator()); 40 | } 41 | 42 | @Override 43 | protected void applyTransformation(float interpolatedTime, Transformation t) { 44 | if (mStopType != 0) { 45 | return; 46 | } 47 | for (int i = 0; i < mTransitionList.size(); i++) { 48 | mTransitionList.get(i).updateProgress(mReverse ? 1 - interpolatedTime : interpolatedTime); 49 | } 50 | } 51 | 52 | @Override 53 | public void addAnimation(AbstractAnimation animation) { 54 | mAnimationList.add(animation); 55 | } 56 | 57 | @Override 58 | public void setAnimationDuration(long duration) { 59 | setDuration(duration); 60 | } 61 | 62 | @Override 63 | public void startController() { 64 | View target = mTargetRef.get(); 65 | if (target == null) { 66 | return; 67 | } 68 | mStopType = 0; 69 | mEnded = false; 70 | target.startAnimation(this); 71 | } 72 | 73 | /** 74 | * Currently not supported 75 | */ 76 | @Override 77 | public void pauseController() { 78 | //TODO currently does nothing 79 | } 80 | 81 | /** 82 | * Currently not supported 83 | */ 84 | @Override 85 | public void resumeController() { 86 | //TODO currently does nothing 87 | } 88 | 89 | @Override 90 | public void endController() { 91 | handleStop(STOP_END); 92 | AbstractAnimation ani; 93 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 94 | ani = mAnimationList.get(i); 95 | ani.getTransition().updateProgress(ani.isReverseAnimation() ? 0 : 1); 96 | } 97 | } 98 | 99 | @Override 100 | public void resetController() { 101 | handleStop(STOP_RESET); 102 | AbstractAnimation ani; 103 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 104 | ani = mAnimationList.get(i); 105 | ani.getTransition().updateProgress(ani.isReverseAnimation() ? 1 : 0); 106 | } 107 | } 108 | 109 | public void cancelController() { 110 | handleStop(STOP_CANCEL); 111 | } 112 | 113 | private void handleStop(int stopType) { 114 | this.mStopType = stopType; 115 | View target = mTargetRef.get(); 116 | if (target == null) { 117 | return; 118 | } 119 | target.clearAnimation(); 120 | } 121 | 122 | @Override 123 | public void onAnimationStart(android.view.animation.Animation animation) { 124 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 125 | mAnimationList.get(i).notifyAnimationStart(); 126 | } 127 | } 128 | 129 | @Override 130 | public void onAnimationEnd(android.view.animation.Animation animation) { 131 | if (mEnded) { 132 | return; 133 | } 134 | if (mStopType == 0) { 135 | mStopType = STOP_END; 136 | } 137 | AbstractAnimation ani; 138 | for (int i = 0, size = mAnimationList.size(); i < size; i++) { 139 | ani = mAnimationList.get(i); 140 | ani.setAnimating(false); 141 | switch (mStopType) { 142 | case STOP_END: 143 | ani.notifyAnimationEnd(); 144 | break; 145 | case STOP_CANCEL: 146 | ani.notifyAnimationCancel(); 147 | break; 148 | case STOP_RESET: 149 | ani.notifyAnimationReset(); 150 | break; 151 | } 152 | ani.getTransition().stopTransition(); 153 | } 154 | mEnded = true; 155 | } 156 | 157 | @Override 158 | public void onAnimationRepeat(android.view.animation.Animation animation) { 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_slideup_actionbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | 26 | 27 | 30 | 31 | 34 | 35 | 42 | 43 | 49 | 50 | 54 | 55 | 59 | 60 | 64 | 65 | 69 | 70 | 74 | 75 | 79 | 80 | 84 | 85 | 89 | 90 | 94 | 95 | 96 | 97 | 98 | 108 | 109 | 110 | 116 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/Cascade.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import android.support.annotation.FloatRange; 4 | import android.support.annotation.IntDef; 5 | import android.support.annotation.NonNull; 6 | import android.view.animation.Interpolator; 7 | import android.view.animation.LinearInterpolator; 8 | 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | 12 | /** 13 | * Defines how a cascading transition should be applied for each child of a ViewGroup, this is used for 14 | * {@link ViewTransitionBuilder#transitViewGroup(ViewTransitionBuilder.ViewGroupTransition, Cascade)}. 15 | *
16 | * There are 3 different type: 17 | *
18 | * 1. STAGGERED, where effects would interleave with each other, the length of the effect = getTransitionEnd() - getCascadeEnd(): 19 | *
20 | *

    child 0: |------|
21 | *
22 | *
    child 1:     |------|
23 | *
24 | *
    child 2:         |------|
25 | *
26 | * 2. RUN_TO_THE_END, where each effect would run to getTransitionEnd(), disregarding when it started: 27 | *
28 | *
    child 0: |--------------|
 29 |  * 
30 | *
    child 1:     |----------|
 31 |  * 
32 | *
    child 2:         |------|
 33 |  * 
34 | * 3. SEQUENTIAL, where each effect would run to complete before playing the next effect: 35 | *
36 | * Effect would be run as: 37 | *
38 | *
     child 0: |----|
 39 |  * 
40 | *
    child 1:        |----|
 41 |  * 
42 | *
    child 2:              |----|
 43 |  */
 44 | public class Cascade implements ViewTransitionBuilder.ViewGroupTransition {
 45 |     private static final Interpolator DEFAULT_INTERPOLATOR = new LinearInterpolator();
 46 |     public static final int STAGGERED = 0;
 47 |     public static final int RUN_TO_THE_END = 1;
 48 |     public static final int SEQUENTIAL = 2;
 49 | 
 50 |     @IntDef({STAGGERED, RUN_TO_THE_END, SEQUENTIAL})
 51 |     @Retention(RetentionPolicy.SOURCE)
 52 |     @interface Type {
 53 |     }
 54 | 
 55 |     public final @Type int type;
 56 |     public final Interpolator interpolator;
 57 |     float mCascadeStart;
 58 |     //end of the cascade, i.e. the last child will start the effect at this point
 59 |     public final float cascadeEnd;
 60 |     //end of transition effect, i.e. all effects will stop at this point
 61 |     float mTransitionEnd = 1f;
 62 |     boolean mReverse;
 63 | 
 64 | 
 65 |     /**
 66 |      * @param cascadeEnd This value should never be 1, otherwise no transition will be applied to the last child.
 67 |      */
 68 |     public Cascade(@Type int type, @FloatRange(from = 0.0, to = 1.0) float cascadeEnd) {
 69 |         this(type, cascadeEnd, DEFAULT_INTERPOLATOR);
 70 |     }
 71 | 
 72 |     /**
 73 |      * @param cascadeEnd This value should never be 1, otherwise no transition will be applied to the last child.
 74 |      */
 75 |     public Cascade(@Type int type, @FloatRange(from = 0.0, to = 1.0) float cascadeEnd, @NonNull Interpolator interpolator) {
 76 |         this.type = type;
 77 |         this.cascadeEnd = cascadeEnd;
 78 |         this.interpolator = interpolator;
 79 |     }
 80 | 
 81 |     public int getType() {
 82 |         return type;
 83 |     }
 84 | 
 85 |     public Interpolator getInterpolator() {
 86 |         return interpolator;
 87 |     }
 88 | 
 89 |     public float getCascadeEnd() {
 90 |         return cascadeEnd;
 91 |     }
 92 | 
 93 |     public Cascade cascadeStart(@FloatRange(from = 0.0, to = 1.0) float cascadeStart) {
 94 |         this.mCascadeStart = cascadeStart;
 95 |         return this;
 96 |     }
 97 | 
 98 |     public float getCascadeStart() {
 99 |         return mCascadeStart;
100 |     }
101 | 
102 |     public Cascade transitionEnd(@FloatRange(from = 0.0, to = 1.0) float transitionEnd) {
103 |         this.mTransitionEnd = transitionEnd;
104 |         return this;
105 |     }
106 | 
107 |     public float getTransitionEnd() {
108 |         return mTransitionEnd;
109 |     }
110 | 
111 |     public Cascade reverse() {
112 |         this.mReverse = true;
113 |         return this;
114 |     }
115 | 
116 |     public boolean isReverse() {
117 |         return mReverse;
118 |     }
119 | 
120 |     /**
121 |      * Configures a {@link ViewTransitionBuilder} per the Cascade's Type.
122 |      *
123 |      * @param builder
124 |      * @param config Contains context about the view being configured.
125 |      */
126 |     @Override
127 |     public void transit(ViewTransitionBuilder builder, ViewTransitionBuilder.ViewGroupTransitionConfig config) {
128 |         float cascadeLength = cascadeEnd - mCascadeStart;
129 |         float minEffectLength = mTransitionEnd - cascadeEnd;
130 |         float fraction = cascadeLength / (config.total - 1);
131 |         float rangeStart = mCascadeStart + (mReverse ? config.total - 1 - config.getIndex() : config.getIndex()) * fraction;
132 |         float rangeEnd;
133 |         switch (type) {
134 |             case STAGGERED:
135 |                 rangeEnd = rangeStart + minEffectLength;
136 |                 break;
137 |             case RUN_TO_THE_END:
138 |                 rangeEnd = mTransitionEnd;
139 |                 break;
140 |             case SEQUENTIAL:
141 |                 rangeEnd = rangeStart + fraction;
142 |                 if (rangeEnd > mTransitionEnd) {
143 |                     rangeEnd = mTransitionEnd;
144 |                 }
145 |                 break;
146 |             default:
147 |                 throw new IllegalArgumentException();
148 |         }
149 | 
150 |         builder.range(interpolator.getInterpolation(rangeStart), interpolator.getInterpolation(rangeEnd));
151 |     }
152 | }


--------------------------------------------------------------------------------
/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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
 46 | if $cygwin ; then
 47 |     [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
 48 | fi
 49 | 
 50 | # Attempt to set APP_HOME
 51 | # Resolve links: $0 may be a link
 52 | PRG="$0"
 53 | # Need this for relative symlinks.
 54 | while [ -h "$PRG" ] ; do
 55 |     ls=`ls -ld "$PRG"`
 56 |     link=`expr "$ls" : '.*-> \(.*\)$'`
 57 |     if expr "$link" : '/.*' > /dev/null; then
 58 |         PRG="$link"
 59 |     else
 60 |         PRG=`dirname "$PRG"`"/$link"
 61 |     fi
 62 | done
 63 | SAVED="`pwd`"
 64 | cd "`dirname \"$PRG\"`/" >&-
 65 | APP_HOME="`pwd -P`"
 66 | cd "$SAVED" >&-
 67 | 
 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 69 | 
 70 | # Determine the Java command to use to start the JVM.
 71 | if [ -n "$JAVA_HOME" ] ; then
 72 |     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
 73 |         # IBM's JDK on AIX uses strange locations for the executables
 74 |         JAVACMD="$JAVA_HOME/jre/sh/java"
 75 |     else
 76 |         JAVACMD="$JAVA_HOME/bin/java"
 77 |     fi
 78 |     if [ ! -x "$JAVACMD" ] ; then
 79 |         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
 80 | 
 81 | Please set the JAVA_HOME variable in your environment to match the
 82 | location of your Java installation."
 83 |     fi
 84 | else
 85 |     JAVACMD="java"
 86 |     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 87 | 
 88 | Please set the JAVA_HOME variable in your environment to match the
 89 | location of your Java installation."
 90 | fi
 91 | 
 92 | # Increase the maximum file descriptors if we can.
 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
 94 |     MAX_FD_LIMIT=`ulimit -H -n`
 95 |     if [ $? -eq 0 ] ; then
 96 |         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
 97 |             MAX_FD="$MAX_FD_LIMIT"
 98 |         fi
 99 |         ulimit -n $MAX_FD
100 |         if [ $? -ne 0 ] ; then
101 |             warn "Could not set maximum file descriptor limit: $MAX_FD"
102 |         fi
103 |     else
104 |         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 |     fi
106 | fi
107 | 
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 |     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 | 
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 |     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 |     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 | 
118 |     # We build the pattern for arguments to be converted via cygpath
119 |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 |     SEP=""
121 |     for dir in $ROOTDIRSRAW ; do
122 |         ROOTDIRS="$ROOTDIRS$SEP$dir"
123 |         SEP="|"
124 |     done
125 |     OURCYGPATTERN="(^($ROOTDIRS))"
126 |     # Add a user-defined pattern to the cygpath arguments
127 |     if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 |         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 |     fi
130 |     # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 |     i=0
132 |     for arg in "$@" ; do
133 |         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 |         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
135 | 
136 |         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
137 |             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 |         else
139 |             eval `echo args$i`="\"$arg\""
140 |         fi
141 |         i=$((i+1))
142 |     done
143 |     case $i in
144 |         (0) set -- ;;
145 |         (1) set -- "$args0" ;;
146 |         (2) set -- "$args0" "$args1" ;;
147 |         (3) set -- "$args0" "$args1" "$args2" ;;
148 |         (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 |         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 |         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 |         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 |         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 |         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 |     esac
155 | fi
156 | 
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 |     JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 | 
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/kaichunlin/transition/internal/TransitionController.java:
--------------------------------------------------------------------------------
  1 | package com.kaichunlin.transition.internal;
  2 | 
  3 | import android.support.annotation.CheckResult;
  4 | import android.support.annotation.NonNull;
  5 | import android.support.annotation.Nullable;
  6 | import android.view.View;
  7 | import android.view.animation.Interpolator;
  8 | 
  9 | import com.kaichunlin.transition.R;
 10 | import com.kaichunlin.transition.util.TransitionStateLogger;
 11 | 
 12 | /**
 13 |  */
 14 | public abstract class TransitionController {
 15 |     public static final float DEFAULT_START = 0f;
 16 |     public static final float DEFAULT_END = 1f;
 17 | 
 18 |     private String mId;
 19 |     View mTarget;
 20 |     float mStart = DEFAULT_START;
 21 |     float mEnd = DEFAULT_END;
 22 |     float mProgressWidth;
 23 |     boolean mStarted;
 24 |     long mStartDelay;
 25 |     long mDuration;
 26 |     long mTotalDuration;
 27 |     long mLastTime;
 28 |     boolean mSetup;
 29 |     Interpolator mInterpolator;
 30 |     boolean mUpdateStateAfterUpdateProgress;
 31 |     boolean mEnable = true;
 32 |     boolean mReverse;
 33 | 
 34 |     /**
 35 |      * @param target the view this object should manipulate
 36 |      */
 37 |     public TransitionController(@Nullable View target) {
 38 |         this.mTarget = target;
 39 |     }
 40 | 
 41 |     public void setId(@NonNull String id) {
 42 |         mId = id;
 43 |     }
 44 | 
 45 |     public String getId() {
 46 |         if (mId == null) {
 47 |             mId = toString();
 48 |         }
 49 |         return mId;
 50 |     }
 51 | 
 52 |     /**
 53 |      * Starts the transition
 54 |      */
 55 |     public void start() {
 56 |         mLastTime = -1;
 57 |         mSetup = true;
 58 |     }
 59 | 
 60 |     /**
 61 |      * @param progress
 62 |      */
 63 |     protected abstract void updateProgress(float progress);
 64 | 
 65 |     /**
 66 |      * Ends the transition
 67 |      */
 68 |     public void end() {
 69 |         mStarted = false;
 70 |     }
 71 | 
 72 |     protected void updateProgressWidth() {
 73 |         mProgressWidth = Math.abs(mEnd - mStart);
 74 |     }
 75 | 
 76 |     /**
 77 |      * Defaults to [0..1] if not set
 78 |      *
 79 |      * @param start the start of the applicable transition range
 80 |      * @param end   the end of the applicable transition range
 81 |      * @return
 82 |      */
 83 |     public T setRange(float start, float end) {
 84 |         this.mStart = start;
 85 |         this.mEnd = end;
 86 |         updateProgressWidth();
 87 |         return self();
 88 |     }
 89 | 
 90 |     /**
 91 |      * @return the start value for the applicable transition range
 92 |      */
 93 |     public float getStart() {
 94 |         return mStart;
 95 |     }
 96 | 
 97 |     public T setStart(float mStart) {
 98 |         this.mStart = mStart;
 99 |         updateProgressWidth();
100 |         return self();
101 |     }
102 | 
103 |     /**
104 |      * @return the end value for the applicable transition range
105 |      */
106 |     public float getEnd() {
107 |         return mEnd;
108 |     }
109 | 
110 |     public T setEnd(float end) {
111 |         this.mEnd = end;
112 |         updateProgressWidth();
113 |         return self();
114 |     }
115 | 
116 |     public View getTarget() {
117 |         return mTarget;
118 |     }
119 | 
120 |     /**
121 |      * Reverse how the transition is applied, such that the transition previously performed when progress=start of range is only performed when progress=end of range
122 |      *
123 |      * @return
124 |      */
125 |     public T reverse() {
126 |         String id = getId();
127 |         String REVERSE = "_REVERSE";
128 |         if (id.endsWith(REVERSE)) {
129 |             setId(id.substring(0, id.length() - REVERSE.length()));
130 |         }
131 | 
132 |         float start = mStart;
133 |         float end = mEnd;
134 |         mStart = end;
135 |         mEnd = start;
136 |         mReverse = !mReverse;
137 |         return self();
138 |     }
139 | 
140 |     public boolean isReverse() {
141 |         return mReverse;
142 |     }
143 | 
144 |     /**
145 |      * @param target the view this controller should manipulate
146 |      * @return
147 |      */
148 |     public T setTarget(@Nullable View target) {
149 |         this.mTarget = target;
150 |         return self();
151 |     }
152 | 
153 |     /**
154 |      * @param interpolator the Interpolator this controller should be when transiting a view
155 |      */
156 |     public void setInterpolator(@Nullable Interpolator interpolator) {
157 |         mInterpolator = interpolator;
158 |     }
159 | 
160 |     /**
161 |      * @param updateStateAfterUpdateProgress whether or not to update a controller's enable state after each {@link #updateProgress(float)} call
162 |      */
163 |     public void setUpdateStateAfterUpdateProgress(boolean updateStateAfterUpdateProgress) {
164 |         mUpdateStateAfterUpdateProgress = updateStateAfterUpdateProgress;
165 |     }
166 | 
167 |     /**
168 |      * Enables or disables the controller
169 |      *
170 |      * @param enable
171 |      */
172 |     public void setEnable(boolean enable) {
173 |         mEnable = enable;
174 |     }
175 | 
176 |     /**
177 |      * TODO hack to make ViewPager work
178 |      *
179 |      * @return is the controlled enabled
180 |      */
181 |     public boolean isEnable() {
182 |         return mEnable;
183 |     }
184 | 
185 |     public TransitionStateLogger getTransitionStateHolder() {
186 |         TransitionStateLogger logger = (TransitionStateLogger) getTarget().getTag(R.id.debug_id);
187 |         if (logger == null) {
188 |             logger = new TransitionStateLogger(getId());
189 |             getTarget().setTag(R.id.debug_id, logger);
190 |         }
191 |         return logger;
192 |     }
193 | 
194 |     @CheckResult
195 |     @Override
196 |     public TransitionController clone() {
197 |         try {
198 |             return (TransitionController) super.clone();
199 |         } catch (CloneNotSupportedException e) {
200 |             e.printStackTrace();
201 |         }
202 |         return null;
203 |     }
204 | 
205 |     protected abstract T self();
206 | }
207 | 


--------------------------------------------------------------------------------
/core/src/main/java/com/kaichunlin/transition/animation/TransitionAnimation.java:
--------------------------------------------------------------------------------
  1 | package com.kaichunlin.transition.animation;
  2 | 
  3 | import android.os.Build;
  4 | import android.os.Handler;
  5 | import android.support.annotation.IntRange;
  6 | import android.support.annotation.NonNull;
  7 | import android.support.annotation.UiThread;
  8 | import android.view.View;
  9 | 
 10 | import com.kaichunlin.transition.AbstractTransition;
 11 | import com.kaichunlin.transition.TransitionOperation;
 12 | 
 13 | /**
 14 |  */
 15 | @UiThread
 16 | public class TransitionAnimation extends AbstractAnimation {
 17 |     private StateController mController;
 18 |     private boolean mReverse;
 19 |     private int mDuration = 300;
 20 | 
 21 |     public TransitionAnimation(@NonNull TransitionOperation transition) {
 22 |         super(transition);
 23 | 
 24 |         addAnimationListener(new AnimationListener() {
 25 |             @Override
 26 |             public void onAnimationStart(Animation animationManager) {
 27 | 
 28 |             }
 29 | 
 30 |             @Override
 31 |             public void onAnimationEnd(Animation animationManager) {
 32 |                 getTransition().stopTransition();
 33 |             }
 34 | 
 35 |             @Override
 36 |             public void onAnimationCancel(Animation animationManager) {
 37 |                 getTransition().stopTransition();
 38 |             }
 39 | 
 40 |             @Override
 41 |             public void onAnimationReset(Animation animationManager) {
 42 |                 getTransition().stopTransition();
 43 |             }
 44 |         });
 45 |     }
 46 | 
 47 |     @Override
 48 |     public void setDuration(@IntRange(from = 0) int duration) {
 49 |         mDuration = duration;
 50 |     }
 51 | 
 52 |     @Override
 53 |     public int getDuration() {
 54 |         return mDuration;
 55 |     }
 56 | 
 57 |     @Override
 58 |     public void setReverseAnimation(boolean reverse) {
 59 |         mReverse = reverse;
 60 |     }
 61 | 
 62 |     @Override
 63 |     public boolean isReverseAnimation() {
 64 |         return mReverse;
 65 |     }
 66 | 
 67 |     @Override
 68 |     public void startAnimation() {
 69 |         startAnimation(mDuration);
 70 |     }
 71 | 
 72 |     @Override
 73 |     public void startAnimation(@IntRange(from = 0) final int duration) {
 74 |         if (getStateControllerType() == CONTROLLER_ANIMATION) {
 75 |             View target = ((AbstractTransition) getTransition()).getTarget();
 76 |             if (target == null) {
 77 |                 setStateControllerType(CONTROLLER_ANIMATOR);
 78 |                 startAnimation(duration);
 79 |                 return;
 80 |             }
 81 |             mController = new AnimationController(target, mReverse, getTransition());
 82 |         } else if (getStateControllerType() == CONTROLLER_ANIMATOR) {
 83 |             mController = new AnimatorController(isReverseAnimation());
 84 |         }
 85 |         prepareAnimation(mController, duration);
 86 |         mController.startController();
 87 |     }
 88 | 
 89 |     /**
 90 |      * Called before {@link StateController#startController()} to configure the animation.
 91 |      *
 92 |      * @param sharedController A shared {@link StateController} to reduce resource usage.
 93 |      * @param duration Duration of the animation, -1 if sharedController's duration should not be overridden.
 94 |      */
 95 |     protected void prepareAnimation(@NonNull StateController sharedController, @IntRange(from = -1) final int duration) {
 96 |         if (isAnimating()) {
 97 |             return;
 98 |         }
 99 |         setAnimating(true);
100 |         getTransition().startTransition(mReverse ? 1 : 0);
101 | 
102 |         mController = sharedController;
103 |         if (duration != -1) {
104 |             mController.setAnimationDuration(duration);
105 |         }
106 |         mController.addAnimation(this);
107 |     }
108 | 
109 |     public void startAnimationDelayed(@IntRange(from = 0) int delay) {
110 |         if (mHandler == null) {
111 |             mHandler = new Handler();
112 |         }
113 |         mHandler.postDelayed(mStartAnimation, delay);
114 |     }
115 | 
116 |     public void startAnimationDelayed(@IntRange(from = 0) final int duration, @IntRange(from = 0) int delay) {
117 |         if (mHandler == null) {
118 |             mHandler = new Handler();
119 |         }
120 |         mHandler.postDelayed(new Runnable() {
121 |             @Override
122 |             public void run() {
123 |                 startAnimation(duration);
124 |             }
125 |         }, delay);
126 |     }
127 | 
128 |     @Override
129 |     public void cancelAnimation() {
130 |         if (mController != null) {
131 |             mController.cancelController();
132 |             mController = null;
133 |         }
134 |     }
135 | 
136 |     @Override
137 |     public void pauseAnimation() {
138 |         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
139 |             return;
140 |         }
141 |         if (mController != null) {
142 |             mController.pauseController();
143 |         }
144 |     }
145 | 
146 |     @Override
147 |     public void resumeAnimation() {
148 |         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
149 |             return;
150 |         }
151 |         if (mController != null) {
152 |             mController.resumeController();
153 |         }
154 |     }
155 | 
156 |     @Override
157 |     public void endAnimation() {
158 |         if (mController != null) {
159 |             mController.endController();
160 |             mController = null;
161 |         }
162 |     }
163 | 
164 |     @Override
165 |     public void forceEndState() {
166 |         startAnimation();
167 |         endAnimation();
168 |         notifyAnimationEnd();
169 |     }
170 | 
171 |     @Override
172 |     public void resetAnimation() {
173 |         notifyAnimationReset();
174 |         if (mController != null) {
175 |             mController.resetController();
176 |             mController = null;
177 |             setAnimating(false);
178 |             //TODO optimize
179 |             getTransition().startTransition();
180 |             getTransition().updateProgress(mReverse ? 1 : 0);
181 |             getTransition().stopTransition();
182 |         }
183 |     }
184 | }


--------------------------------------------------------------------------------
/core/src/main/java/com/kaichunlin/transition/animation/AbstractAnimation.java:
--------------------------------------------------------------------------------
  1 | package com.kaichunlin.transition.animation;
  2 | 
  3 | import android.os.Handler;
  4 | import android.support.annotation.IntDef;
  5 | import android.support.annotation.IntRange;
  6 | import android.support.annotation.NonNull;
  7 | import android.support.annotation.UiThread;
  8 | 
  9 | import com.kaichunlin.transition.TransitionOperation;
 10 | 
 11 | import java.lang.annotation.Retention;
 12 | import java.lang.annotation.RetentionPolicy;
 13 | import java.util.HashSet;
 14 | import java.util.Set;
 15 | 
 16 | /**
 17 |  */
 18 | @UiThread
 19 | public abstract class AbstractAnimation implements Animation {
 20 |     @IntDef({CONTROLLER_ANIMATION, CONTROLLER_ANIMATOR})
 21 |     @Retention(RetentionPolicy.SOURCE)
 22 |     public @interface StateControllerType {
 23 |     }
 24 | 
 25 |     public static final int CONTROLLER_ANIMATION = 0;
 26 |     public static final int CONTROLLER_ANIMATOR = 1;
 27 | 
 28 |     protected final Runnable mStartAnimation = new Runnable() {
 29 |         @Override
 30 |         public void run() {
 31 |             startAnimation();
 32 |         }
 33 |     };
 34 | 
 35 |     protected Handler mHandler;
 36 |     private final Set mAnimationListenerSet = new HashSet<>();
 37 |     private int mDuration = -1;
 38 |     private boolean mReverse;
 39 |     private boolean mAnimating;
 40 |     private final TransitionOperation mTransition;
 41 |     private @StateControllerType int mStateControllerType = CONTROLLER_ANIMATION;
 42 | 
 43 |     public AbstractAnimation() {
 44 |         mTransition = null;
 45 |     }
 46 | 
 47 |     public AbstractAnimation(@NonNull TransitionOperation transition) {
 48 |         mTransition = transition;
 49 |     }
 50 | 
 51 |     /**
 52 |      * The driver for the animation, can be either {@link #CONTROLLER_ANIMATION} or
 53 |      * {@link #CONTROLLER_ANIMATOR}, currently the chief difference is that {@link #CONTROLLER_ANIMATION}
 54 |      * may be more performant in some situations, where as {@link #CONTROLLER_ANIMATOR} allows
 55 |      * pausing/resuming the animation on API level 19 or later.
 56 |      * 

57 | * {@link #CONTROLLER_ANIMATION} is the default, unless a valid View cannot be found (for example when animating 58 | * the menu), then {@link #CONTROLLER_ANIMATOR} is automatically used. 59 | */ 60 | public void setStateControllerType(@StateControllerType int stateControllerType) { 61 | mStateControllerType = stateControllerType; 62 | } 63 | 64 | @StateControllerType 65 | public int getStateControllerType() { 66 | return mStateControllerType; 67 | } 68 | 69 | protected TransitionOperation getTransition() { 70 | return mTransition; 71 | } 72 | 73 | /** 74 | * @param listener 75 | */ 76 | public void addAnimationListener(AnimationListener listener) { 77 | mAnimationListenerSet.add(listener); 78 | } 79 | 80 | /** 81 | * @param listener 82 | */ 83 | public void removeAnimationListener(AnimationListener listener) { 84 | mAnimationListenerSet.remove(listener); 85 | } 86 | 87 | public void setDuration(@IntRange(from = 0) int duration) { 88 | mDuration = duration; 89 | } 90 | 91 | public int getDuration() { 92 | return mDuration; 93 | } 94 | 95 | public void setReverseAnimation(boolean reverse) { 96 | mReverse = reverse; 97 | } 98 | 99 | /** 100 | * @return 101 | */ 102 | public boolean isReverseAnimation() { 103 | return mReverse; 104 | } 105 | 106 | /** 107 | * Starts the animation with the default duration. 108 | */ 109 | public abstract void startAnimation(); 110 | 111 | /** 112 | * Starts the animation with the specified duration in milliseconds. 113 | * 114 | * @param duration 115 | */ 116 | public abstract void startAnimation(@IntRange(from = 0) int duration); 117 | 118 | /** 119 | * Starts the animation with the default duration (300 ms) after a set delay. 120 | * 121 | * @param delay time to delay the animation in milliseconds 122 | */ 123 | @Override 124 | public void startAnimationDelayed(@IntRange(from = 0) int delay) { 125 | if (mHandler == null) { 126 | mHandler = new Handler(); 127 | } 128 | mHandler.postDelayed(mStartAnimation, delay); 129 | } 130 | 131 | public void startAnimationDelayed(@IntRange(from = 0) final int duration, @IntRange(from = 0) int delay) { 132 | if (mHandler == null) { 133 | mHandler = new Handler(); 134 | } 135 | mHandler.postDelayed(new Runnable() { 136 | @Override 137 | public void run() { 138 | startAnimation(duration); 139 | } 140 | }, delay); 141 | } 142 | 143 | protected void setAnimating(boolean animating) { 144 | mAnimating = animating; 145 | } 146 | 147 | public boolean isAnimating() { 148 | return mAnimating; 149 | } 150 | 151 | protected void notifyAnimationStart() { 152 | if (mAnimationListenerSet.size() == 0) { 153 | return; 154 | } 155 | for (AnimationListener listener : mAnimationListenerSet) { 156 | listener.onAnimationStart(this); 157 | } 158 | } 159 | 160 | protected void notifyAnimationEnd() { 161 | if (mAnimationListenerSet.size() == 0) { 162 | return; 163 | } 164 | for (AnimationListener listener : mAnimationListenerSet) { 165 | listener.onAnimationEnd(this); 166 | } 167 | } 168 | 169 | protected void notifyAnimationCancel() { 170 | if (mAnimationListenerSet.size() == 0) { 171 | return; 172 | } 173 | for (AnimationListener listener : mAnimationListenerSet) { 174 | listener.onAnimationCancel(this); 175 | } 176 | } 177 | 178 | protected void notifyAnimationReset() { 179 | if (mAnimationListenerSet.size() == 0) { 180 | return; 181 | } 182 | for (AnimationListener listener : mAnimationListenerSet) { 183 | listener.onAnimationReset(this); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/DefaultTransitionManager.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | /** 11 | * Manages a collection of {@link Transition}. 12 | */ 13 | public class DefaultTransitionManager implements TransitionManager { 14 | private ArrayList mListenerList = new ArrayList<>(); 15 | private ArrayList mTransitionList = new ArrayList<>(); 16 | private Set mBackupTransitionList = new HashSet<>(); 17 | 18 | @Override 19 | public void addTransition(@NonNull AbstractTransitionBuilder transitionBuilder) { 20 | addTransition(transitionBuilder.build()); 21 | } 22 | 23 | @Override 24 | public void addTransition(@NonNull Transition transition) { 25 | processAnimation(transition); 26 | } 27 | 28 | @Override 29 | public void addAllTransitions(@NonNull List transitionsList) { 30 | for (int i = 0, size = transitionsList.size(); i < size; i++) { 31 | addTransition(transitionsList.get(i)); 32 | } 33 | } 34 | 35 | private void processAnimation(Transition transition) { 36 | //attempt to merge an animation 37 | boolean merged = false; 38 | //no optimization is taken if the TransitionOption is not an AbstractTransition subclass 39 | if (transition instanceof AbstractTransition) { 40 | TransitionOperation to; 41 | for (int i = 0, size = mTransitionList.size(); i < size; i++) { 42 | to = mTransitionList.get(i); 43 | if (to instanceof AbstractTransition) { 44 | AbstractTransition atTo = (AbstractTransition) to; 45 | AbstractTransition atTransition = (AbstractTransition) transition; 46 | if (atTo.isCompatible(atTransition)) { 47 | //the Transition has already merged another Transition previously 48 | if (atTo.hasMultipleSetup()) { 49 | atTo.merge(atTransition); 50 | } else { //the Transition has not merged another Transition 51 | mTransitionList.remove(atTo); 52 | atTo = atTo.clone(); 53 | atTo.merge(atTransition); 54 | mTransitionList.add(atTo); 55 | } 56 | break; 57 | } 58 | } 59 | } 60 | } 61 | if (!merged) { 62 | mTransitionList.add(transition); 63 | } 64 | mBackupTransitionList.add(transition); 65 | } 66 | 67 | @Override 68 | public boolean removeTransition(@NonNull Transition transition) { 69 | //rebuilding mTransitionList 70 | if (mBackupTransitionList.remove(transition)) { 71 | List temp = new ArrayList<>(mBackupTransitionList.size()); 72 | temp.addAll(mBackupTransitionList); 73 | mTransitionList.clear(); 74 | mBackupTransitionList.clear(); 75 | 76 | for (int i = 0, size = temp.size(); i < size; i++) { 77 | processAnimation(temp.get(i)); 78 | } 79 | 80 | return true; 81 | } 82 | return false; 83 | } 84 | 85 | @Override 86 | public void removeAllTransitions() { 87 | mTransitionList.clear(); 88 | } 89 | 90 | @Override 91 | public List getTransitions() { 92 | return mTransitionList; 93 | } 94 | 95 | @Override 96 | public boolean startTransition() { 97 | //call listeners so they can perform their actions first, like modifying this adapter's transitions 98 | notifyTransitionStart(); 99 | 100 | boolean start = false; 101 | final int size = mTransitionList.size(); 102 | for (int i = 0; i < size; i++) { 103 | start |= mTransitionList.get(i).startTransition(); 104 | } 105 | return start; 106 | } 107 | 108 | @Override 109 | public boolean startTransition(float progress) { 110 | //call listeners so they can perform their actions first, like modifying this adapter's transitions 111 | notifyTransitionStart(); 112 | 113 | boolean start = false; 114 | final int size = mTransitionList.size(); 115 | for (int i = 0; i < size; i++) { 116 | start |= mTransitionList.get(i).startTransition(progress); 117 | } 118 | return start; 119 | } 120 | 121 | /** 122 | * Updates the transition progress 123 | * 124 | * @param value 125 | */ 126 | @Override 127 | public void updateProgress(float value) { 128 | final int size = mTransitionList.size(); 129 | for (int i = 0; i < size; i++) { 130 | mTransitionList.get(i).updateProgress(value); 131 | } 132 | } 133 | 134 | /** 135 | * Stops all transitions. 136 | */ 137 | @Override 138 | public void stopTransition() { 139 | //call listeners so they can perform their actions first, like modifying this adapter's transitions 140 | for (int i = 0, size = mListenerList.size(); i < size; i++) { 141 | mListenerList.get(i).onTransitionEnd(this); 142 | } 143 | 144 | for (int i = 0, size = mTransitionList.size(); i < size; i++) { 145 | mTransitionList.get(i).stopTransition(); 146 | } 147 | } 148 | 149 | @Override 150 | public void addTransitionListener(TransitionManagerListener listener) { 151 | if (!mListenerList.contains(listener)) { 152 | mListenerList.add(listener); 153 | } 154 | } 155 | 156 | @Override 157 | public void removeTransitionListener(TransitionManagerListener listener) { 158 | mListenerList.remove(listener); 159 | } 160 | 161 | @Override 162 | public void notifyTransitionStart() { 163 | for (int i = 0, size = mListenerList.size(); i < size; i++) { 164 | mListenerList.get(i).onTransitionStart(this); 165 | } 166 | } 167 | 168 | @Override 169 | public void notifyTransitionEnd() { 170 | for (int i = 0, size = mListenerList.size(); i < size; i++) { 171 | mListenerList.get(i).onTransitionEnd(this); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /core/src/main/java/com/kaichunlin/transition/AbstractTransition.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition; 2 | 3 | import android.support.annotation.CheckResult; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.view.View; 7 | import android.view.animation.Interpolator; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.Comparator; 12 | import java.util.List; 13 | 14 | /** 15 | * Provides common implementations for all transitions. 16 | */ 17 | public abstract class AbstractTransition implements Transition { 18 | List mSetupList = new ArrayList<>(); 19 | String mId; 20 | boolean mReverse; 21 | Interpolator mInterpolator; 22 | View mTarget; 23 | boolean mUpdateStateAfterUpdateProgress; 24 | 25 | public AbstractTransition(@Nullable String id) { 26 | this.mId = id; 27 | } 28 | 29 | @Override 30 | public T setId(@Nullable String id) { 31 | mId = id; 32 | return self(); 33 | } 34 | 35 | @Override 36 | public String getId() { 37 | return mId == null ? (mId = toString()) : mId; 38 | } 39 | 40 | @Override 41 | public boolean startTransition(float progress) { 42 | if (startTransition()) { 43 | updateProgress(progress); 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | @Override 50 | public void setProgress(float progress) { 51 | //TODO optimize 52 | startTransition(); 53 | updateProgress(progress); 54 | stopTransition(); 55 | } 56 | 57 | @Override 58 | public T reverse() { 59 | String id = getId(); 60 | String REVERSE = "_REVERSE"; 61 | if (id.endsWith(REVERSE)) { 62 | setId(id.substring(0, id.length() - REVERSE.length())); 63 | } else { 64 | setId(id + REVERSE); 65 | } 66 | 67 | mReverse = !mReverse; 68 | return self(); 69 | } 70 | 71 | @Override 72 | public T setSetup(@NonNull S setup) { 73 | if (setup != null) { 74 | mSetupList.add(setup); 75 | } 76 | return self(); 77 | } 78 | 79 | /** 80 | * Only true if it has merged another Transition 81 | * 82 | * @return 83 | */ 84 | boolean hasMultipleSetup() { 85 | return mSetupList.size() > 1; 86 | } 87 | 88 | @Override 89 | public void setTarget(@Nullable View target) { 90 | mTarget = target; 91 | invalidate(); 92 | } 93 | 94 | @Override 95 | public View getTarget() { 96 | return mTarget; 97 | } 98 | 99 | @Override 100 | public T setUpdateStateAfterUpdateProgress(boolean updateStateAfterUpdateProgress) { 101 | mUpdateStateAfterUpdateProgress = updateStateAfterUpdateProgress; 102 | return self(); 103 | } 104 | 105 | /** 106 | * Invalidates the current transition, which may mean the currently running transition is stopped. 107 | */ 108 | protected abstract void invalidate(); 109 | 110 | // Returns the correct type. 111 | protected abstract T self(); 112 | 113 | @Override 114 | public T setInterpolator(@Nullable Interpolator interpolator) { 115 | mInterpolator = interpolator; 116 | return self(); 117 | } 118 | 119 | @CheckResult 120 | @Override 121 | public AbstractTransition clone() { 122 | AbstractTransition newClone = null; 123 | try { 124 | newClone = (AbstractTransition) super.clone(); 125 | newClone.setId(newClone.getId() + "_CLONE"); 126 | newClone.mSetupList = new ArrayList<>(mSetupList.size()); 127 | newClone.mSetupList.addAll(mSetupList); 128 | } catch (CloneNotSupportedException e) { 129 | e.printStackTrace(); 130 | } 131 | return newClone; 132 | } 133 | 134 | /** 135 | * Checks to see if another AbstractTransition's states is isCompatible for merging. 136 | * 137 | * @param another 138 | * @return 139 | */ 140 | @CheckResult 141 | public boolean isCompatible(AbstractTransition another) { 142 | if (getClass().equals(another.getClass()) && mTarget == another.mTarget && mReverse == another.mReverse && ((mInterpolator == null && another.mInterpolator == null) || 143 | mInterpolator.getClass().equals(another.mInterpolator.getClass()))) { 144 | return true; 145 | } 146 | return false; 147 | } 148 | 149 | /** 150 | * Merge another AbstractTransition's states into this object, such that the other AbstractTransition 151 | * can be discarded. 152 | * 153 | * @param another 154 | * @return true if the merge is successful. 155 | */ 156 | public boolean merge(AbstractTransition another) { 157 | if (!isCompatible(another)) { 158 | return false; 159 | } 160 | if (another.mId != null) { 161 | if (mId == null) { 162 | mId = another.mId; 163 | } else { 164 | StringBuilder sb = new StringBuilder(mId.length() + another.mId.length()); 165 | sb.append(mId); 166 | sb.append("_MERGED_"); 167 | sb.append(another.mId); 168 | mId = sb.toString(); 169 | } 170 | } 171 | mUpdateStateAfterUpdateProgress |= another.mUpdateStateAfterUpdateProgress; 172 | mSetupList.addAll(another.mSetupList); 173 | Collections.sort(mSetupList, new Comparator() { 174 | @Override 175 | public int compare(S lhs, S rhs) { 176 | if (lhs instanceof AbstractTransitionBuilder && rhs instanceof AbstractTransitionBuilder) { 177 | AbstractTransitionBuilder left = (AbstractTransitionBuilder) lhs; 178 | AbstractTransitionBuilder right = (AbstractTransitionBuilder) rhs; 179 | float startLeft = left.mReverse ? left.mEnd : left.mStart; 180 | float startRight = right.mReverse ? right.mEnd : right.mStart; 181 | return (int) ((startRight - startLeft) * 1000); 182 | } 183 | return 0; 184 | } 185 | }); 186 | 187 | return true; 188 | } 189 | 190 | /** 191 | * Represents an object that configures how a Transition for a specific effect. 192 | */ 193 | public interface Setup { 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /app/src/main/java/com/kaichunlin/transition/app/DrawerViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.kaichunlin.transition.app; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.widget.DrawerLayout; 5 | import android.support.v7.app.ActionBarDrawerToggle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.View; 9 | import android.view.ViewTreeObserver; 10 | import android.view.animation.AccelerateDecelerateInterpolator; 11 | import android.view.animation.LinearInterpolator; 12 | 13 | import com.kaichunlin.transition.Cascade; 14 | import com.kaichunlin.transition.TransitionManager; 15 | import com.kaichunlin.transition.TransitionManagerListener; 16 | import com.kaichunlin.transition.ViewTransition; 17 | import com.kaichunlin.transition.ViewTransitionBuilder; 18 | import com.kaichunlin.transition.adapter.DrawerListenerAdapter; 19 | import com.kaichunlin.transition.animation.AnimationManager; 20 | import com.kaichunlin.transition.internal.debug.TraceTransitionManagerListener; 21 | import com.kaichunlin.transition.util.TransitionUtil; 22 | 23 | import kaichunlin.transition.app.R; 24 | 25 | 26 | public class DrawerViewActivity extends AppCompatActivity implements View.OnClickListener { 27 | 28 | private ActionBarDrawerToggle mDrawerToggle; 29 | private DrawerLayout mDrawerLayout; 30 | private DrawerListenerAdapter mDrawerListenerAdapter; 31 | private ViewTransitionBuilder mRotateEffectBuilder; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | 37 | setContentView(R.layout.activity_drawer_view); 38 | findViewById(R.id.rotate_android).setOnClickListener(this); 39 | findViewById(R.id.slide_subviews).setOnClickListener(this); 40 | findViewById(R.id.slide_bg).setOnClickListener(this); 41 | 42 | final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 43 | setSupportActionBar(toolbar); 44 | 45 | mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout); 46 | mDrawerToggle = new ActionBarDrawerToggle( 47 | this, /* host Activity */ 48 | mDrawerLayout, /* DrawerLayout object */ 49 | toolbar, 50 | R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ 51 | R.string.navigation_drawer_close /* "close drawer" description for accessibility */ 52 | ); 53 | 54 | //Create an adapter that listens for ActionBarDrawerToggle state change and update transition states 55 | mDrawerListenerAdapter = new DrawerListenerAdapter(mDrawerToggle, R.id.drawerList); 56 | mDrawerListenerAdapter.setDrawerLayout(mDrawerLayout); 57 | mDrawerListenerAdapter.setDrawerListener(new DialogDrawerListener(this)); 58 | 59 | //this builder is used to build both transition & animation effect 60 | mRotateEffectBuilder = ViewTransitionBuilder.transit(findViewById(R.id.big_icon)).rotation(0f, 360f).scaleX(1f, 0.2f).scaleY(1f, 0f).translationX(200f); 61 | 62 | //build the desired transition and adds to the adapter 63 | final ViewTransition transition = mRotateEffectBuilder.build(); 64 | mDrawerListenerAdapter.addTransition(ViewTransitionBuilder.transit(findViewById(R.id.mainContent)).scale(0.9f)); 65 | mDrawerListenerAdapter.addTransition(transition); 66 | 67 | //configure the transition after the layout is complete 68 | TransitionUtil.executeOnGlobalLayout(this, new ViewTreeObserver.OnGlobalLayoutListener() { 69 | @Override 70 | public void onGlobalLayout() { 71 | //since the start animation is the reverse of the transition, set the current view state to transition's final state 72 | transition.setProgress(1f); 73 | //init an animation and add a delay to prevent stutter, needs to be higher if animation is enabled 74 | final AnimationManager animationManager = new AnimationManager(); 75 | mRotateEffectBuilder.reverse().buildAnimationFor(animationManager); 76 | 77 | //add fade-in animation 78 | Cascade cascade = new Cascade(Cascade.SEQUENTIAL, 0.9f); 79 | ViewTransitionBuilder.transit(findViewById(R.id.lay_buttons)).alpha(0f, 1f).visible().interpolator(new AccelerateDecelerateInterpolator()).transitViewGroup(new ViewTransitionBuilder.ViewGroupTransition() { 80 | @Override 81 | public void transit(ViewTransitionBuilder builder, ViewTransitionBuilder.ViewGroupTransitionConfig config) { 82 | builder.buildAnimationFor(animationManager); 83 | } 84 | }, cascade); 85 | animationManager.startAnimation(600); 86 | 87 | //this is to prevent conflict when the drawer is being opened while the above animation is still in progress 88 | //unfortunately there's no way to reconcile the two, so the transiting/animating View will "jump" to a new state 89 | //TODO evaluate if it's possible to reconcile the two states automatically, maybe if they share the same ITransition instance? 90 | mDrawerListenerAdapter.addTransitionListener(new TransitionManagerListener() { 91 | @Override 92 | public void onTransitionStart(TransitionManager manager) { 93 | animationManager.endAnimation(); 94 | } 95 | 96 | @Override 97 | public void onTransitionEnd(TransitionManager manager) { 98 | } 99 | }); 100 | } 101 | }); 102 | 103 | //debug 104 | mDrawerListenerAdapter.addTransitionListener(new TraceTransitionManagerListener()); 105 | } 106 | 107 | @Override 108 | protected void onPostCreate(Bundle savedInstanceState) { 109 | super.onPostCreate(savedInstanceState); 110 | 111 | mDrawerToggle.syncState(); 112 | } 113 | 114 | @Override 115 | public void onClick(View v) { 116 | updateTransition(v); 117 | } 118 | 119 | public void updateTransition(View v) { 120 | mDrawerListenerAdapter.removeAllTransitions(); 121 | switch (v.getId()) { 122 | case R.id.rotate_android: 123 | mDrawerListenerAdapter.addTransition(ViewTransitionBuilder.transit(findViewById(R.id.mainContent)).scale(0.9f)); 124 | mDrawerListenerAdapter.addTransition(mRotateEffectBuilder); 125 | break; 126 | case R.id.slide_subviews: 127 | Cascade cascade = new Cascade(Cascade.RUN_TO_THE_END, 0.6f).cascadeStart(0.2f); 128 | ViewTransitionBuilder.transit(findViewById(R.id.lay_buttons)).interpolator(new AccelerateDecelerateInterpolator()).transitViewGroup(new ViewTransitionBuilder.ViewGroupTransition() { 129 | @Override 130 | public void transit(ViewTransitionBuilder builder, ViewTransitionBuilder.ViewGroupTransitionConfig config) { 131 | builder.translationX(config.parentViewGroup.getRight()).buildFor(mDrawerListenerAdapter); 132 | } 133 | }, cascade); 134 | break; 135 | case R.id.slide_bg: 136 | mDrawerListenerAdapter.addTransition(ViewTransitionBuilder.transit(findViewById(R.id.bg)).interpolator(new LinearInterpolator()).translationXAsFractionOfWidth(0.25f)); 137 | break; 138 | } 139 | } 140 | } 141 | --------------------------------------------------------------------------------