SlideShare a Scribd company logo
Epoxy 介紹
黃千碩 (Kros)
oSolve, Ltd./打⼯工趣
Mobile App Developer
Outline
• What is Epoxy
• When to use
• How to use
• Detail
• Future
• Summary
What is Epoxy
• Airbnb 開源專案
• ⽤用來來取代傳統 Adapter
• 可建立複雜的 RecyclerView Adapter
• 內建儲存 view state, ⾃自動 diff
Airbnb 的使⽤用情境
When to use
When to use
• 有三種⽅方法實作
• ScrollView
• Adapter with view type
• Epoxy Controller
ScrollView
• 需在 XML 裡⾯面設定
• Slow
• 如果要動態改變/增減欄欄位,情況複雜
• Animation 困難
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
• 所⾒見見即所得
Epoxy 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
How
EpoxyController
⼿手動建立
How
EpoxyControllerAdapter
⾃自動產⽣生
⼿手動建立
How
RecyclerView EpoxyControllerAdapter
⾃自動產⽣生使⽤用
⼿手動建立
EpoxyModel
EpoxyModel
EpoxyModel
EpoxyModel
View
EpoxyModel 中
指定需顯⽰示的 View
EpoxyModel
EpoxyModel
View
Data
View 可以設定
需顯⽰示的資料
EpoxyModel
EpoxyModel
View
Data Data
Data Data
可設定多個資料
EpoxyModel
EpoxyController
EpoxyModel
EpoxyControllerEpoxyModel
View
Data Data
Data Data
EpoxyModel
EpoxyController
在 EpoxyController 中
建立所需的 EpoxyModel
How
RecyclerView Adapter
⾃自動產⽣生使⽤用
⼿手動建立
EpoxyControl
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();

}
Detail


private void setUpRecyclerView() {
// ...

recyclerView.setAdapter(controller.getAdapter());
// ...

}
Detail
• 如何更更新資料?
Detail


private void updateController(Data data) {
// ...

controller.setData(data);
// ...

}
Detail
• Controller 裡⾯面要做什什麼?
• 加入 EpoxyModel
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)
Create EpoxyModel
• ⼿手動建立
• ⾃自動建立 (透過 Annotation)
Create EpoxyModel
• ⼿手動建立
• ⾃自動建立 (透過 Annotation)
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 為何
R.layout.model_settings_item_view:
R.layout.model_settings_item_view:
<?xml version="1.0" encoding="utf-8"?>

<com.example.epoxydemo.view.SettingsItemView

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height=“wrap_content"/>
在 model_settings_item_view 中指定要使⽤用
SettingsItemView
@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 裡
EpoxyViewHolder
• 如果是單純重複的 list?
EpoxyViewHolder
• 如果是單純重複的 list?
• 可使⽤用 EpoxyViewHolder
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
Future
• Sticky Header, Footer, Decoration
Summary
• 若若 RecyclerView 有兩兩種以上的 view type,可以考慮⽤用 Epoxy
• 想要動態增減 RecyclerView 內容,可以考慮⽤用 Epoxy
• ScrollView 太長,可以考慮⽤用 Epoxy
• 需要 Header, Footer,可以考慮⽤用 Epoxy
• 不想⾃自⼰己實作 diff, save state,可以考慮⽤用 Epoxy
Demo Project
• https://github.com/ch8908/EpoxyDemo
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

More Related Content

What's hot

Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
Arturo Herrero
 
Linux Device Tree
Linux Device TreeLinux Device Tree
Linux Device Tree
艾鍗科技
 
Rootlinux17: An introduction to Xen Project Virtualisation
Rootlinux17:  An introduction to Xen Project VirtualisationRootlinux17:  An introduction to Xen Project Virtualisation
Rootlinux17: An introduction to Xen Project Virtualisation
The Linux Foundation
 
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
Shengyou Fan
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
Antoine Rey
 
Introduction to Rust language programming
Introduction to Rust language programmingIntroduction to Rust language programming
Introduction to Rust language programming
Rodolfo Finochietti
 
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Edureka!
 
Kernel debug log and console on openSUSE
Kernel debug log and console on openSUSEKernel debug log and console on openSUSE
Kernel debug log and console on openSUSE
SUSE Labs Taipei
 
Spring Framework - Spring Security
Spring Framework - Spring SecuritySpring Framework - Spring Security
Spring Framework - Spring Security
Dzmitry Naskou
 
Workshop spring session 2 - La persistance au sein des applications Java
Workshop spring   session 2 - La persistance au sein des applications JavaWorkshop spring   session 2 - La persistance au sein des applications Java
Workshop spring session 2 - La persistance au sein des applications JavaAntoine Rey
 
Spring - Part 2 - Autowiring, Annotations, Java based Configuration - slides
Spring - Part 2 - Autowiring, Annotations, Java based Configuration - slidesSpring - Part 2 - Autowiring, Annotations, Java based Configuration - slides
Spring - Part 2 - Autowiring, Annotations, Java based Configuration - slides
Hitesh-Java
 
Unirest Java Tutorial | Java Http Client
Unirest Java Tutorial | Java Http ClientUnirest Java Tutorial | Java Http Client
Unirest Java Tutorial | Java Http Client
rahul patel
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
beom kyun choi
 
"Learning AOSP" - Android Hardware Abstraction Layer (HAL)
"Learning AOSP" - Android Hardware Abstraction Layer (HAL)"Learning AOSP" - Android Hardware Abstraction Layer (HAL)
"Learning AOSP" - Android Hardware Abstraction Layer (HAL)
Nanik Tolaram
 
Sockets and Socket-Buffer
Sockets and Socket-BufferSockets and Socket-Buffer
Sockets and Socket-Buffer
Sourav Punoriyar
 
Using Xcore with Xtext
Using Xcore with XtextUsing Xcore with Xtext
Using Xcore with Xtext
Holger Schill
 
Nestjs MasterClass Slides
Nestjs MasterClass SlidesNestjs MasterClass Slides
Nestjs MasterClass Slides
Nir Kaufman
 
Safety-Certifying Open Source Software: The Case of the Xen Hypervisor
Safety-Certifying Open Source Software: The Case of the Xen HypervisorSafety-Certifying Open Source Software: The Case of the Xen Hypervisor
Safety-Certifying Open Source Software: The Case of the Xen Hypervisor
Stefano Stabellini
 
syzkaller: the next gen kernel fuzzer
syzkaller: the next gen kernel fuzzersyzkaller: the next gen kernel fuzzer
syzkaller: the next gen kernel fuzzer
Dmitry Vyukov
 
코틀린 멀티플랫폼, 미지와의 조우
코틀린 멀티플랫폼, 미지와의 조우코틀린 멀티플랫폼, 미지와의 조우
코틀린 멀티플랫폼, 미지와의 조우
Arawn Park
 

What's hot (20)

Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
Linux Device Tree
Linux Device TreeLinux Device Tree
Linux Device Tree
 
Rootlinux17: An introduction to Xen Project Virtualisation
Rootlinux17:  An introduction to Xen Project VirtualisationRootlinux17:  An introduction to Xen Project Virtualisation
Rootlinux17: An introduction to Xen Project Virtualisation
 
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
[JCConf 2023] 從 Kotlin Multiplatform 到 Compose Multiplatform:在多平台間輕鬆共用業務邏輯與 U...
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
 
Introduction to Rust language programming
Introduction to Rust language programmingIntroduction to Rust language programming
Introduction to Rust language programming
 
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
Angular Directives | Angular 2 Custom Directives | Angular Tutorial | Angular...
 
Kernel debug log and console on openSUSE
Kernel debug log and console on openSUSEKernel debug log and console on openSUSE
Kernel debug log and console on openSUSE
 
Spring Framework - Spring Security
Spring Framework - Spring SecuritySpring Framework - Spring Security
Spring Framework - Spring Security
 
Workshop spring session 2 - La persistance au sein des applications Java
Workshop spring   session 2 - La persistance au sein des applications JavaWorkshop spring   session 2 - La persistance au sein des applications Java
Workshop spring session 2 - La persistance au sein des applications Java
 
Spring - Part 2 - Autowiring, Annotations, Java based Configuration - slides
Spring - Part 2 - Autowiring, Annotations, Java based Configuration - slidesSpring - Part 2 - Autowiring, Annotations, Java based Configuration - slides
Spring - Part 2 - Autowiring, Annotations, Java based Configuration - slides
 
