Share
Embed
Transcript
88 slides
-
Outline • What is Epoxy • When to use •
How to use • Detail • Future • Summary -
What is Epoxy • Airbnb 開源專案 • ⽤用來來取代傳統 Adapter •
可建立複雜的 RecyclerView Adapter • 內建儲存 view state, ⾃自動 diff -
When to use • 有三種⽅方法實作 • ScrollView • Adapter with
view type • Epoxy Controller -
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: } } -
Epoxy Controller • The EpoxyController encourages usage similar to the
popular Model-View-ViewModel and Model-View-Intent patterns. • 類似現在的 Model-View-ViewMode 改念念,把建立 RecyclerView Items 的邏輯放在 Controller 裡⾯面 -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
AvatarItemViewModel_()); add(new BannerItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } 在 EpoxyController 中使⽤用的⽅方式 -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
AvatarItemViewModel_()); add(new BannerItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
AvatarItemViewModel_()); add(new BannerItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
AvatarItemViewModel_()); add(new BannerItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
AvatarItemViewModel_()); add(new BannerItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
AvatarItemViewModel_()); add(new BannerItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } -
Epoxy Controller • 所⾒見見即所得 @Override protected void buildModels() { add(new
BannerItemViewModel_()); add(new AvatarItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } 加入順序 == 顯⽰示順序 -
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 非常容易易 -
How to use • 使⽤用三步驟 • 建立 EpoxyController • 建立
EpoxyModel • 設定 RecyclerView 的 Adapter -
Detail @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //
... setUpController(); setUpRecyclerView(); } -
Detail @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //
... setUpController(); setUpRecyclerView(); } -
Detail private void setUpController() { // ... controller =
new MyEpoxyController(); // ... } // 繼承 EpoxyController public class MyEpoxyController extends EpoxyController { } -
Detail @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //
... setUpController(); setUpRecyclerView(); } -
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 概念念 -
Create EpoxyModel • 定義 Model • 定義 View • 定義
Property (Data) -
View Annotations • 建立 View • 設定 Annotation • 設定
Property (為了了 equals and hashCode) -
View Annotations • 建立 View • 設定 Annotation • 設定
Property (為了了 equals and hashCode) • 就會⾃自動產⽣生 EpoxyModel 了了! -
SettingsItemViewModel @Override protected void buildModels() { add(new AvatarItemViewModel_()); add(new BannerItemViewModel_());
add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); add(new SettingsItemViewModel_()); } -
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); } } -
@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); } } -
@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 為何 -
@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); } } -
@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); } } -
@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 顯⽰示對應的資料 -
@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 -
@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 標記 -
@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); } } -
Integration • 在 EpoxyController 中設定需要哪些 EpoxyModels • 有兩兩種⽅方式可以建立 EpoxyModel •
⼿手動建立 • ⾃自動建立 (AutoModel Annotation) -
Integration • 在 EpoxyController 中設定需要哪些 EpoxyModels • 有兩兩種⽅方式可以建立 EpoxyModel •
⼿手動建立 • ⾃自動建立 (AutoModel Annotation) -
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) { } }) ); } } -
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 -
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 -
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 裡 -
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 -
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 -
Integration • 在 EpoxyController 中設定需要哪些 EpoxyModels • 有兩兩種⽅方式可以建立 EpoxyModel •
⼿手動建立 • ⾃自動建立 (AutoModel Annotation) -
Using EpoxyModel public class SimpleController extends EpoxyController { @Override
protected void buildModels() { } } -
Using EpoxyModel public class SimpleController extends EpoxyController { @AutoModel SettingsItemViewModel_
settingsItemViewModel1; @Override protected void buildModels() { } } 使⽤用 Annotation -
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); } } -
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 -
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 裡 -
public class ListController extends TypedEpoxyController<List<Comment>> {
@Override protected void buildModels(List<Comment> data) { } } -
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); } } -
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); } } -
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; } } -
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 -
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 -
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); } } -
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); } } -
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 -
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); } } -
Other Features • EpoxyModelGroup • Saving View state • Payloads
• Data binding layouts • Litho Components • Kotlin Support • Grid Suport -
Summary • 若若 RecyclerView 有兩兩種以上的 view type,可以考慮⽤用 Epoxy • 想要動態增減
RecyclerView 內容,可以考慮⽤用 Epoxy • ScrollView 太長,可以考慮⽤用 Epoxy • 需要 Header, Footer,可以考慮⽤用 Epoxy • 不想⾃自⼰己實作 diff, save state,可以考慮⽤用 Epoxy -
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