Mais conteúdo relacionado Semelhante a Epoxy 介紹 (20) Epoxy 介紹3. What is Epoxy
• Airbnb 開源專案
• ⽤用來來取代傳統 Adapter
• 可建立複雜的 RecyclerView Adapter
• 內建儲存 view state, ⾃自動 diff
6. When to use
• 有三種⽅方法實作
• ScrollView
• Adapter with view type
• Epoxy Controller
8. Adapter with view type
@Override
public int getItemViewType(int position) {
if (0 == position) {
return TYPE_1;
} else if (1 == position) {
return TYPE_2;
} else {
return TYPE_3;
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
switch (viewType) {
case TYPE_1:
case TYPE_2:
case TYPE_3:
}
}
9. Epoxy Controller
• The EpoxyController encourages usage similar to the popular
Model-View-ViewModel and Model-View-Intent patterns.
• 類似現在的 Model-View-ViewMode 改念念,把建立 RecyclerView
Items 的邏輯放在 Controller 裡⾯面
12. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new AvatarItemViewModel_());
add(new BannerItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
在 EpoxyController 中使⽤用的⽅方式
13. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new AvatarItemViewModel_());
add(new BannerItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
14. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new AvatarItemViewModel_());
add(new BannerItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
15. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new AvatarItemViewModel_());
add(new BannerItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
16. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new AvatarItemViewModel_());
add(new BannerItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
17. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new AvatarItemViewModel_());
add(new BannerItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
18. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new BannerItemViewModel_());
add(new AvatarItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
}
加入順序 == 顯⽰示順序
19. Epoxy Controller
• 所⾒見見即所得
@Override
protected void buildModels() {
add(new BannerItemViewModel_());
add(new AvatarItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new SettingsItemViewModel_());
add(new AdItemViewModel_());
}
新增 Item 非常容易易
20. How to use
• 使⽤用三步驟
• 建立 EpoxyController
• 建立 EpoxyModel
• 設定 RecyclerView 的 Adapter
40. Epoxy Model
• Epoxy uses EpoxyModel objects to know which views to display
and how to bind data to them. This is similar to the popular
ViewModel pattern. You should subclass EpoxyModel to specify
what layout your model uses and how to bind data to that view.
• EpoxyModel 裡設定 View 為何,需顯⽰示的 Data 為何,有點像現在的
ViewModel 概念念
45. View Annotations
• 建立 View
• 設定 Annotation
• 設定 Property (為了了 equals and hashCode)
• 就會⾃自動產⽣生 EpoxyModel 了了!
47.
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
}
48. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
}
49. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
}
指定 Model 要 Inflate 的 View 為何
52. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
}
53. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
@ModelProp
public void setTitle(@StringRes int resId) {
titleTextView.setText(resId);
}
@ModelProp(options = Option.DoNotHash)
public void setClickListener(@Nullable OnClickListener clickListener) {
this.setOnClickListener(clickListener);
}
}
54. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
@ModelProp
public void setTitle(@StringRes int resId) {
titleTextView.setText(resId);
}
@ModelProp(options = Option.DoNotHash)
public void setClickListener(@Nullable OnClickListener clickListener) {
this.setOnClickListener(clickListener);
}
}
設定 Property
讓 View 顯⽰示對應的資料
55. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
@ModelProp
public void setTitle(@StringRes int resId) {
titleTextView.setText(resId);
}
@ModelProp(options = Option.DoNotHash)
public void setClickListener(@Nullable OnClickListener clickListener) {
this.setOnClickListener(clickListener);
}
}
設定 ClickListener
56. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
@ModelProp
public void setTitle(@StringRes int resId) {
titleTextView.setText(resId);
}
@ModelProp(options = Option.DoNotHash)
public void setClickListener(@Nullable OnClickListener clickListener) {
this.setOnClickListener(clickListener);
}
} Interface 沒有 equal and hash
所以使⽤用 Option.DoNotHash 標記
57. @ModelView(defaultLayout = R.layout.model_settings_item_view)
public class SettingsItemView extends LinearLayout {
@BindView(R.id.titleTextView) TextView titleTextView;
public SettingsItemView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.view_settings_item, this);
ButterKnife.bind(this);
}
@ModelProp
public void setTitle(@StringRes int resId) {
titleTextView.setText(resId);
}
@ModelProp(options = Option.DoNotHash)
public void setClickListener(@Nullable OnClickListener clickListener) {
this.setOnClickListener(clickListener);
}
}
60. Using EpoxyModel
public class SimpleController extends EpoxyController {
@Override
protected void buildModels() {
add(new SettingsItemViewModel_()
.id(3)
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
);
}
}
61. Using EpoxyModel
public class SimpleController extends EpoxyController {
@Override
protected void buildModels() {
add(new SettingsItemViewModel_()
.id(3)
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
);
}
}
⼿手動 new ⼀一個 EpoxyModel
62. Using EpoxyModel
public class SimpleController extends EpoxyController {
@Override
protected void buildModels() {
add(new SettingsItemViewModel_()
.id(3)
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
);
}
}
⾃自動產⽣生的 EpoxyModel
63. Using EpoxyModel
public class SimpleController extends EpoxyController {
@Override
protected void buildModels() {
add(new SettingsItemViewModel_()
.id(3)
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
);
}
}
透過 EpoxyController 的 method
加入到 Controller 裡
64. Using EpoxyModel
public class SimpleController extends EpoxyController {
@Override
protected void buildModels() {
add(new SettingsItemViewModel_()
.id(3)
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
);
}
}
若若是⼿手動建立
要設定 Unique ID
65. Using EpoxyModel
public class SimpleController extends EpoxyController {
@Override
protected void buildModels() {
add(new SettingsItemViewModel_()
.id(3)
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
);
}
}
剛剛設定的 Properties
68. Using EpoxyModel
public class SimpleController extends EpoxyController {
@AutoModel SettingsItemViewModel_ settingsItemViewModel1;
@Override
protected void buildModels() {
}
}
使⽤用 Annotation
69. Using EpoxyModel
public class SimpleController extends EpoxyController {
@AutoModel SettingsItemViewModel_ settingsItemViewModel1;
@Override
protected void buildModels() {
settingsItemViewModel1
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
.addTo(this);
}
}
70. Using EpoxyModel
public class SimpleController extends EpoxyController {
@AutoModel SettingsItemViewModel_ settingsItemViewModel1;
@Override
protected void buildModels() {
settingsItemViewModel1
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
.addTo(this);
}
}
透過 Annotation 建立的 EpoxyModel
會⾃自動設定 Unique ID
71. Using EpoxyModel
public class SimpleController extends EpoxyController {
@AutoModel SettingsItemViewModel_ settingsItemViewModel1;
@Override
protected void buildModels() {
settingsItemViewModel1
.title(R.string.settings_1)
.clickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
})
.addTo(this);
}
}
加入到 Controller 裡
74. public class ListController extends TypedEpoxyController<List<Comment>> {
@Override
protected void buildModels(List<Comment> data) {
}
}
75. public class ListController extends TypedEpoxyController<List<Comment>> {
@AutoModel HeaderItemViewModel_ headerItemViewModel;
@AutoModel FooterItemViewModel_ footerItemViewModel;
@Override
protected void buildModels(List<Comment> data) {
headerItemViewModel
.title(R.string.header_1)
.addTo(this);
footerItemViewModel
.title(R.string.footer_1)
.addTo(this);
}
}
76. public class ListController extends TypedEpoxyController<List<Comment>> {
@AutoModel HeaderItemViewModel_ headerItemViewModel;
@AutoModel FooterItemViewModel_ footerItemViewModel;
@Override
protected void buildModels(List<Comment> data) {
headerItemViewModel
.title(R.string.header_1)
.addTo(this);
for (Comment comment : data) {
new CommentItemModel_()
.id(comment.getId())
.comment(comment.getComment())
.clickListener(//...)
})
.addTo(this);
}
footerItemViewModel
.title(R.string.footer_1)
.addTo(this);
}
}
77. Using EpoxyHolder
@EpoxyModelClass(layout = R.layout.model_comment_item_view)
public abstract class CommentItemModel extends EpoxyModelWithHolder<CommentItemHolder> {
@EpoxyAttribute String comment;
@EpoxyAttribute(DoNotHash) OnClickListener clickListener;
@Override
public void bind(CommentItemHolder holder) {
holder.itemView.setOnClickListener(clickListener);
holder.commentTextView.setText(comment);
}
@Override
public void unbind(CommentItemHolder holder) {
holder.itemView.setOnClickListener(null);
}
public static class CommentItemHolder extends BaseEpoxyHolder {
@BindView(R.id.itemView) View itemView;
@BindView(R.id.commentTextView) TextView commentTextView;
}
}
78. Using EpoxyHolder
@EpoxyModelClass(layout = R.layout.model_comment_item_view)
public abstract class CommentItemModel extends EpoxyModelWithHolder<CommentItemHolder> {
@EpoxyAttribute String comment;
@EpoxyAttribute(DoNotHash) OnClickListener clickListener;
@Override
public void bind(CommentItemHolder holder) {
holder.itemView.setOnClickListener(clickListener);
holder.commentTextView.setText(comment);
}
@Override
public void unbind(CommentItemHolder holder) {
holder.itemView.setOnClickListener(null);
}
public static class CommentItemHolder extends BaseEpoxyHolder {
@BindView(R.id.itemView) View itemView;
@BindView(R.id.commentTextView) TextView commentTextView;
}
}
繼承 EpoxyModelWithHolder
繼承 BaseEpoxyHolder
https://github.com/airbnb/epoxy/wiki/Epoxy-Models#view-holders
79. Using EpoxyHolder
@EpoxyModelClass(layout = R.layout.model_comment_item_view)
public abstract class CommentItemModel extends EpoxyModelWithHolder<CommentItemHolder> {
@EpoxyAttribute String comment;
@EpoxyAttribute(DoNotHash) OnClickListener clickListener;
@Override
public void bind(CommentItemHolder holder) {
holder.itemView.setOnClickListener(clickListener);
holder.commentTextView.setText(comment);
}
@Override
public void unbind(CommentItemHolder holder) {
holder.itemView.setOnClickListener(null);
}
public static class CommentItemHolder extends BaseEpoxyHolder {
@BindView(R.id.itemView) View itemView;
@BindView(R.id.commentTextView) TextView commentTextView;
}
}
類似 ViewHolder 的 lifecycle
80. public class ListController extends TypedEpoxyController<List<Comment>> {
@AutoModel HeaderItemViewModel_ headerItemViewModel;
@AutoModel FooterItemViewModel_ footerItemViewModel;
@Override
protected void buildModels(List<Comment> data) {
headerItemViewModel
.title(R.string.header_1)
.addTo(this);
for (Comment comment : data) {
new CommentItemModel_()
.id(comment.getId())
.comment(comment.getComment())
.clickListener(//...)
})
.addTo(this);
}
footerItemViewModel
.title(R.string.footer_1)
.addTo(this);
}
}
81. public class ListController extends TypedEpoxyController<List<Comment>> {
@AutoModel HeaderItemViewModel_ headerItemViewModel;
@AutoModel FooterItemViewModel_ footerItemViewModel;
@Override
protected void buildModels(List<Comment> data) {
headerItemViewModel
.title(R.string.header_1)
.addTo(this);
for (Comment comment : data) {
new CommentItemModel_()
.id(comment.getId())
.comment(comment.getComment())
.clickListener(//...)
})
.addTo(this);
}
loadMoreViewModel
.addTo(true, this);
}
}
82. public class ListController extends TypedEpoxyController<List<Comment>> {
@AutoModel HeaderItemViewModel_ headerItemViewModel;
@AutoModel FooterItemViewModel_ footerItemViewModel;
@Override
protected void buildModels(List<Comment> data) {
headerItemViewModel
.title(R.string.header_1)
.addTo(this);
for (Comment comment : data) {
new CommentItemModel_()
.id(comment.getId())
.comment(comment.getComment())
.clickListener(//...)
})
.addTo(this);
}
loadMoreViewModel
.addTo(true, this);
}
}
Add with condition,
可以設定條件(true/false)
決定是否要加入到 Controller
83. public class ListController extends TypedEpoxyController<List<Comment>> {
@AutoModel HeaderItemViewModel_ headerItemViewModel;
@AutoModel FooterItemViewModel_ footerItemViewModel;
@Override
protected void buildModels(List<Comment> data) {
headerItemViewModel
.title(R.string.header_1)
.addTo(this);
for (Comment comment : data) {
new CommentItemModel_()
.id(comment.getId())
.comment(comment.getComment())
.clickListener(//...)
})
.addTo(this);
}
loadMoreViewModel
.addTo(false, this);
}
}
86. Summary
• 若若 RecyclerView 有兩兩種以上的 view type,可以考慮⽤用 Epoxy
• 想要動態增減 RecyclerView 內容,可以考慮⽤用 Epoxy
• ScrollView 太長,可以考慮⽤用 Epoxy
• 需要 Header, Footer,可以考慮⽤用 Epoxy
• 不想⾃自⼰己實作 diff, save state,可以考慮⽤用 Epoxy
88. Reference
• Epoxy: Airbnb’s View Architecture on Android
https://medium.com/airbnb-engineering/epoxy-airbnbs-view-
architecture-on-android-c3e1af150394
• Epoxy Github Wiki
https://github.com/airbnb/epoxy/wiki