接着上篇 简单的列表-1,这一篇主要讲解如何在列表的顶部添加一个水平滚动的列表,充分利用 Litho 和 Section API 的可组合性。
简单的列表-1 中 ListSectionSpec 描述了一个包含有 32 个子 SingleComponentSection 的 GroupSection ,每个 SingleComponentSection 负责渲染一个 ListItem Component,每个 Component 的内容只有显示的数字发生了变化。
这篇中将利用 Litho 中的另外一个核心 Section :DataDiffSection 来渲染列表, 我们把 ListSectionSpec 中的 SingleComponentSection 替换成 DataDiffSection 。
本篇中的例子把上篇例子中的 ListItemSpec 变成了 ListItem2Spec , ListSectionSpec 变成了 ListSection2Spec, ListSectionSpec 与 ListItemSpec 对应,ListSection2Spec 与 ListItem2Spec 对应。
1. 重构列表
首先,把 ListItemSpec 中的三个属性抽取为一个数据模型:
public class DataModel {
public String title;
public String subtitle;
public int color;
}
把 ListItemSpec 按照如下修改:
@LayoutSpec
public class ListItem2Spec {
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop DataModel dataModel) {
return Column.create(c)
.paddingDip(ALL, 16)
.backgroundColor(dataModel.color)
.child(
Text.create(c)
.text(dataModel.title)
.textSizeSp(40))
.child(
Text.create(c)
.text(dataModel.subtitle)
.textSizeSp(20))
.build();
}
}
在 ListSection2Spec 中添加生成列表的方法,这里使用静态方法模拟,实际场景应该是拉取数据:
private static List<DataModel> generateData(int count) {
final List<DataModel> data = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
DataModel model = new DataModel();
model.color = (i % 2 == 0 ? Color.WHITE : Color.LTGRAY);
model.title = i + ". Hello, world!";
model.subtitle = "Litho tutorial";
data.add(model);
}
return data;
}
再写一个方法,接收 DataModel 参数,创建一个 ListItem2 。
@OnEvent(RenderEvent.class)
static RenderInfo onRender(final SectionContext c, @FromEvent DataModel model,@FromEvent int index) {
return ComponentRenderInfo.create()
.component(
ListItem2.create(c)
.dataModel(model)
.build())
.build();
}
接下来把两个方法结合在一起,修改 onCreateChildren 方法:
@OnCreateChildren
static Children onCreateChildren(final SectionContext c) {
return Children.create()
.child(
DataDiffSection.<DataModel>create(c)
.data(generateData(32))
.renderEventHandler(ListSection2.onRender(c)))
.build();
}
那么 @OnEvent 是什么东西?ListSection2.onRender(c) 又是怎么来的?
下面简单解释一下:
- 当一个列表条目需要被渲染的时候,DataDiffSection 会产生一个 RenderEvent。
- 创建 DataDiffSection 的时候,我们传入自定义的 RenderEventHandler ,ListSection2.onRender(c)。
- 这个自定义的 EventHandler 在接收到 RenderEvent 的时候,会调用在 ListSection2Spec 中定义的 onRender 方法。
- 所有的 EventHandler 是由 @OnEvent 注解标注的代码生成的。
运行APP,效果如下:
2. 添加水平滚动列表
还记得我们是如何用RecyclerCollectionComponent来创建列表的么?应为RecyclerCollectionComponent自身也是一个Component,所以我们可以在Section里再创建一个列表,轻松实现嵌套列表。在一个垂直的列表中再嵌套一个垂直的列表是没有意义的,比较常见的是在垂直列表中嵌入一个水平列表。接下来我们就要这么做。
更新 onCreateChildren()中的代码,在DataDiffSection前面加一个SingleComponentSection:
@OnCreateChildren
static Children onCreateChildren(final SectionContext c) {
return Children.create()
.child(
SingleComponentSection.create(c)
.component(
RecyclerCollectionComponent.create(c)
.disablePTR(true)
.recyclerConfiguration(new ListRecyclerConfiguration(LinearLayoutManager.HORIZONTAL, /*reverse layout*/ false, SNAP_TO_CENTER))
.section(
DataDiffSection.<DataModel>create(c)
.data(generateData(32))
.renderEventHandler(ListSection2.onRender(c))
)
.canMeasureRecycler(true)
)
.build()
)
.child(
DataDiffSection.<DataModel>create(c)
.data(generateData(32))
.renderEventHandler(ListSection2.onRender(c))
)
.build();
}
这里我们看到 RecyclerCollectionComponent 的几个新 props:
- recyclerConfiguration 接收一个配置对象用于设置组件的布局以及RecyclerCollectionComponent的对齐方式。
- canMeasureRecycler对于没有固定高度的水平 RecyclerCollectionComponent 来说须设置成true。RecyclerCollectionComponent会测量第一个孩子的高度并将此高度作为整个水平列表的高度。
运行APP,应该会看到如下画面: