Litho

Litho学习之--列表的实现-1

2018-05-20  本文已影响205人  言者无知_n4c

这篇文章主要讲解一个简单列表的实现,包括如何自定义列表中的每个条目, 利用 RecyclerCollectionComponent 组件以及 Sections 库来创建列表,如何自定义每个组件的属性。

第一个自定义组件

首先我们先来定义列表中的条目,每个条目包含一个主标题和副标题,Litho 的预定义组件中并没有这样的组件,事实上也不应该有这样的组件,需要我们自定义组件,相当于在 Android 系统中 LinearLayout 中的垂直方向摆放两个 TextView 。 Litho 中,编写 Spec 类来声明组件的布局,也就是编写各种不同组件的组合,在 Spec 类上添加 @LayoutSpec 注解,编写一个用 @OnCreateLayout 注解的方法返回需要显示的组件,实际上用到的类是去掉 Spec 后缀的 Component 类,框架会生成代码中真正用到的 Component 类,这里我们的自定义组件叫做 ListItem ,相应地,我们要编写 ListItemSpec 类:

@LayoutSpec
public class ListItemSpec {

  @OnCreateLayout
  static Component onCreateLayout(ComponentContext c) {

    return Column.create(c)
        .paddingDip(ALL, 16)
        .backgroundColor(Color.WHITE)
        .child(
            Text.create(c)
                .text("Hello world")
                .textSizeSp(40))
        .child(
            Text.create(c)
                .text("Litho tutorial")
                .textSizeSp(20))
        .build();
  }

}

解释:

这里的 Text 就是 Hello World 里面见到的 Litho 中的核心组件,这个例子中,我们把 Text 组件作为 Column 的子组件传入,这里的 Column 相当于 Android 中垂直方向的 LinearLayout ,里面设置了 padding 和 backbroundColor 两个属性。

那么如何使用我们刚刚编写的这个组件?

final Component component = ListItem.create(context).build();

注意:这里我们用的是 ListItem,而不是 ListItemSpec。

那么 ListItem 是怎么来的? create() 和 build() 方法在哪里定义的?
在 Hello World 中我们在 gradle 文件中添加入了有关注解处理器的依赖, Litho 的注解处理器会扫描代码,查找 Spec 类,并生成去掉后缀 Spec 的组件类,同时自动填充一些必要的方法。

Litho 还可以实现类似于 LinearLayout 中的 weight 和 FrameLayout 的效果,请参考 Layout

运行APP,效果如下:

First Custom Component

创建列表

这一节我们要使用到 Litho 中的 RecyclerCollectionComponent 组件以及 Sections 库来创建列表。
RecyclerCollectionComponent 用于创建 Litho 滚动的单元,隐藏了直接使用 Android 中 RecyclerView 和 Adapter 交互的复杂性。
Sections API 可以把列表中的条目放到 Section 中,写 GroupSectionSpec 类来声明每个 Section 要渲染的内容和使用的数据。
这里我们要自定义的 Section 叫做 ListSection, 因此需要声明 ListSectionSpec 类,在类上添加 @GroupSectionSpec 注解,定义 onCreateChildren 方法,返回需要渲染的子 Section 们,这里每个子 Section 显示一个 ListItem 组件。

@GroupSectionSpec
public class ListSectionSpec {

  @OnCreateChildren
  static Children onCreateChildren(final SectionContext c) {
    Children.Builder builder = Children.create();

    for (int i = 0; i < 32; i++) {
      builder.child(
          SingleComponentSection.create(c)
              .key(String.valueOf(i))
              .component(ListItem.create(c).build()));
    }
    return builder.build();
  }
}

解释:
SingleComponentSection 是 Litho Section API 提供中的一个核心 Section,定义在 com.facebook.litho.sections.widget 这个包中,只不过这个 Section 负责渲染一个单一的 Component 。ListSectionSpec 描述了一个包含有 32 个子 Section 的 Section ,这 32 个子 Section 中,每个 Section 负责渲染一个 ListItem 组件。
这里定义的是 Section ,并没有定义 Component,那么如何把 Section 显示在屏幕上?
把 Activity 中组件的定义改成下面的代码:

