Giter Club home page Giter Club logo

android_additive_animations's Introduction

Android Additive Animations

Additive animations for Android! An easy way to additively animate any property of any object, with convenient builder methods for Views.

Get a good overview of this library here: https://medium.com/@david.gansterd/bringing-smooth-animation-transitions-to-android-88786347e512

Integration

To use AdditiveAnimator in your project, add the following lines to your build.gradle:

dependencies {
    compile 'at.wirecube:additive_animations:1.9.3'
}

Quick start

Here is a sample of what additive animations can do for the user experience (note: there seem to be a few dropped frames in the gif which aren't present when running on a device):

Additive animations demo

The amount code required to produce this animation is trivial:

public boolean onTouch(View v, MotionEvent event) {
    AdditiveAnimator.animate(animatedView, 1000).x(event.getX()).y(event.getY()).start();
    return true;
}

Additionally, AdditiveAnimator supports animating multiple targets simultaneously without any boilerplate:

new AdditiveAnimator().setDuration(1000)
                      .target(myView1).x(100).y(100)
                      .target(myView2).xBy(20).yBy(20)
                      .start();

New in 1.6:

1.6 added a some convenience features, such as the ability to switch duration midway to building an animation, providing a SpringInterpolator class, and being able to switch back to the default interpolator using the switchToDefaultInterpolator() method.

Then main attraction of 1.6 though:

You can now animate the same property for multiple views without looping.

new AdditiveAnimator().targets(myView1, myView2).alpha(0).start();

To achieve a delay between the start of the animation of each target, you can optionally add the 'stagger' parameter to add a delay between each of the animations.

long staggerBetweenAnimations = 50L;
new AdditiveAnimator().targets(Arrays.asList(myView1, myView2), staggerBetweenAnimations).alpha(0).start();

In this example, myView1 is faded out 50 milliseconds before myView2.

Starting with 1.6.1, the delay between the animation of the views is preserved when using then() chaining:

long staggerBetweenAnimations = 50L;
AdditiveAnimator.animate(Arrays.asList(myView1, myView2), staggerBetweenAnimations).translationYBy(50).thenWithDelay(20).translationYBy(-50).start();

The timeline of this animation looks like this: myView1 is translated by 50 pixels at delay 0. myView2 is translated by 50 pixels at delay 50. myView1 is translated by -50 pixles at delay 20. myView2 is translated by -50 pixles at delay 70.

Check out MultipleViewsAnimationDemoFragment in the demo app for an example of this!

Visibility animations

New in 1.7.2

View visibility can now be properly animated without adding an animation end block and checking if the visibility should be updated based on some other state variable:

AdditiveAnimator.animate(view)
    .fadeVisibility(View.GONE) // fades out the view, then sets visibility to GONE
    .start();

Since fading the visibiliy is probably the most common usecase, there's a default builder method for it. A few more default animations are provided as well:

AdditiveAnimator.animate(view)
    // the first param decides whether the view should be GONE or INVISIBLE,
    // the second one decides how much to move the view as it fades out
    .visibility(ViewVisibilityAnimation.fadeOutAndTranslateX(true, 100f)) // only move x
    .visibility(ViewVisibilityAnimation.fadeOutAndTranslateY(true, 100f)) // only move y
    .visibility(ViewVisibilityAnimation.fadeOutAndTranslate(true, 100f, 100f)) // move x and y
    .start();

The new ViewVisibilityAnimation class provides a convenient constructor to make your own view state animations - an example can be found in the new demo (StateDemoFragment).

New in 1.9.2 The API for building new AnimationStates and view visibility animations has been improved. You can now access the AnimationState.Builder<T> class to more easily use one-off states. There are also more specializations for View-specific classes, like the ViewAnimation, ViewAnimationState and ViewStateBuilder.

Animation States

AdditiveAnimator now supports the concept of animation states. A State encapsulates a set of animations to perform when an object changes its... state.

What's special about this is that AdditiveAnimator can now automatically decide whether or not to run animation start and end blocks - if the view is no longer in the appropriate state for the block, it won't run.

This is how the view visibility feature is implemented, and it can easily be extended to work with all kinds of custom states via the new state() builder method.

For example, we might want to switch the states of some views between highlighted and normal in a then()-chained block like this:

new AdditiveAnimator()
    .targets(normalViews)
    .scale(1f) // normal
    .then()
    .target(highlightedView)
    .scale(1.2f) // highlighted
    .start();

There's a race condition in this piece of code: The then()-chained animation is executed whether or not the highlightedView is actually still highlighted by the time the previous animation finishes.

Animation states fix this problem entirely:

new AdditiveAnimator()
    .targets(normalViews)
    .state(MyViewState.NORMAL)
    .then()
    .target(highlightedView)
    .state(MyViewState.HIGHLIGHTED)
    .start();

With this code, the animations associated with the NORMAL and HIGHLIGHTED states are only allowed to run if the state of the enqueued animation still matches the current view state. Even when rapidly switching which view is highlighted, this will produce the desired outcome.

Animating all kinds of objects and properties

In addition to the builder methods for views, there are multiple options for animating custom properties of any object. The first - highly recommended - option is to simply provide a Property for the object you want to animate, plus (if needed) a way to trigger a redraw of your custom object:

// Declaring an animatable property:
FloatProperty<Paint> mPaintColorProperty = 
        FloatProperty.create("PaintColor", paint -> (float) paint.getColor(), (paint, color) -> paint.setColor((int) color));
...

// Using the property to animate the color of a paint:
AdditiveObjectAnimator.animate(myPaint)
    .property(targetColor, // target value
              new ColorEvaluator(), // custom evaluator for colors
              mPaintColorProperty) // how to get/set the property value
    .setAnimationApplier(new ViewAnimationApplier(myView)) // tells the generic AdditiveObjectAnimator how to apply the changed values
    .start();

The second option is not recommended unless you need very specific control over how properties are applied (for example, only applying x/y-scroll changes together instead of one at a time when animating 2-dimensional scrolling). In works by subclassing BaseAdditiveAnimator and providing your own builder methods (which are usually one-liners) such as this:

class PaintAdditiveAnimator extends BaseAdditiveAnimator<PaintAdditiveAnimator, Paint> {
    private static final String COLOR_ANIMATION_KEY = "ANIMATION_KEY";

    // Support animation chaining by providing a construction method:
    @Override protected PaintAdditiveAnimator newInstance() { return new PaintAdditiveAnimator(); }

    // Custom builder method for animating the color of a Paint:
    public PaintAdditiveAnimator color(int color) {
        return animate(new AdditiveAnimation<>(
            mCurrentTarget, // animated object (usually this is the current target)
            COLOR_ANIMATION_KEY, // key to identify the animation
            mCurrentTarget.getColor(), // start value
            color)); // target value
    }

    // Applying the changed properties when they don't have a Property wrapper:
    @Override protected void applyCustomProperties(Map<String, Float> tempProperties, Paint target) {
        if(tempProperties.containsKey(COLOR_ANIMATION_KEY)) {
            target.setColor(tempProperties.get(COLOR_ANIMATION_KEY).intValue());
        }
    }
    
    // For animations without a property, your subclass is responsible for providing the current property value.
    // This is easy to forget when adding new animatable properties, which is one of the reasons this method is discouraged.
    @Override public Float getCurrentPropertyValue(String propertyName) {
        switch(propertyName) {
            case ANIMATION_KEY:
                return mCurrentTarget.getColor();
        }
        return null;
    }
}

A more complete example of both of these approaches can be found in the sample app in CustomDrawingFragment.java.

Of course you can combine both approaches - custom builder methods which animate properties. This is the recommended approach and is how everything provided by AdditiveAnimator was built.

Both versions only require very little code, and the few lines you have to write are almost always trivial - mostly getters and setters.

Note:

There is a breaking change when migrating from a version <1.5.0 to a version >= 1.5.0: Instead of subclassing AdditiveAnimator, you now have to subclass SubclassableAdditiveViewAnimator instead. Sorry for the change, it was necessary due to Java constraints (nesting of generics across subclasses) and improves interop with Kotlin (no more generic arguments required!).

Note

There is another breaking change when migrating from <1.6.0 to >= 1.6.0: You have to implement a new abstract method (getCurrentPropertyValue()) when subclassing BaseAdditiveAnimator. This method is only called when using tag-based animations, instead of property-based ones. If your subclass does not use tag-based animations, you can simply return null;.

License

AdditiveAnimator is licensed under the Apache v2 license:

Copyright 2021 David Ganster

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

android_additive_animations's People

Contributors

davidganster avatar ryanmoelter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android_additive_animations's Issues

iOS?

Hi there!

Thanks for your lib, it's awesome! What about the same but for iOS? :)

Question

Hi, i would like to ask question about specific use case. How do you handle startAction, endAction cases, such as:
when animation1 with endAction that sets viewA.visibility=GONE and animation2 with startAction that sets viewA.visibility=VISIBLE. Problem occurs if animation1 is started and in the middle of animation1, animation2 is started. Desired result of viewA visibility is VISIBLE since animation2 is started later. But result is viewA visibility GONE because animation1s endAction runs even animation2 is started later.

Library sometimes crashes when mRunningAnimationsManager is null internally

I've go following stacktrace:

03-31 12:36:17.715  4093  4093 E AndroidRuntime: FATAL EXCEPTION: main
03-31 12:36:17.715  4093  4093 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'void at.wirecube.additiveanimations.additive_animator.RunningAnimationsManager.setCurrentState(at.wirecube.additiveanimations.additive_animator.animation_set.AnimationState)' on a null object reference
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at at.wirecube.additiveanimations.additive_animator.BaseAdditiveAnimator.state(BaseAdditiveAnimator.java:371)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at at.wirecube.additiveanimations.additive_animator.SubclassableAdditiveViewAnimator.fadeVisibility(SubclassableAdditiveViewAnimator.java:180)
...
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at android.os.Handler.handleCallback(Handler.java:873)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:99)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:193)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6680)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
03-31 12:36:17.715  4093  4093 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