Unirest Java Tutorial | Java Http Client
Unirest Java Tutorial | Java Http ClientUnirest Java Tutorial | Java Http Client
Unirest Java Tutorial | Java Http Client
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
"Learning AOSP" - Android Hardware Abstraction Layer (HAL)
"Learning AOSP" - Android Hardware Abstraction Layer (HAL)"Learning AOSP" - Android Hardware Abstraction Layer (HAL)
"Learning AOSP" - Android Hardware Abstraction Layer (HAL)
 
Sockets and Socket-Buffer
Sockets and Socket-BufferSockets and Socket-Buffer
Sockets and Socket-Buffer
 
Using Xcore with Xtext
Using Xcore with XtextUsing Xcore with Xtext
Using Xcore with Xtext
 
Nestjs MasterClass Slides
Nestjs MasterClass SlidesNestjs MasterClass Slides
Nestjs MasterClass Slides
 
Safety-Certifying Open Source Software: The Case of the Xen Hypervisor
Safety-Certifying Open Source Software: The Case of the Xen HypervisorSafety-Certifying Open Source Software: The Case of the Xen Hypervisor
Safety-Certifying Open Source Software: The Case of the Xen Hypervisor
 
syzkaller: the next gen kernel fuzzer
syzkaller: the next gen kernel fuzzersyzkaller: the next gen kernel fuzzer
syzkaller: the next gen kernel fuzzer
 
코틀린 멀티플랫폼, 미지와의 조우
코틀린 멀티플랫폼, 미지와의 조우코틀린 멀티플랫폼, 미지와의 조우
코틀린 멀티플랫폼, 미지와의 조우
 

Similar to Epoxy 介紹

Advanced #6 clean architecture
Advanced #6  clean architectureAdvanced #6  clean architecture
Advanced #6 clean architecture
Vitali Pekelis
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
AgileThought
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
Ernesto Hernández Rodríguez
 
React Native & NativeScript
React Native & NativeScriptReact Native & NativeScript
React Native & NativeScript
ElifTech
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
Hassan Abid
 
Simple React Todo List
Simple React Todo ListSimple React Todo List
Simple React Todo List
Ritesh Chaudhari
 
[Final] ReactJS presentation
[Final] ReactJS presentation[Final] ReactJS presentation
[Final] ReactJS presentation
洪 鹏发
 
Rules SDK IBM WW BPM Forum March 2013
Rules SDK IBM WW BPM Forum March 2013Rules SDK IBM WW BPM Forum March 2013
Rules SDK IBM WW BPM Forum March 2013
Dan Selman
 
MVC 1.0 als alternative Webtechnologie
MVC 1.0 als alternative WebtechnologieMVC 1.0 als alternative Webtechnologie
MVC 1.0 als alternative Webtechnologie
OPEN KNOWLEDGE GmbH
 
Modern android development
Modern android developmentModern android development
Modern android development
Khiem-Kim Ho Xuan
 
A Minimalist’s Attempt at Building a Distributed Application
A Minimalist’s Attempt at Building a Distributed ApplicationA Minimalist’s Attempt at Building a Distributed Application
A Minimalist’s Attempt at Building a Distributed Application
David Hoerster
 
2-0. Spring ecosytem.pdf
2-0. Spring ecosytem.pdf2-0. Spring ecosytem.pdf
2-0. Spring ecosytem.pdf
DeoDuaNaoHet
 
Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5
Tuna Tore
 
springmvc-150923124312-lva1-app6892
springmvc-150923124312-lva1-app6892springmvc-150923124312-lva1-app6892
springmvc-150923124312-lva1-app6892
Tuna Tore
 
Effective Java. By materials of Josch Bloch's book
Effective Java. By materials of Josch Bloch's bookEffective Java. By materials of Josch Bloch's book
Effective Java. By materials of Josch Bloch's book
Roman Tsypuk
 
Angular.js Primer in Aalto University
Angular.js Primer in Aalto UniversityAngular.js Primer in Aalto University
Angular.js Primer in Aalto University
SC5.io
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
[T]echdencias
 
Say bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & KotlinSay bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & Kotlin
Miquel Beltran Febrer
 
Jetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO ExtendedJetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO Extended
Toru Wonyoung Choi
 
Android development
Android developmentAndroid development
Android development
Gregoire BARRET
 

