티스토리 뷰
GroupSection Spec's Lifecylce looks like following:
GroupSection Spect has lifecycle in order below:
@OnCreateInitialState
To set an initial value for a state, you have to write a method annotated with @OnCreateInitialState
in your spec.
The first parameter must be of type ComponentContext
. StateValue and @Prop
are allowed. For example,
@LayoutSpec
object CheckboxSpec {
@OnCreateInitialState
fun createInitialState(
c: ComponentContext,
isChecked: StateValue<Boolean>,
@Prop initChecked: Boolean) {
isChecked.set(initChecked);
}
}
@OnCreateService
You can create service class in @OnCreateService
function if you want to have data persistent or REST API instance with LithoView.@OnCreateService
annotated function is called only once when the Section is first created. In the function, you should create and return your service.
@GroupSectionSpec
object ServiceSectionSpec {
...
@OnCreateService
fun onCreateServices(
c: SectionContext,
@Prop config: Configuration,
...): DataLoader {
return DataLoaderFactory.createLoader(config)
}
}
@OnCreateTreeProp
Each TreeProp is declared and created from a method annotated with @OnCreateTreeProp
.
@LayoutSpec
object ParentComponentSpec {
@OnCreateTreeProp
fun onCreatePrefetcher(
c: ComponentContext,
@Prop prefetcher: Prefetcher): Prefetcher {
return prefetcher
}
@OnCreateLayout
fun onCreateLayout(
c: ComponentContext,
@Prop imageUri: Uri): Component {
return ChildComponent.create(c)
.imageUri(imageUri)
.build()
}
}
You can only declare one TreeProp for any one given type. If a child of ParentComponent also defines a TreeProp of type Prefetcher, it will override the value of that TreeProp for all its children (but not for itself).
@OnCreateChildren@OnCreateChildren
annotated function returns a tree of Sections that will have root in this GroupSectionSpec
. The children can be Section instances created from other GroupSectionSpec
classes or from DiffSectionSpec classes.
Let’s have a look at how you would declare a simple list that contains a header followed by a list of Strings. We’ll use a SingleComponentSection for the header and a DataDiffSection for the list of Strings and we’ll combine these in a hierarchy.
@GroupSectionSpec
object FooSectionSpec {
@OnCreateChildren
fun onCreateChildren(
c: SectionContext,
@Prop headerTitle: String,
@Prop data: List<String>): Children {
return Children.create()
.child(
SingleComponentSection.create(c)
.component(
Text.create(c)
.text(headerTitle)
.build()))
.child(
DataDiffSection.create<String>(c)
.data(data)
.renderEventHandler(FooSection.onRender(c)))
.build()
}
@OnEvent(RenderEvent.class)
fun onRender(
c: SectionContext,
@FromEvent model: String): RenderInfo {
return ComponentRenderInfo.create()
.component(
Text.create(c)
.text(model)
.build())
.build()
}
}
@Event(RenderEvent::class)
Sections work best when combined with the rendering optimisations that Litho Components offer. However, the API also provides support for rendering with Views instead of Components. This makes the transition to Sections easier and you can still take advantage of the performance benefits regardless of your product’s UI using traditional Views, Litho Components or a mix of the two.
View support is only offered by DataDiffSection
at the moment. Let’s have another look at the DataDiffSection example to recap how you declare what the framework should render for a certain item.
@GroupSectionSpec
object MyGroupSectionSpec {
@OnCreateChildren
fun onCreateChildren(
c: SectionContext,
@Prop dataModel: List<MyModel>): Children {
return Children.create()
.child(DataDiffSection.create(c)
.data(dataModel)
.renderEventHandler(MyGroupSection.onRenderEvent(c)))
.build()
}
@OnEvent(RenderEvent::class)
fun onRenderEvent(
c: SectionContext,
@FromEvent model: MyModel): RenderInfo {
return ComponentRenderInfo.create()
.component(MyModelItemComponent.create(c).item(model).build())
.build()
}
}
@OnUnbindService@OnUnbindService
provides the callback for you to clean up and undo anything you have done in @OnBindService
. @OnUnBindService
annotated function is called (along with onBindService()) every time the section tree is updated (usually because of a state update).
This function is passed the service created by onCreateService
as the second function parameter. This should be the inverse of onBindService
. Anything set or bound in onBindService
should be undone here.
@GroupSectionSpec
object ServiceSectionSpec {
...
@OnBindService
fun onBindService(c: SectionContext, service: DataLoader, ...) {
service.registerEventLoader(ServiceSection.dataLoaded(c))
}
@OnUnbindService
fun onUnbindService(c: SectionContext, service: DataLoader, ...) {
service.unregisterEventLoader()
}
@OnEvent(YourData.class)
fun dataLoaded(c: SectionContext, @FromEvent data: Data) {
// Update your state with the new data
ServiceSection.updateData(c, data)
}
@UpdateState
fun updateData(connectionData: StateValue<Data>, @Param data: Data) {
connectionData.set(data);
}
}
@OnBindService@OnBindService
is a callback that allows you to define how your service should interact with its section. This bridge can be used to pass the new set of data to the section whenever a fetch is completed.onBindService()
is called (along with onUnbindService()) every time
* the section tree is updated (usually because of a state update).
* This function is passed the service created by onCreateService as the second function parameter.
* In this function you should bind any EventHandler that will make state changes to your service.
@OnDataBound
A method annotated with this annotation will be called when the data changes corresponding to this section’s hierarchy is made available to the SectionTree.Target
.
Data changes could occur due to any number of the following: 1) Insertion 2) Update 3) Delete 4) Move
Availability of these data changes do not mean they are visible on the viewport. To detect visibility, use @OnViewportChanged
.
class SectionSpec {
@OnDataBound
fun OnDataBound(
c: SectionContext,
@Prop prop: YourProp,
@State state: YourState) {
// Handle data changes
}
}
@OnDataRendered
???
@OnViewportChanged
A method annotated with this annotation will be called when there is a change in the visible viewport.
Call SectionTree#viewPortChanged()
or SectionTree#viewPortChangedFromScrolling()
to allow your sections to know that something on the viewport is now different.
class SectionSpec {
@OnViewportChanged
fun onViewportChanged(
c: SectionContext,
firstVisiblePosition: Int,
lastVisiblePosition: Int,
totalCount: Int,
firstFullyVisibleIndex: Int,
lastFullyVisibleIndex: Int,
@Prop prop: YourProp,
@State state: YourState) {
}
}
@OnRefresh
A method annotated with this annotation will be called when the the UI rendered by the section’s hierarchy is requesting for a refresh of the content. You need to handle this annotated function when RecyclerCollectionComponent.disablePTR
is true, which means SwipeRefreshLayout
works and handle here when refresh starts.
Call SectionTree#refresh()
to propagate your refresh request to all the sections in the hierarchy. Then you can handle it in your section like this:
class SectionSpec {
@OnRefresh
fun onRefresh(c: SectionContext, @Prop yourProp, @State yourState) {
// Handle your refresh request
}
}
@OnUpdateState
You can define how a component’s state or states should be updated by declaring methods annotated with @OnUpdateState
in the specs.
You can have as many @OnUpdateState
methods as you need, according to what states you want to update or what parameters your states depend on.
Each call to an @OnUpdateState
method will trigger a new layout calculation for its ComponentTree. For better performance, if there are situations that can trigger an update for multiple states, you should define an @OnUpdateState
method that updates the value for all those states. Bundling them in the same update call reduces the number of new layout calculations and improves performance.
This is what you need to know when writing an @OnUpdateState
method:
- Parameters representing the states must match the name of a parameter annotated with @State and their type must be a StateValue parameterized with the type of the matching @State.
@Param
parameters are allowed. If the value of your state depends on props, you can declare them like this and pass the value of the prop when the update call is triggered.- All other parameters must have a corresponding parameter annotated with
@State
in the other lifecycle methods, and their type must be aStateValue
parameterized with the type of the matching@State
element.
Here’s how you would define a state update method for the checkbox:
@LayoutSpec
object CheckboxSpec {
@OnUpdateState
fun updateCheckboxState(isChecked: StateValue<Boolean> ) {
isChecked.set(!isChecked.get())
}
}
'Android > Litho' 카테고리의 다른 글
6. StateValue (0) | 2019.11.10 |
---|---|
5. HeroItemSpec (0) | 2019.11.10 |
3. Sample Application - HeroesComponentSpec (0) | 2019.11.09 |
2. Sample Application - Intialisation (0) | 2019.11.09 |
1. Litho - Get Started (0) | 2019.11.09 |