Events for Specs
The framework provides a general-purpose API to connect components through events. Events are declared as a POJO (Plain Old Java Object) with an @Event
annotation. By convention, Event class names are suffixed with Event.
Event declarations may not be inner classes of a LayoutSpec
or MountSpec
. This is by design as specs are supposed to be a private concept and events can be used across multiple components.
@Event
public class ColorChangedEvent {
public int color;
}
In the following sample, it's assumed there is a component called ColorComponent
. To indicate that a ColorComponent
can dispatch a ColorChangedEvent
, the ColorComponentSpec
must be annotated with that information. This is done with the events
parameter of the @MountSpec
and @LayoutSpec
annotations. A component may be annotated to dispatch multiple events.
@LayoutSpec(events = { ColorChangedEvent.class })
class ColorComponentSpec {
...
@OnCreateLayout
static Component onCreateLayout(
Context c,
@Prop int color) {
...
EventHandler handler = ColorComponent.getColorChangedEventHandler(c);
if (handler != null) {
ColorComponent.dispatchColorChangedEvent(
handler,
color);
}
...
}
}
For an event of type FooEvent
, this will auto-generate a matching dispatchFooEvent
method and an event identifier that will used by event callbacks.
The dispatchFooEvent
method takes an EventHandler as the first argument followed by the list of attributes defined in the @Event
class. An EventHandler
is essentially a generic listener interface to connect components through events. The convention is to have an EventHandler
prop for each event exposed by the component.
In the example above, ColorComponent
takes a colorChangedHandler
as prop and dispatches the ColorChangedEvent
to it with the generated dispatchColorChangedEvent()
method.
Callbacksβ
In order to handle events dispatched by other components, you'll need an EventHandler
instance and a matching callback.
EventHandler
instances can be created by using the generated component's corresponding event handler factory method. This method will have the same name as the event callback method.
The event callback can be defined using the @OnEvent
annotation. @OnEvent
takes one argument: the event class. The first parameter of a method annotated with @OnEvent
has to be a ComponentContext that the framework will populate.
For example, the following code shows how a component would define a handler for the ColorChangedEvent
declared above:
@LayoutSpec
class MyComponentSpec {
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop String someColor) {
return Column.create(c)
...
.child(
ColorComponent.create(c)
.color(someColor)
.colorChangedHandler(MyComponent.onColorChanged(c)))
...
.build();
}
@OnEvent(ColorChangedEvent.class)
static void onColorChanged(
ComponentContext c,
@FromEvent int color,
@Prop String someProp) {
Log.d("MyComponent", "Color changed: " + color);
}
}
By using the @Param
annotation on one or more of the parameters of the callback method, dynamic event parameters can be defined. This is useful for defining a callback for a certain type of event (such as onAvatarClicked()
) where there is a need to know which avatar was clicked. The avatar parameter in this case would be passed to the event handler factory method.
@OnEvent
callbacks have access to all component props just like the other spec methods:
@LayoutSpec
class FacePileComponentSpec {
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop Uri[] faces) {
Component.Builder builder = Column.create(c);
for (Uri face : faces) {
builder.child(
FrescoVitoImage2.create(c)
.uri(face)
.clickHandler(FacePileComponent.onFaceClicked(c, face)));
}
return builder.build();
}
@OnEvent(ClickEvent.class)
static void onFaceClicked(
ComponentContext c,
@Param Uri face) {
Log.d("FacePileComponent", "Face clicked: " + face);
}
}