Similar to Epoxy 介紹 (20)

Advanced #6 clean architecture
Advanced #6  clean architectureAdvanced #6  clean architecture
Advanced #6 clean architecture
 
Patterns Are Good For Managers
Patterns Are Good For ManagersPatterns Are Good For Managers
Patterns Are Good For Managers
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
 
React Native & NativeScript
React Native & NativeScriptReact Native & NativeScript
React Native & NativeScript
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
 
Simple React Todo List
Simple React Todo ListSimple React Todo List
Simple React Todo List
 
[Final] ReactJS presentation
[Final] ReactJS presentation[Final] ReactJS presentation
[Final] ReactJS presentation
 
Rules SDK IBM WW BPM Forum March 2013
Rules SDK IBM WW BPM Forum March 2013Rules SDK IBM WW BPM Forum March 2013
Rules SDK IBM WW BPM Forum March 2013
 
MVC 1.0 als alternative Webtechnologie
MVC 1.0 als alternative WebtechnologieMVC 1.0 als alternative Webtechnologie
MVC 1.0 als alternative Webtechnologie
 
Modern android development
Modern android developmentModern android development
Modern android development
 
A Minimalist’s Attempt at Building a Distributed Application
A Minimalist’s Attempt at Building a Distributed ApplicationA Minimalist’s Attempt at Building a Distributed Application
A Minimalist’s Attempt at Building a Distributed Application
 
2-0. Spring ecosytem.pdf
2-0. Spring ecosytem.pdf2-0. Spring ecosytem.pdf
2-0. Spring ecosytem.pdf
 
Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5Java Spring MVC Framework with AngularJS by Google and HTML5
Java Spring MVC Framework with AngularJS by Google and HTML5
 
springmvc-150923124312-lva1-app6892
springmvc-150923124312-lva1-app6892springmvc-150923124312-lva1-app6892
springmvc-150923124312-lva1-app6892
 
Effective Java. By materials of Josch Bloch's book
Effective Java. By materials of Josch Bloch's bookEffective Java. By materials of Josch Bloch's book
Effective Java. By materials of Josch Bloch's book
 
Angular.js Primer in Aalto University
Angular.js Primer in Aalto UniversityAngular.js Primer in Aalto University
Angular.js Primer in Aalto University
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
 
Say bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & KotlinSay bye to Fragments with Conductor & Kotlin
Say bye to Fragments with Conductor & Kotlin
 
Jetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO ExtendedJetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO Extended
 
Android development
Android developmentAndroid development
Android development
 

More from Kros Huang

Kotlin Receiver Types 介紹
Kotlin Receiver Types 介紹Kotlin Receiver Types 介紹
Kotlin Receiver Types 介紹
Kros Huang
 
Android MvRx Framework 介紹
Android MvRx Framework 介紹Android MvRx Framework 介紹
Android MvRx Framework 介紹
Kros Huang
 
Kotlin Data Model
Kotlin Data ModelKotlin Data Model
Kotlin Data Model
Kros Huang
 
Fastlane on Android 介紹
Fastlane on Android 介紹Fastlane on Android 介紹
Fastlane on Android 介紹
Kros Huang
 
RxJava 2.0 介紹
RxJava 2.0 介紹RxJava 2.0 介紹
RxJava 2.0 介紹
Kros Huang
 
Rxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJavaRxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJava
Kros Huang
 
Android with dagger_2
Android with dagger_2Android with dagger_2
Android with dagger_2
Kros Huang
 

More from Kros Huang (7)

Kotlin Receiver Types 介紹
Kotlin Receiver Types 介紹Kotlin Receiver Types 介紹
Kotlin Receiver Types 介紹
 
Android MvRx Framework 介紹
Android MvRx Framework 介紹Android MvRx Framework 介紹
Android MvRx Framework 介紹
 
Kotlin Data Model
Kotlin Data ModelKotlin Data Model
Kotlin Data Model
 
Fastlane on Android 介紹
Fastlane on Android 介紹Fastlane on Android 介紹
Fastlane on Android 介紹
 
RxJava 2.0 介紹
RxJava 2.0 介紹RxJava 2.0 介紹
RxJava 2.0 介紹
 
Rxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJavaRxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJava
 
Android with dagger_2
Android with dagger_2Android with dagger_2
Android with dagger_2
 