There is no particular case, when it happen, but it seems that it is related to the object mRunningAnimationsManager being null sometimes (it is nullable by design and there is no null checks in that part of code).

Can you fix that?

Content gone in 1.9.2 but works in 1.9.1

I just upgraded from 1.7.5 to 1.9.2 and most of my app's content was not shown anymore. In lots of places I fade in/out content with AdditiveAnimator. After two hours of searching for the root cause I switched from 1.9.2 back to 1.9.1 and suddenly everything is working again.

Might there be a bug in 1.9.2? I saw that lots of visibility/fade related framework code has been changed in 1.9.2.

This is a typical pattern which works in 1.9.1 but does NOT work in 1.9.2:

AdditiveAnimator.animate(contentViews)
    .setDuration(UI.CARDS_INTRO_DURATION)
    .setInterpolator(new DecelerateInterpolator())
    .fadeVisibility(show ? View.VISIBLE : View.GONE)
    .start();

Am I missing something?

Provide Kotlin extensions

AdditiveAnimator.animate(this)
    .addEndAction(object : AnimationEndListener() {
        override fun onAnimationEnd(wasCancelled: Boolean) {
            
        }
    })

Function above can be simplified using typealias and lambda.

typealias AnimationEndListener =
    (wasCancelled: Boolean) -> Unit

