Dynamic Props
Normally, when the value of a @Prop
within a ComponentTree
changes, the framework needs to compute layout and mount the Component
again.
However, there is a category of @Props
that do not affect layout, thus when the value of the @Prop
changes, the framework can take a "shortcut": apply the new value to the mounted UI element that represents the Component
right away.
We call such properties "dynamic".
DynamicValue<T>
is the interface that makes it possible.
Common Dynamic Props
The dynamic properties that are available for all Components
are:
- Alpha
- Scale X/Y
- Translation X/Y
- Background Color
- Rotation
- Elevation (from Lollipop and above)
To use this, all you need to do is to create and pass a DynamicValue<T>
object to the corresponding Component.Builder
method.
Normally, you would hold on to this object, and use its set()
method to update the actual value.
In the following sample we have a Component
that renders a yellow square in the middle of the screen.
We also have two regular Android SeekBars
"outside" of the Components
hierarchy that control the alpha and the scale levels of the square.
Notice that:
- On lines 43-44, in
MyActivity.java
, we createDynamicValue
objects - On lines 50-51, in
MyActivity.java
, we supply theDynamicValues
to theMyComponent
(just as regular@Props
). - On lines 14-16, in
MyComponentSpec.java
, we passDynamicValue<Float>
objects toalpha()
,scaleX()
andscaleY()
methods ofComponent.Builder
to control the corresponding properties of theRect
component. - On lines 76, 81, in
MyActivity.java
, we use theDynamicValue
objects to keep the state of theSeekBars
and the value of the properties they control in sync.
Custom Dynamic Props for MountSpecs
You may have your own MountSpec
which has @Props
that also do not affect layout.
It is possible to control the values of those properties in similar way using DynamicValue
interface.
However, in this case you will need to tell framework how to apply the value of the @Prop
to the mounted element.
To show you how to do this, let us consider a MountSpec
that mounts a ClockView
and defines time
property, which it passes to the View in @OnMount
.
Notice that the value of the time
property does not affect layout, it only controls how the ClockView
draws clock hands.
However, every time you want to update it the framework will have to go through LayoutState
and MountState
.
Here is how we can fix this by converting to Dynamic Props and, at the same time, get a more convenient interface to adjust the value.
First thing you need to do is to mark the @Prop
as dynamic - line 15.
Once you have done this, the framework will generate an additional method to the builder of your Component
that takes a DynamicValue
.
At the same time it will keep the version of this method that takes "static" value, if you choose to use this in some situations.
Second thing is to create a @OnBindDynamicValue
method - lines 12-17 in ClockComponentSpec.java
- that should set the value to the mounted content.
This method should always takes 2 arguments - mounted content, and the @Prop
itself. You need to create one such method for every dynamic @Prop
you define.
Then, it is the responsibility of the framework to invoke these methods to keep changes to the DynamicValue
.
Here you find the full implementation of the sample above.
Animating Common Dynamic Props
Dynamic Props values can be used with Android Animators to create custom animations.
In the following example we define a click event that starts an animation. As this is using Android's animation api we can easily set properties like duration and interpolation. We can also register for callbacks using Animator.addListener(..)
.
In this example we have created a simple LayoutSpec
that contains a Text
component that animates when we click it.
The scale of the Text
defined by a DynamicValue
, which will trigger a re-draw when ever it is updated. The DynamicValue
is defined within a @State
so that it can be easily shared between the LayoutSpec
's static functions.
There is an Animator
that updates the scale DynamicValue
. The Animator
is defined as a @State
so the instance can be shared between different on click events. It is also wrapped inside an AtomicReference
which allows us to replace the Animator instance so we don't have to re-use the same Animator
for different animations.
For more examples of creating Animations using Common Dynamic Props, see our Animations Cook Book in the Sample App.