final Component component =
    RecyclerCollectionComponent.create(context)
        .disablePTR(true)
        .section(ListSection.create(new SectionContext(context)).build())
        .build();

注意:这里使用的是 ListSection ,而不是 ListSectionSpec 。

解释:

这里我们用 RecyclerCollectionComponent 这个组件,把刚刚定义的Section 显示在屏幕上。 RecyclerCollectionComponent 接收一个 Section 作为属性,会渲染一个 RecyclerView 显示 Section中的内容。它来管理数据刷新的操作,这里不使用下拉刷新功能,所以 通过设置 .disablePTR(true) 把这个功能关掉。

运行代码,效果如下:

列表实现1

定义组件的属性

上面的列表中,我们所有的列表项都显示重复的内容,现在我们想要列表中的内容是变化的。
这里引入 Litho 中属性的概念,也就是 Prop 。Component 的属性就是 Component Spec 类(这个类是我们编写用于生成对应的 Component 类的)中方法的参数,这些参数上带有 @Prop 注解。

把 ListItemSpec 进行如下修改:

@OnCreateLayout
static Component onCreateLayout(
    ComponentContext c,
    @Prop int color,
    @Prop String title,
    @Prop String subtitle) {

  return Column.create(c)
        .paddingDip(ALL, 16)
        .backgroundColor(color)
        .child(
            Text.create(c)
                .text(title)
                .textSizeSp(40))
        .child(
            Text.create(c)
                .text(subtitle)
                .textSizeSp(20))
        .build();
}

解释:
这里我们添加了三个属性,color,title,subtitle 。这里的 backgroundColor 以及 Text 组件的 文本内容不再是硬编码的形式,而是根据 onCreateLayout 方法中的参数给出。

Litho 的注解处理器会根据 @Prop 注解,为注解的参数生成相应的构造器方法,例如这里参数名称是 color,就会为 ListItem 生成 color(int) 方法,相应地,这里还会生成另外两个构造器方法,title(String) ,subtitle(String) 。在创建 ListItem 组件的时候,就需要在构造器方法中,对属性进行赋值。

修改 ListSection 中的方法:

@OnCreateChildren
static Children onCreateChildren(final SectionContext c) {
  Children.Builder builder = Children.create();

  for (int i = 0; i < 32; i++) {
    builder.child(
        SingleComponentSection.create(c)
            .key(String.valueOf(i))
            .component(ListItem.create(c)
               .color(i % 2 == 0 ? Color.WHITE : Color.LTGRAY)
               .title(i + ". Hello, world!")
               .subtitle("Litho tutorial")
               .build()));
  }
  return builder.build();
}

另外,属性上还可以有其他选项,例如:
@Prop(optional = true, resType = ResType.DIMEN_OFFSET) int shadowRadius,

注解处理器还会生成一些对应的方法 :shadowRadiusPx, shadowRadiusDip, shadowRadiusSp 以及 shadowRadiusRes。

注意:

  1. Prop 可以被 Spec 中不同的生命周期方法访问,只需要在相应的方法参数上添加这个 Prop ,Litho 保证每个方法访问到的属性值是一致的,但是要保证同一个Prop 在不同方法中的声明完全一致,比如 方法1 中 使用 @Prop(optional = true) String prop1,那么 方法2 中也要采用 @Prop(optional = true) String prop1, 否则注解处理器会报错。
  2. 另外对于 optional = true 的属性,在组件创建时可不传入该属性的值,如果未添加此项设置,则会在运行时报如下错误:下面是我把上面代码中的 subtitle一样注释掉报的错:
    java.lang.IllegalStateException: The following props are not marked as optional and were not supplied: [subtitle]
    有关属性的问题,请参考 Props

运行APP,会看到如下效果:


列表实现-带属性
上一篇下一篇

猜你喜欢

热点阅读