fun AdditiveAnimator.addEndAction(listener: AnimationEndListener) {
    addEndAction(object : AnimationEndListener() {
        override fun onAnimationEnd(wasCancelled: Boolean) {
            listener(wasCancelled)
        }
    })
}

// Use
AdditiveAnimator.animate(this)
    .addEndAction { wasCancelled ->
         
    }

Do you have any plans to do something like this? (Support Kotlin)

effect?

Do not understand the effect of this is what?
2017-08-12_221649

Leak

Hi, i run animations using

AdditiveAnimator()
  .setDuration()
  ...
  .addStartAction(...)
  ...
  .start()

and i get leak reports from LeakCanary like below. I assume AdditiveAnimator keeps reference to Views which keeps reference to Activity Context. How do we clear those references when leaving the Activity?

screen shot 2018-09-05 at 15 13 37

pivotXY for view rotation around center

The animation of rotation of a view looks wierd because its using the upper left corner for the rotation, how can I change the pivotX and pivotY to the center of the view to animate around this center?

Cancel repeating animation

Hi, how to cancel animation with repeat count INFINITE?

shakeAnimation = AdditiveAnimator.animate(image)
                .setDuration(200L)
                .setInterpolator(LinearInterpolator())

                .rotation(3f)
                .then()
                .rotation(-3f)

                .setRepeatCount(ValueAnimator.INFINITE)
                .setRepeatMode(ValueAnimator.REVERSE)