Recently uploaded

ITSM Integration with MuleSoft.pptx
ITSM  Integration with MuleSoft.pptxITSM  Integration with MuleSoft.pptx
ITSM Integration with MuleSoft.pptx
VANDANAMOHANGOUDA
 
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student MemberIEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
VICTOR MAESTRE RAMIREZ
 
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by AnantLLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
Anant Corporation
 
Applications of artificial Intelligence in Mechanical Engineering.pdf
Applications of artificial Intelligence in Mechanical Engineering.pdfApplications of artificial Intelligence in Mechanical Engineering.pdf
Applications of artificial Intelligence in Mechanical Engineering.pdf
Atif Razi
 
Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...
bijceesjournal
 
Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...
IJECEIAES
 
Embedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoringEmbedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoring
IJECEIAES
 
Null Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAMNull Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAM
Divyanshu
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
Yasser Mahgoub
 
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
Gino153088
 
Certificates - Mahmoud Mohamed Moursi Ahmed
Certificates - Mahmoud Mohamed Moursi AhmedCertificates - Mahmoud Mohamed Moursi Ahmed
Certificates - Mahmoud Mohamed Moursi Ahmed
Mahmoud Morsy
 
Data Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason WebinarData Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason Webinar
UReason
 
CompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURS
CompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURSCompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURS
CompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURS
RamonNovais6
 
People as resource Grade IX.pdf minimala
People as resource Grade IX.pdf minimalaPeople as resource Grade IX.pdf minimala
People as resource Grade IX.pdf minimala
riddhimaagrawal986
 
132/33KV substation case study Presentation
132/33KV substation case study Presentation132/33KV substation case study Presentation
132/33KV substation case study Presentation
kandramariana6
 
Seminar on Distillation study-mafia.pptx
Seminar on Distillation study-mafia.pptxSeminar on Distillation study-mafia.pptx
Seminar on Distillation study-mafia.pptx
Madan Karki
 
原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样
原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样
原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样
ydzowc
 
Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...
Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...
Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...
IJECEIAES
 
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
171ticu
 
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
ecqow
 

Recently uploaded (20)

ITSM Integration with MuleSoft.pptx
ITSM  Integration with MuleSoft.pptxITSM  Integration with MuleSoft.pptx
ITSM Integration with MuleSoft.pptx
 
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student MemberIEEE Aerospace and Electronic Systems Society as a Graduate Student Member
IEEE Aerospace and Electronic Systems Society as a Graduate Student Member
 
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by AnantLLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
LLM Fine Tuning with QLoRA Cassandra Lunch 4, presented by Anant
 
Applications of artificial Intelligence in Mechanical Engineering.pdf
Applications of artificial Intelligence in Mechanical Engineering.pdfApplications of artificial Intelligence in Mechanical Engineering.pdf
Applications of artificial Intelligence in Mechanical Engineering.pdf
 
Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...
 
Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...Advanced control scheme of doubly fed induction generator for wind turbine us...
Advanced control scheme of doubly fed induction generator for wind turbine us...
 
Embedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoringEmbedded machine learning-based road conditions and driving behavior monitoring
Embedded machine learning-based road conditions and driving behavior monitoring
 
Null Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAMNull Bangalore | Pentesters Approach to AWS IAM
Null Bangalore | Pentesters Approach to AWS IAM
 
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
2008 BUILDING CONSTRUCTION Illustrated - Ching Chapter 02 The Building.pdf
 
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
4. Mosca vol I -Fisica-Tipler-5ta-Edicion-Vol-1.pdf
 
Certificates - Mahmoud Mohamed Moursi Ahmed
Certificates - Mahmoud Mohamed Moursi AhmedCertificates - Mahmoud Mohamed Moursi Ahmed
Certificates - Mahmoud Mohamed Moursi Ahmed
 
Data Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason WebinarData Driven Maintenance | UReason Webinar
Data Driven Maintenance | UReason Webinar
 
CompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURS
CompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURSCompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURS
CompEx~Manual~1210 (2).pdf COMPEX GAS AND VAPOURS
 
People as resource Grade IX.pdf minimala
People as resource Grade IX.pdf minimalaPeople as resource Grade IX.pdf minimala
People as resource Grade IX.pdf minimala
 
