xref: /aosp_15_r20/frameworks/base/packages/SystemUI/docs/physics-animation-layout.md (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker# Physics Animation Layout
2*d57664e9SAndroid Build Coastguard Worker
3*d57664e9SAndroid Build Coastguard Worker## Overview
4*d57664e9SAndroid Build Coastguard Worker**PhysicsAnimationLayout** works with implementations of **PhysicsAnimationController** to configure and run physics-based animations for each of its child views. During the initial construction of the animations, the layout queries the controller for basic configuration settings such as which properties to animate, which animations to chain together, and the default physics parameters to use.
5*d57664e9SAndroid Build Coastguard Worker
6*d57664e9SAndroid Build Coastguard WorkerOnce the animations are built, the controller can access **PhysicsPropertyAnimator** instances to run them. The animator behaves similarly to the familiar `ViewPropertyAnimator`, with the ability to animate `alpha`, `translation`, and `scale` values. It also supports additional functionality such as `followAnimatedTargetAlongPath` for more advanced motion.
7*d57664e9SAndroid Build Coastguard Worker
8*d57664e9SAndroid Build Coastguard WorkerThe controller is notified whenever children are added or removed from the layout, so that it can animate their entrance or exit, respectively.
9*d57664e9SAndroid Build Coastguard Worker
10*d57664e9SAndroid Build Coastguard WorkerAn example usage is Bubbles, which uses a PhysicsAnimationLayout for its stack of bubbles. Bubbles has controller subclasses including StackAnimationController and ExpansionAnimationController. StackAnimationController tells the layout to configure the translation animations to be chained (for the ‘following’ drag effect), and has methods such as ```moveStack(x, y)``` to animate the stack to a given point. ExpansionAnimationController asks for no animations to be chained, and exposes methods like ```expandStack()``` and ```collapseStack()```, which animate the bubbles to positions along the bottom of the screen.
11*d57664e9SAndroid Build Coastguard Worker
12*d57664e9SAndroid Build Coastguard Worker## PhysicsAnimationController
13*d57664e9SAndroid Build Coastguard WorkerPhysicsAnimationController is a public abstract class in PhysicsAnimationLayout. Controller instances must override configuration methods, which are used by the layout while constructing the animations, and animation control methods, which are called to initiate animations in response to events.
14*d57664e9SAndroid Build Coastguard Worker
15*d57664e9SAndroid Build Coastguard Worker### Configuration Methods
16*d57664e9SAndroid Build Coastguard Worker![Diagram of how animations are configured using the controller's configuration methods.](physics-animation-layout-config-methods.png)
17*d57664e9SAndroid Build Coastguard WorkerThe controller must override the following methods:
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker```Set<ViewProperty> getAnimatedProperties()```
20*d57664e9SAndroid Build Coastguard WorkerReturns the properties, such as TRANSLATION_X and TRANSLATION_Y, for which the layout should construct physics animations.
21*d57664e9SAndroid Build Coastguard Worker
22*d57664e9SAndroid Build Coastguard Worker```int getNextAnimationInChain(ViewProperty property, int index)```
23*d57664e9SAndroid Build Coastguard WorkerIf the animation at the given index should update another animation whenever its value changes, return the index of the other animation. Otherwise, return NONE. This is used to chain animations together, so that when one animation moves, the other ‘follows’ closely behind.
24*d57664e9SAndroid Build Coastguard Worker
25*d57664e9SAndroid Build Coastguard Worker```float getOffsetForChainedPropertyAnimation(ViewProperty property)```
26*d57664e9SAndroid Build Coastguard WorkerValue to add every time chained animations update the subsequent animation in the chain. For example, returning TRANSLATION_X offset = 20px means that if the first animation in the chain is animated to 10px, the second will update to 30px, the third to 50px, etc.
27*d57664e9SAndroid Build Coastguard Worker
28*d57664e9SAndroid Build Coastguard Worker```SpringForce getSpringForce(ViewProperty property)```
29*d57664e9SAndroid Build Coastguard WorkerReturns a SpringForce instance to use for animations of the given property. This allows the controller to configure stiffness and bounciness values. Since the physics animations internally use SpringForce instances to hold inflight animation values, this method needs to return a new SpringForce instance each time - no constants allowed.
30*d57664e9SAndroid Build Coastguard Worker
31*d57664e9SAndroid Build Coastguard Worker### Animation Control Methods
32*d57664e9SAndroid Build Coastguard WorkerOnce the layout has used the controller’s configuration properties to build the animations, the controller can use them to actually run animations. This is done for two reasons - reacting to a view being added or removed, or responding to another class (such as a touch handler or broadcast receiver) requesting an animation. ```onChildAdded```, ```onChildRemoved```, and ```setChildVisibility``` are called automatically by the layout, giving the controller the opportunity to animate the child in/out/visible/gone. Custom methods are called by anyone with access to the controller instance to do things like expand, collapse, or move the child views.
33*d57664e9SAndroid Build Coastguard Worker
34*d57664e9SAndroid Build Coastguard WorkerIn either case, the controller can use `super.animationForChild` to retrieve a `PhysicsPropertyAnimator` instance. This object behaves similarly to the `ViewPropertyAnimator` object you would receive from `View.animate()`.
35*d57664e9SAndroid Build Coastguard Worker
36*d57664e9SAndroid Build Coastguard Worker#### PhysicsPropertyAnimator
37*d57664e9SAndroid Build Coastguard Worker
38*d57664e9SAndroid Build Coastguard WorkerLike `ViewPropertyAnimator`, `PhysicsPropertyAnimator` provides the following methods for animating properties:
39*d57664e9SAndroid Build Coastguard Worker- `alpha(float)`
40*d57664e9SAndroid Build Coastguard Worker- `translationX/Y/Z(float)`
41*d57664e9SAndroid Build Coastguard Worker- `scaleX/Y(float)`
42*d57664e9SAndroid Build Coastguard Worker
43*d57664e9SAndroid Build Coastguard Worker...as well as shortcut methods to reduce the amount of boilerplate code needed for common use cases:
44*d57664e9SAndroid Build Coastguard Worker- `position(float, float, Runnable…)`, which starts translationX and translationY animations, and calls the provided callbacks only when both animations have completed.
45*d57664e9SAndroid Build Coastguard Worker- `followAnimatedTargetAlongPath(Path, int, TimeInterpolator)`, which animates a ‘target’ point along the given path using a traditional Animator. As the target moves, the translationX/Y physics animations are updated to follow the target, similarly to how they might follow a touch event location. This results in the view roughly following the path, but with natural motion that takes momentum into account. For example, if a path makes a 90 degree turn to the right, the physics animations will cause the view to curve naturally towards the new trajectory.
46*d57664e9SAndroid Build Coastguard Worker
47*d57664e9SAndroid Build Coastguard WorkerIt also provides the following configuration methods:
48*d57664e9SAndroid Build Coastguard Worker- `withStartDelay(int)`, for starting the animation after a given delay.
49*d57664e9SAndroid Build Coastguard Worker- `withStartVelocity(float)`, for starting the animation with the given start velocity.
50*d57664e9SAndroid Build Coastguard Worker- `withStiffness(float)` and `withDampingRatio(float)`, for overriding the default physics param values returned by the controller’s getSpringForce method.
51*d57664e9SAndroid Build Coastguard Worker- `withPositionStartVelocities(float, float)`, for setting specific start velocities for TRANSLATION_X and TRANSLATION_Y, since these typically differ.
52*d57664e9SAndroid Build Coastguard Worker- `start(Runnable)`, to start the animation, with an optional end action to call when the animations for every property (including chained animations) have completed.
53*d57664e9SAndroid Build Coastguard Worker
54*d57664e9SAndroid Build Coastguard WorkerFor example, moving the first child view:
55*d57664e9SAndroid Build Coastguard Worker
56*d57664e9SAndroid Build Coastguard Worker```
57*d57664e9SAndroid Build Coastguard WorkeranimationForChild(getChildAt(0))
58*d57664e9SAndroid Build Coastguard Worker    .translationX(100)
59*d57664e9SAndroid Build Coastguard Worker    .translationY(200)
60*d57664e9SAndroid Build Coastguard Worker    .setStartDelay(500)
61*d57664e9SAndroid Build Coastguard Worker    .start();
62*d57664e9SAndroid Build Coastguard Worker```
63*d57664e9SAndroid Build Coastguard Worker
64*d57664e9SAndroid Build Coastguard WorkerThis would use the physics animations constructed by the layout to spring the view to *(100, 200)* after 500ms.
65*d57664e9SAndroid Build Coastguard Worker
66*d57664e9SAndroid Build Coastguard WorkerIf the controller’s ```getNextAnimationInChain``` method set up the first child’s TRANSLATION_X/Y animations to be chained to the second child’s, this would result in the second child also springing towards (100, 200), plus any offset returned by ```getOffsetForChainedPropertyAnimation```.
67*d57664e9SAndroid Build Coastguard Worker
68*d57664e9SAndroid Build Coastguard Worker##### Advanced Usage
69*d57664e9SAndroid Build Coastguard WorkerThe animator has additional functionality to reduce the amount of boilerplate required for typical physics animation use cases.
70*d57664e9SAndroid Build Coastguard Worker
71*d57664e9SAndroid Build Coastguard Worker- Often, animations will set starting values for properties before the animation begins. Property methods like `translationX` have an overloaded variant: `translationX(from, to)`. When `start()` is called, the animation will set the view's translationX property to `from` before beginning the animation to `to`.
72*d57664e9SAndroid Build Coastguard Worker- We may want to use different end actions for each property. For example, if we're animating a view to the bottom of the screen, and also fading it out, we might want to perform an action as soon as the fade out is complete. We can use `alpha(to, endAction)`, which will call endAction as soon as the alpha animation is finished. A special case is `position(x, y, endAction)`, where the endAction is called when both translationX and translationY animations have completed.
73*d57664e9SAndroid Build Coastguard Worker- `PhysicsAnimationController` also provides `animationsForChildrenFromIndex(int, ChildAnimationConfigurator)`. This is a convenience method for starting animations on multiple child views, starting at the given index. The `ChildAnimationConfigurator` is called with a `PhysicsPropertyAnimator` for each child, where calls to methods like `translationX` and `withStartVelocity` can be made. `animationsForChildrenFromIndex` returns a `MultiAnimationStarter` with a single method, `startAll(endAction)`, which starts all of the animations and calls the end action when they have all completed.
74*d57664e9SAndroid Build Coastguard Worker
75*d57664e9SAndroid Build Coastguard Worker##### Examples
76*d57664e9SAndroid Build Coastguard WorkerSpring the stack of bubbles (whose animations are chained) to the bottom of the screen, shrinking them to 50% size. Once the first bubble is done shrinking, begin fading them out, and then remove them all from the parent once all bubbles have faded out:
77*d57664e9SAndroid Build Coastguard Worker
78*d57664e9SAndroid Build Coastguard Worker```
79*d57664e9SAndroid Build Coastguard WorkeranimationForChild(leadBubble)
80*d57664e9SAndroid Build Coastguard Worker    .position(screenCenter, screenBottom)
81*d57664e9SAndroid Build Coastguard Worker    .scaleX(0.5f)
82*d57664e9SAndroid Build Coastguard Worker    .scaleY(0.5f, () -> animationForChild(leadBubble).alpha(0).start(removeAllFromParent))
83*d57664e9SAndroid Build Coastguard Worker    .start();
84*d57664e9SAndroid Build Coastguard Worker```
85*d57664e9SAndroid Build Coastguard Worker
86*d57664e9SAndroid Build Coastguard Worker'Drop in' a child view that was just added to the layout:
87*d57664e9SAndroid Build Coastguard Worker
88*d57664e9SAndroid Build Coastguard Worker```
89*d57664e9SAndroid Build Coastguard WorkeranimationForChild(newView)
90*d57664e9SAndroid Build Coastguard Worker    .scaleX(1.15f /* from */, 1f /* to */)
91*d57664e9SAndroid Build Coastguard Worker    .scaleY(1.15f /* from */, 1f /* to */)
92*d57664e9SAndroid Build Coastguard Worker    .alpha(0f /* from */, 1f /* to */)
93*d57664e9SAndroid Build Coastguard Worker    .position(posX, posY)
94*d57664e9SAndroid Build Coastguard Worker    .start();
95*d57664e9SAndroid Build Coastguard Worker```
96*d57664e9SAndroid Build Coastguard Worker
97*d57664e9SAndroid Build Coastguard WorkerMove every view except for the first to x = (index - 1) * 50, then remove the first view.
98*d57664e9SAndroid Build Coastguard Worker
99*d57664e9SAndroid Build Coastguard Worker```
100*d57664e9SAndroid Build Coastguard WorkeranimationsForChildrenFromIndex(1, (index, anim) -> anim.translationX((index - 1) * 50))
101*d57664e9SAndroid Build Coastguard Worker    .startAll(removeFirstView);
102*d57664e9SAndroid Build Coastguard Worker```
103*d57664e9SAndroid Build Coastguard Worker
104*d57664e9SAndroid Build Coastguard WorkerMove a view up along the left side of the screen, and then to the top right of the screen (assume a view that is currently halfway down the left side of the screen). When the translation animations have finished following the target, call a callback:
105*d57664e9SAndroid Build Coastguard Worker
106*d57664e9SAndroid Build Coastguard Worker```
107*d57664e9SAndroid Build Coastguard WorkerPath path = new Path();
108*d57664e9SAndroid Build Coastguard Workerpath.moveTo(view.getTranslationX(), view.getTranslationY());
109*d57664e9SAndroid Build Coastguard Workerpath.lineTo(view.getTranslationX(), 0);
110*d57664e9SAndroid Build Coastguard Workerpath.lineTo(mScreenWidth, 0);
111*d57664e9SAndroid Build Coastguard WorkeranimationForChild(view)
112*d57664e9SAndroid Build Coastguard Worker    .followAnimatedTargetAlongPath(path, 100, new LinearInterpolator())
113*d57664e9SAndroid Build Coastguard Worker    .start(callbackAfterFollowingFinished);
114*d57664e9SAndroid Build Coastguard Worker```
115*d57664e9SAndroid Build Coastguard Worker
116*d57664e9SAndroid Build Coastguard Worker## PhysicsAnimationLayout
117*d57664e9SAndroid Build Coastguard WorkerThe layout itself is a FrameLayout descendant with a few extra methods:
118*d57664e9SAndroid Build Coastguard Worker
119*d57664e9SAndroid Build Coastguard Worker```setActiveController(PhysicsAnimationController controller)```
120*d57664e9SAndroid Build Coastguard WorkerSets the given controller as the active controller for the layout. This causes the layout to construct or reconfigure the physics animations according to the new controller’s configuration methods, and halt any in-progress animations.
121*d57664e9SAndroid Build Coastguard Worker
122*d57664e9SAndroid Build Coastguard WorkerOnly the currently active controller is allowed to start animations. If a different controller is set as the active controller, the previous controller will no longer be able to start animations. Attempts to do so will have no effect. This is to ensure that multiple controllers aren’t updating animations at the same time, which can cause undefined behavior.