shakeAnimation.cancelAllAnimations() does not stop animation.

Move to AndroidX

Would you consider move to AndroidX? I still get the following running canISayByeByeJetifier

Scanning at.wirecube:additive_animations:1.8.0
 Absoulute path: /Users/xiwei/.gradle/caches/modules-2/files-2.1/at.wirecube/additive_animations/1.8.0/1af778f898822b9b74d83f8c3632188f191fc41b/additive_animations-1.8.0.aar
 Graphs to this dependency:
 +---at.wirecube:additive_animations:1.8.0
 Issues found:
 * at/wirecube/additiveanimations/additive_animator/SubclassableAdditiveViewAnimator.class -> android/support/v4/view/ViewCompat
 * at/wirecube/additiveanimations/helper/EaseInOutPathInterpolator.class -> android/support/v4/view/animation/PathInterpolatorCompat

Thanks

purpose?

If it is "return true", when the choice of "Additive animations enabled is false", long press the effect is not ideal
01
Maybe you can change that way, or do you have other ideas?
02

Gradle build failed

Error:Unable to find method 'org.gradle.api.internal.project.ProjectInternal.getPluginManager()Lorg/gradle/api/internal/plugins/PluginManagerInternal;'.
Possible causes for this unexpected error include:

In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.

Annoying `AdditiveAnimator` constructor with Kotlin

Problem:
Kotlin requires that type information be provided. i.e. the following code has an error.

AdditiveAnimator()

Instead, it has to be used like this:

AdditiveAnimator<AdditiveAnimator<*>>()

It's quite ugly and annoying.

Short Term Solution:
Please provide static methods that duplicate the constructor arguments.

AdditiveAnimator.create()
AdditiveAnimator.create(300L)

Long Term Solution:
I don't understand the library well enough to recommend a good long-term solution, but ideally the API would be reconsidered with Kotlin in mind.

Properly blend delayed animations

First off, thanks for this library! It's made animating things so much better.

But could we include animations with delays in the blending calculations as soon as we call .start() on the chain? Say we have a simple fade-out-then-fade-in:

animator.animate(view)
    .alpha(0f)
    .then()
    .target(otherView)
    .alpha(1f)
    .start()

And we have its reverse:

animator.animate(otherView)
    .alpha(0f)
    .then()
    .target(view)
    .alpha(1f)
    .start()

If we run these in quick succession, the outcome is that both views are visible, since the .alpha(1f) animations are started last due to the .then() delay.

I would expect that the blending would take into account any animations that have been .start()ed, i.e. that the last animation block to be declared and .start()ed would be the end state of all views involved, regardless of delays/choreography.

I can make a PR for this if I find the time before you do, but I'd love to hear your thoughts first.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.