Releases: wirecube/android_additive_animations
Fix visibility start/end actions
This release fixes #19, which could cause view visibility animations to not work correctly if no custom start- or end action was defined.
It also fixes an issue where the start- and end action could be called multiple times for the same target for state animations.
Builders for state animations
This release adds a few simpler builder methods to the AnimationState<V>
class, and a few specializations for Android Views, such as the ViewAnimationState
.
The improvements to the AnimationState
API were made to provide a simpler, safer alternative to the usage of addEndAction()
, since there are always potential race conditions when executing end actions in highly interactive animations.
The new API allows you to create states on the fly without too much syntactic overhead:
AdditiveAnimator.animate(target)
.state(new ViewAnimationState(
new ViewAnimation(View.TRANSLATION_X, 40f),
new ViewAnimation(View.TRANSLATION_Y, 40f),
))
.start()
You can now also more easily create custom visibility animations (with custom start/end actions) by utilizing the new ViewVisibilityBuilder
.
Improved error handling for incorrect usage
An IllegalStateException
will now be thrown if you try to enqueue an animation without setting a target first.
Move to AndroidX
The project has moved to used AndroidX.
Memory Leak fixes, additional way of building animations
This release fixes a potential memory leak that might cause fragments to be retained by AdditiveAnimator
animations, especially infinite animations.
There is a breaking API change with regards to cancelling animations to catch this kind of leak easily:
cancelAnimations()
has been replaced with cancelAnimationsForObject()
and cancelAnimationsInCollection()
.
Also, don't forget to cancel your infinite animations when the fragment/view is being destroyed!
See below for a short explanation of the issue*.
Furthermore, a new API for building animations has been added that allows simple orchestration of multiple AdditiveAnimator
instances.
Three new methods are available: AnimationSequence.playTogether()
, AnimationSequence.playSequentially()
and AnimationSequence.playWithDelayBetweenAnimations()
.
Each of these methods takes a List<AnimationSequence>
, so the calls to these methods can be nested like this:
AnimationSequence.playSequentially(
AnimationSequence.playTogether(…),
AnimationSequence.playSequentially(
AdditiveAnimator.animate(…),
AnimationSequence.playTogether(…)
)
).start();
Although this syntax doesn't introduce any new features to the library, it does provides a different way of building animations through composition – which can map more nicely to specific architectures.
For example, it allows outsourcing some of the animation creation code to different methods without passing around an AdditiveAnimator
object.
The API is also made extensible, so you can provide your own AnimationSequence
implementations – for example, adding support for keyframe-based animations with relative timings would be relatively simple with this approach.
*Explanation for the breaking change: Since cancelAnimations()
was overloaded to both take an Object
and a List<Object>
as a parameter, the compiler couldn't know which one you meant when you passed in an ArrayList – and it would incorrectly call the one taking an Object
, causing the animations to not actually be canceled.
Fix animation states not being applied to all targets
Animating multiple targets simultaneously using a state
object caused only the last object in the list to actually receive the correct state variable, thus not running all of the animations and start/end actions correctly.
1.7.4 - Better out-of-the-box performance, improved state handling
AdditiveAnimator
now knows which animations require a call to requestLayout
and will just call invalidate
for animations which don't require it.
This change deprecates skipRequestLayout
, since it is no longer needed and therefore comes with a nice automatic performance improvement.
Another improvement was made regarding animation state handling:
States no longer require an id
property, since they are now checked for equality instead.
Important: You should not reuse AnimationState
objects for multiple animations since that can cause race conditions when triggering their respective AnimationEndAction
when quickly toggling between states.
1.7.3 - convenience and cleanup
This release adds just one convenience feature:
It lets you create one-line properties by using the new create
method of FloatProperty
.
This can often simplify creating animations for custom objects or properties quite a bit:
AdditiveAnimator.animate(myView)
.property(100f, FloatProperty.create("radius", v -> v.getRadius(), (v, r) -> v.setRadius(r));
.start();
The demos using subclasses and custom animations have been reworked to make use of this simplification.
1.7.2 - Animation States and View Visibility animations!
Visibility animations
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
).
Animation States
AdditiveAnimator now supports the concepts of animation states. A State encapsulates a set of animations to perform and an ID to uniquely identify it.
What's special about this is that AdditiveAnimator can now automatically decide whether or not to run animation start/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 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()-chaining block is executed whether or not the highlightedView is actually still highlighted.
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!
1.6.2 - Bugfix
This release fixes an issue where switchDuration()
doesn't work correctly in combination with targets()
when called in a particular order.