132/33KV substation case study Presentation
132/33KV substation case study Presentation132/33KV substation case study Presentation
132/33KV substation case study Presentation
 
Seminar on Distillation study-mafia.pptx
Seminar on Distillation study-mafia.pptxSeminar on Distillation study-mafia.pptx
Seminar on Distillation study-mafia.pptx
 
原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样
原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样
原版制作(Humboldt毕业证书)柏林大学毕业证学位证一模一样
 
Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...
Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...
Redefining brain tumor segmentation: a cutting-edge convolutional neural netw...
 
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样学校原版美国波士顿大学毕业证学历学位证书原版一模一样
学校原版美国波士顿大学毕业证学历学位证书原版一模一样
 
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
一比一原版(CalArts毕业证)加利福尼亚艺术学院毕业证如何办理
 

Epoxy 介紹

  • 1. Epoxy 介紹 黃千碩 (Kros) oSolve, Ltd./打⼯工趣 Mobile App Developer
  • 2. Outline • What is Epoxy • When to use • How to use • Detail • Future • Summary
  • 3. What is Epoxy • Airbnb 開源專案 • ⽤用來來取代傳統 Adapter • 可建立複雜的 RecyclerView Adapter • 內建儲存 view state, ⾃自動 diff
  • 6. When to use • 有三種⽅方法實作 • ScrollView • Adapter with view type • Epoxy Controller
  • 7. ScrollView • 需在 XML 裡⾯面設定 • Slow • 如果要動態改變/增減欄欄位,情況複雜 • Animation 困難
  • 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
  • 32. Detail @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 // ...
 setUpController();
 setUpRecyclerView();
 }
  • 33. Detail @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 // ...
 setUpController();
 setUpRecyclerView();
 }
  • 34. Detail 
 private void setUpController() { // ...
 controller = new MyEpoxyController(); // ... } // 繼承 EpoxyController public class MyEpoxyController extends EpoxyController { }
  • 35. Detail @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 // ...
 setUpController();
 setUpRecyclerView();
 }
  • 36. Detail 
 private void setUpRecyclerView() { // ...
 recyclerView.setAdapter(controller.getAdapter()); // ...
 }
  • 38. Detail 
 private void updateController(Data data) { // ...
 controller.setData(data); // ...
 }
  • 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 概念念
  • 41. Create EpoxyModel • 定義 Model • 定義 View • 定義 Property (Data)
  • 42. Create EpoxyModel • ⼿手動建立 • ⾃自動建立 (透過 Annotation)
  • 43. Create EpoxyModel • ⼿手動建立 • ⾃自動建立 (透過 Annotation)
  • 44. View Annotations • 建立 View • 設定 Annotation • 設定 Property (為了了 equals and hashCode)
  • 45. View Annotations • 建立 View • 設定 Annotation • 設定 Property (為了了 equals and hashCode) • 就會⾃自動產⽣生 EpoxyModel 了了!
  • 46. SettingsItemViewModel @Override
 protected void buildModels() {
 add(new AvatarItemViewModel_());
 add(new BannerItemViewModel_());
 add(new SettingsItemViewModel_());
 add(new SettingsItemViewModel_());
 add(new SettingsItemViewModel_()); }
  • 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);
 }
 }
  • 58. Integration • 在 EpoxyController 中設定需要哪些 EpoxyModels • 有兩兩種⽅方式可以建立 EpoxyModel • ⼿手動建立 • ⾃自動建立 (AutoModel Annotation)
  • 59. Integration • 在 EpoxyController 中設定需要哪些 EpoxyModels • 有兩兩種⽅方式可以建立 EpoxyModel • ⼿手動建立 • ⾃自動建立 (AutoModel Annotation)
  • 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
  • 66. Integration • 在 EpoxyController 中設定需要哪些 EpoxyModels • 有兩兩種⽅方式可以建立 EpoxyModel • ⼿手動建立 • ⾃自動建立 (AutoModel Annotation)
  • 67. Using EpoxyModel public class SimpleController extends EpoxyController { 
 @Override
 protected void buildModels() {
 
 }
 }
  • 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);
 }
 }
  • 84. Other Features • EpoxyModelGroup • Saving View state • Payloads • Data binding layouts • Litho Components • Kotlin Support • Grid Suport
  • 85. Future • Sticky Header, Footer, Decoration
  • 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