Skip to main content

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);
}
}