ç§ãé¢äžããã»ãšãã©ãã¹ãŠã®ãããžã§ã¯ãã§ãèŠçŽ ã®ãªã¹ãïŒãªãã³ïŒã衚瀺ããå¿
èŠããããŸãããããããã®èŠçŽ ã¯ããŸããŸãªã¿ã€ãã§ããã å€ãã®å Žåãã¿ã¹ã¯ã¯ã¡ã€ã³ã¢ããã¿ãŒå
ã§è§£æ±ºãããgetItemViewTypeïŒïŒã®instanceOfãä»ããŠèŠçŽ ã®ã¿ã€ãã決å®ãããŸãã ã¿ã€ã2ãŸãã¯3ã®ããŒãã®å Žåããã®ã¢ãããŒãã¯ããèªäœãæ£åœåããããã«èŠããŸã...ãŸãã¯ããã§ã¯ãããŸãããïŒ ææ¥ãããã«è€éãªããžãã¯ã«åŸã£ãŠãããã«ããã€ãã®åãå°å
¥ããèŠæ±ãæ¥ããã©ããªããŸããïŒ

ãã®èšäºã§ã¯ãDelegateAdapterãã¿ãŒã³ããã®åé¡ãã©ã®ããã«è§£æ±ºãããã瀺ããããšæããŸãã LayoutContainerã䜿çšããKotlinã§ã®å®è£
ãèŠããšããã¿ãŒã³ã«ç²ŸéããŠããããšãèå³æ·±ãå ŽåããããŸãã
åé¡
äŸããå§ããŸãããã 2çš®é¡ã®ããŒã¿ïŒèª¬æä»ãã®ããã¹ããšåçïŒãå«ããªãã³ã衚瀺ããã¿ã¹ã¯ããããšããŸãã
åã®ã¢ãã«ãäœæããŸããpublic interface IViewModel {}
public class TextViewModel implements IViewModel { @NonNull public final String title; @NonNull public final String description; public TextViewModel(@NonNull String title, @NonNull String description) { this.title = title; this.description = description; } }
public class ImageViewModel implements IViewModel { @NonNull public final String title; @NonNull public final @DrawableRes int imageRes; public ImageViewModel(@NonNull String title, @NonNull int imageRes) { this.title = title; this.imageRes = imageRes; } }
å
žåçãªã¢ããã¿ã¯æ¬¡ã®ããã«ãªããŸã public class BadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int TEXT_VIEW_TYPE = 1; private static final int IMAGE_VIEW_TYPE = 2; private List<IViewModel> items; private View.OnClickListener imageClickListener; public BadAdapter(List<IViewModel> items, View.OnClickListener imageClickListener) { this.items = items; this.imageClickListener = imageClickListener; } public int getItemViewType(int position) { IViewModel item = items.get(position); if (item instanceof TextViewModel) return TEXT_VIEW_TYPE; if (item instanceof ImageViewModel) return IMAGE_VIEW_TYPE; throw new IllegalArgumentException( "Can't find view type for position " + position); } @Override public RecyclerView.ViewHolder onCreateViewHolder( ViewGroup parent, int viewType) { RecyclerView.ViewHolder holder; LayoutInflater inflater = LayoutInflater.from(parent.getContext()); if (viewType == TEXT_VIEW_TYPE) { holder = new TextViewHolder( inflater.inflate(R.layout.text_item, parent, false)); } else if (viewType == IMAGE_VIEW_TYPE) { holder = new ImageViewHolder( inflater.inflate(R.layout.image_item, parent, false), imageClickListener); } else { throw new IllegalArgumentException( "Can't create view holder from view type " + viewType); } return holder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int viewType = getItemViewType(position); if (viewType == TEXT_VIEW_TYPE) { TextViewHolder txtViewHolder = (TextViewHolder) holder; TextViewModel model = (TextViewModel) items.get(position); txtViewHolder.tvTitle.setText(model.title); txtViewHolder.tvDescription.setText(model.description); } else if (viewType == IMAGE_VIEW_TYPE) { ImageViewHolder imgViewHolder = (ImageViewHolder) holder; ImageViewModel model = (ImageViewModel) items.get(position); imgViewHolder.tvTitle.setText(model.title); imgViewHolder.imageView.setImageResource(model.imageRes); } else { throw new IllegalArgumentException( "Can't create bind holder fro position " + position); } } @Override public int getItemCount() { return items.size(); } private static class TextViewHolder extends RecyclerView.ViewHolder { private TextView tvTitle; private TextView tvDescription; private TextViewHolder(View parent) { super(parent); tvTitle = parent.findViewById(R.id.tv_title); tvDescription = parent.findViewById(R.id.tv_description); } } private static class ImageViewHolder extends RecyclerView.ViewHolder { private TextView tvTitle; private ImageView imageView; private ImageViewHolder(View parent, View.OnClickListener listener) { super(parent); tvTitle = parent.findViewById(R.id.tv_title); imageView = parent.findViewById(R.id.img_bg); imageView.setOnClickListener(listener); } } }
ãã®å®è£
ã®æ¬ ç¹ã¯ãDRYããã³SOLIDã®ååïŒåäžè²¬ä»»ããã³ãªãŒãã³ã¯ããŒãºïŒã«éåããŠããããšã§ãã ããã確èªããã«ã¯ã2ã€ã®èŠä»¶ãè¿œå ããã ãã§ååã§ããæ°ããããŒã¿åïŒãã§ãã¯ããã¯ã¹ïŒãšããã§ãã¯ããã¯ã¹ãšç»åã®ã¿ãååšããå¥ã®ããŒããå
¥åããŸãã
2çªç®ã®ããŒãã«åãã¢ããã¿ãŒã䜿çšããããæ°ããã¢ããã¿ãŒãäœæããããšããéžæã«çŽé¢ããŠããŸãã éžæãããœãªã¥ãŒã·ã§ã³ã«é¢ä¿ãªããã³ãŒããå€æŽããå¿
èŠããããŸãïŒã»ãŒåãã§ãããå Žæãç°ãªããŸãïŒã æ°ããVIEW_TYPEãæ°ããViewHolderãè¿œå ããã¡ãœããgetItemViewTypeïŒïŒãonCreateViewHolderïŒïŒããã³onBindViewHolderïŒïŒãç·šéããå¿
èŠããããŸãã
1ã€ã®ã¢ããã¿ãŒãæ®ãããšã«ããå Žåãå€æŽã¯çµäºããŸãã ããããå°æ¥ãæ°ããããžãã¯ãæã€æ°ããããŒã¿åã2çªç®ã®ããŒãã«ã®ã¿è¿œå ãããå Žåãæåã®ããŒãã«ã¯è¿œå ã®æ©èœããããå€æŽã¯ãããŠããŸãããããã¹ãããå¿
èŠããããŸãã
æ°ããã¢ããã¿ãŒãäœæããããšã«ããå ŽåãåçŽã«å€§éã®ã³ãŒããéè€ããŸãã
æ¢è£œã®ãœãªã¥ãŒã·ã§ã³
Delegate Adapterãã¿ãŒã³ã¯ãã®åé¡ã«ããŸã察åŠããŸã-ãã§ã«èšè¿°ãããã³ãŒããå€æŽããå¿
èŠã¯ãªããæ¢åã®ã¢ããã¿ãŒãç°¡åã«åå©çšã§ããŸãã
Kotlinã§ã®ãããžã§ã¯ãã®äœæ
ã«é¢ãã JoãoIgnacioã®
äžé£ã®èšäºãèªãã§ãããšãã«ãåããŠãã¿ãŒã³ã«åºäŒããŸããã Juanã®å®è£
ãããã³ããã«ç
§ãããããœãªã¥ãŒã·ã§ã³
-RendererRecyclerViewAdapter -ViewTypeã«ã€ããŠã®ç¥èããã¹ãŠã®ã¢ããã¿ãŒã«åæ£ãããŠãããããç§ã¯å¥œãŸãããããŸããã
詳现ãªèª¬æJoanã®æ±ºå®ã§ã¯ãViewTypeãç»é²ããå¿
èŠããããŸãã
object AdapterConstants { val NEWS = 1 val LOADING = 2 }
ViewTypeã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããã¢ãã«ãäœæããŸãã
class SomeModel : ViewType { override fun getViewType() = AdapterConstants.NEWS }
DelegateAdapter cãå®æ°ã§ç»é²ããŸãã
delegateAdapters.put(AdapterConstants.NEWS, NewsDelegateAdapter(listener))
ãããã£ãŠãããŒã¿åã®ããžãã¯ã¯3ã€ã®ã¯ã©ã¹ïŒå®æ°ãã¢ãã«ãããã³ç»é²ãè¡ãããå ŽæïŒã«åæ£ãããŸãã ããã«ã誀ã£ãŠåãå€ãæã€2ã€ã®å®æ°ãäœæããªãããã«ããå¿
èŠããããŸããããã¯ãRendererRecyclerViewAdapterã䜿çšãããœãªã¥ãŒã·ã§ã³ã§ã¯éåžžã«ç°¡åã§ãã
class SomeModel implements ItemModel { public static final int TYPE = 0;
ãããã®ã¢ãããŒãã¯ã©ã¡ããHans Dorfman
AdapterDelegatesã©ã€ãã©ãªã«åºã¥ããŠããŸããããã¯ç§ãæ°ã«å
¥ã£ãŠããŸãããã¢ããã¿ãŒãäœæããå¿
èŠããããšããæ¬ ç¹ããããŸãã ãã®éšåã¯ããã€ã©ãŒãã¬ãŒããã§ãããçç¥ã§ããŸãã
å¥ã®è§£æ±ºç
ã³ãŒãã¯èšèãããèªåèªèº«ã®ããã«è©±ãã§ãããã 2ã€ã®ããŒã¿åïŒããã¹ããšç»åïŒã§åãããŒããå®è£
ããŠã¿ãŸãããã LayoutContainerã䜿çšããŠKotlinã§å®è£
ãèšè¿°ããŸãïŒä»¥äžã§è©³çŽ°ã«èª¬æããŸãïŒã
ããã¹ãçšã®ã¢ããã¿ãŒãäœæããŸãã
class TxtDelegateAdapter : KDelegateAdapter<TextViewModel>() { override fun onBind(item: TextViewModel, viewHolder: KViewHolder) = with(viewHolder) { tv_title.text = item.title tv_description.text = item.description } override fun isForViewType(items: List<*>, position: Int) = items[position] is TextViewModel override fun getLayoutId(): Int = R.layout.text_item }
åççšã¢ããã¿ãŒïŒ
class ImageDelegateAdapter(private val clickListener: View.OnClickListener) : KDelegateAdapter<ImageViewModel>() { override fun onBind(item: ImageViewModel, viewHolder: KViewHolder) = with(viewHolder) { tv_title.text = item.title img_bg.setOnClickListener(clickListener) img_bg.setImageResource(item.imageRes) } override fun isForViewType(items: List<*>, position: Int) = items[position] is ImageViewModel override fun getLayoutId(): Int = R.layout.image_item }
ã¡ã€ã³ã¢ããã¿ãŒã®äœæå Žæã«ã¢ããã¿ãŒãç»é²ããŸãã
val adapter = CompositeDelegateAdapter.Builder<IViewModel>() .add(ImageDelegateAdapter(onImageClick)) .add(TextDelegateAdapter()) .build() recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = adapter
ã¿ã¹ã¯ã解決ããããã«è¡ãå¿
èŠãããã®ã¯ããã ãã§ãã åŸæ¥ã®å®è£
ãšæ¯èŒããŠã³ãŒããã©ãã ãå°ãªããã«æ³šç®ããŠãã ããã ããã«ããã®ã¢ãããŒãã«ãããæ°ããããŒã¿åãç°¡åã«è¿œå ããDelegateAdapterãçžäºã«çµã¿åãããããšãã§ããŸãã
æ°ããããŒã¿åïŒãã§ãã¯ããã¯ã¹ïŒãè¿œå ããå¿
èŠãããããšãæ³åããŠã¿ãŸãããã äœãããå¿
èŠããããŸããïŒ
ã¢ãã«ãäœæïŒ
class CheckViewModel(val title: String, var isChecked: Boolean): IViewModel
æžã蟌ã¿ã¢ããã¿ãŒïŒ
class CheckDelegateAdapter : KDelegateAdapter<CheckViewModel>() { override fun onBind(item: CheckViewModel, viewHolder: KViewHolder) = with(viewHolder.check_box) { text = item.title isChecked = item.isChecked setOnCheckedChangeListener { _, isChecked -> item.isChecked = isChecked } } override fun onRecycled(viewHolder: KViewHolder) { viewHolder.check_box.setOnCheckedChangeListener(null) } override fun isForViewType(items: List<*>, position: Int) = items[position] is CheckViewModel override fun getLayoutId(): Int = R.layout.check_item }
ã¢ããã¿ãŒã«è¡ãè¿œå ããŸãã
val adapter = CompositeDelegateAdapter.Builder<IViewModel>() .add(ImageDelegateAdapter(onImageClick)) .add(TextDelegateAdapter()) .add(CheckDelegateAdapter()) .build()
ããŒãã®æ°ããããŒã¿åã¯ãã¬ã€ã¢ãŠããViewHolderãããã³ãã€ã³ãã£ã³ã°ããžãã¯ã§ãã ãŸããææ¡ãããã¢ãããŒãã¯ãã¹ãŠåãã¯ã©ã¹ã«å±ããŠãããããæ°ã«å
¥ã£ãŠããŸãã äžéšã®ãããžã§ã¯ãã§ã¯ãViewHoldersãšViewBindersã¯å¥ã
ã®ã¯ã©ã¹ã«é
眮ãããã¬ã€ã¢ãŠãã®èšåŒµã¯ã¡ã€ã³ã¢ããã¿ãŒã§çºçããŸãã ã¿ã¹ã¯ãæ³åããŠã¿ãŠãã ãã-ããŒãã®ããããã®ããŒã¿åã®ãã©ã³ããµã€ãºãå€æŽããã ãã§ãã ViewHolderã«ç§»åãããšãfindViewByIdïŒR.id.descriptionïŒã衚瀺ãããŸãã 説æãã¯ãªãã¯ãããšãIdeaã¯ããã®IDãæã€ãã¥ãŒãæã€35ã®ã¬ã€ã¢ãŠããæäŸããŸãã 次ã«ãã¡ã€ã³ã¢ããã¿ãŒãParentAdapterãonCreateViewHolderã¡ãœããã®é ã«ç§»åããæåŸã«ã40åã®èŠçŽ ã®äžããç®çã®ã¹ã€ãããèŠã€ããå¿
èŠããããŸãã
ãåé¡ãã»ã¯ã·ã§ã³ã«ã¯ãå¥ã®ããŒãã®äœæã«é¢ããèŠä»¶ããããŸããã ããªã²ãŒãã¢ããã¿ã䜿çšãããšãã¿ã¹ã¯ã¯ç°¡åã«ãªããŸããCompositeAdapterãäœæããå¿
èŠãªã¿ã€ãã®DelegateAdapterãç»é²ããã ãã§ãã
val newAdapter = CompositeDelegateAdapter.Builder<IViewModel>() .add(ImageDelegateAdapter(onImageClick)) .add(CheckDelegateAdapter()) .build()
ããªãã¡ ã¢ããã¿ã¯äºãã«ç¬ç«ããŠãããç°¡åã«çµã¿åãããããšãã§ããŸãã å¥ã®å©ç¹ã¯ããã³ãã©ãŒïŒonlickListenerïŒãæž¡ã䟿å©ãã§ãã BadAdapterïŒäžèšã®äŸïŒã§ã¯ããã³ãã©ãŒãã¢ããã¿ãŒã«æž¡ãããã¢ããã¿ãŒã¯æ¢ã«ãããViewHolderã«æž¡ããŸããã ããã«ãããã³ãŒãã®æ¥ç¶æ§ãåäžããŸãã ææ¡ããããœãªã¥ãŒã·ã§ã³ã§ã¯ããã³ãã©ã¯ã³ã³ã¹ãã©ã¯ã¿ãä»ããŠãããããå¿
èŠãšããã¯ã©ã¹ã«ã®ã¿æž¡ãããŸãã
å®è£
åºæ¬å®è£
ïŒKotlinããã³LayoutContainerãªãïŒã«ã¯ã4ã€ã®ã¯ã©ã¹ãå¿
èŠã§ãã
ã€ã³ã¿ãŒãã§ãŒã¹DelegateAdapter public interface IDelegateAdapter<VH extends RecyclerView.ViewHolder, T> { @NonNull RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType); void onBindViewHolder(@NonNull VH holder, @NonNull List<T> items, int position); void onRecycled(VH holder); boolean isForViewType(@NonNull List<?> items, int position); }
ã¡ã€ã³ã¢ããã¿ãŒ public class CompositeDelegateAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int FIRST_VIEW_TYPE = 0; protected final SparseArray<IDelegateAdapter> typeToAdapterMap; protected final @NonNull List<T> data = new ArrayList<>(); protected CompositeDelegateAdapter( @NonNull SparseArray<IDelegateAdapter> typeToAdapterMap) { this.typeToAdapterMap = typeToAdapterMap; } @Override public final int getItemViewType(int position) { for (int i = FIRST_VIEW_TYPE; i < typeToAdapterMap.size(); i++) { final IDelegateAdapter delegate = typeToAdapterMap.valueAt(i);
ã芧ã®ãšãããéæ³ã§ã¯ãªããonBindãonCreateãonRecycledã®åŒã³åºããå§ä»»ããã ãã§ãïŒHans Dorfmanã«ããAdapterDelegatesã®å®è£
ãšåãã§ãïŒã
次ã«ãåºæ¬çãªViewHolderãšDelegateAdpaterãäœæããŠãããå°ããå®åããåé€ããŸãã
Baseviewholder public class BaseViewHolder extends RecyclerView.ViewHolder { private ItemInflateListener listener; public BaseViewHolder(View parent) { super(parent); } public final void setListener(ItemInflateListener listener) { this.listener = listener; } public final void bind(Object item) { listener.inflated(item, itemView); } interface ItemInflateListener { void inflated(Object viewType, View view); } }
Basedelegateadapter public abstract class BaseDelegateAdapter <VH extends BaseViewHolder, T> implements IDelegateAdapter<VH,T> { abstract protected void onBindViewHolder( @NonNull View view, @NonNull T item, @NonNull VH viewHolder); @LayoutRes abstract protected int getLayoutId(); @NonNull abstract protected VH createViewHolder(View parent); @Override public void onRecycled(VH holder) { } @NonNull @Override public final RecyclerView.ViewHolder onCreateViewHolder( @NonNull ViewGroup parent, int viewType) { final View inflatedView = LayoutInflater .from(parent.getContext()) .inflate(getLayoutId(), parent, false); final VH holder = createViewHolder(inflatedView); holder.setListener(new BaseViewHolder.ItemInflateListener() { @Override public void inflated(Object viewType, View view) { onBindViewHolder(view, (T) viewType, holder); } }); return holder; } @Override public final void onBindViewHolder( @NonNull VH holder, @NonNull List<T> items, int position) { ((BaseViewHolder) holder).bind(items.get(position)); } }
ããã§ãã»ãšãã©äžèšã®äŸã®ããã«ãã¢ããã¿ãŒãäœæã§ããããã«ãªããŸãã
TextDelegateAdapterã®äŸ public class TextDelegateAdapter extends BaseDelegateAdapter<TextDelegateAdapter.TextViewHolder, TextViewModel> { @Override protected void onBindViewHolder(@NonNull View view, @NonNull TextViewModel item, @NonNull TextViewHolder viewHolder) { viewHolder.tvTitle.setText(item.title); viewHolder.tvDescription.setText(item.description); } @Override protected int getLayoutId() { return R.layout.text_item; } @Override protected TextViewHolder createViewHolder(View parent) { return new TextViewHolder(parent); } @Override public boolean isForViewType(@NonNull List<?> items, int position) { return items.get(position) instanceof TextViewModel; } final static class TextViewHolder extends BaseViewHolder { private TextView tvTitle; private TextView tvDescription; private TextViewHolder(View parent) { super(parent); tvTitle = parent.findViewById(R.id.tv_title); tvDescription = parent.findViewById(R.id.tv_description); } } }
ViewHoldersãèªåçã«äœæããã«ã¯ïŒKotlinã§ã®ã¿æ©èœããŸãïŒã次ã®3ã€ã®ããšãå®è¡ããŸãã
- åæãã¥ãŒãªã³ã¯ã€ã³ããŒãçšã®æ¥ç¶ãã©ã°ã€ã³
apply plugin: 'kotlin-android-extensions'
- å®éšçãªãªãã·ã§ã³ãèš±å¯ãã
androidExtensions { experimental = true }
- LayoutContainerã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ãã
ããã©ã«ãã§ã¯ããªã³ã¯ã¯ã¢ã¯ãã£ããã£ãšãã©ã°ã¡ã³ãã«å¯ŸããŠã®ã¿ãã£ãã·ã¥ãããŸãã 詳现ã¯ãã¡ã ã
ããã§ãåºæ¬ã¯ã©ã¹ãèšè¿°ã§ããŸãã
abstract class KDelegateAdapter<T> : BaseDelegateAdapter<KDelegateAdapter.KViewHolder, T>() { abstract fun onBind(item: T, viewHolder: KViewHolder) final override fun onBindViewHolder(view: View, item: T, viewHolder: KViewHolder) { onBind(item, viewHolder) } override fun createViewHolder(parent: View?): KViewHolder { return KViewHolder(parent) } class KViewHolder(override val containerView: View?) : BaseViewHolder(containerView), LayoutContainer }
çæ
- viewTypeã決å®ããå¿
èŠããããšãã«ã¢ããã¿ãŒãæ€çŽ¢ããã«ã¯ãå¹³åN / 2ãå¿
èŠã§ããNã¯ç»é²æžã¿ã¢ããã¿ãŒã®æ°ã§ãã ãã®ãããå€æ°ã®ã¢ããã¿ãŒã䜿çšãããšããœãªã¥ãŒã·ã§ã³ã®åäœãå€å°é
ããªããŸãã
- åãViewModelã«ãµãã¹ã¯ã©ã€ããã2ã€ã®ã¢ããã¿ãŒéã§ç«¶åãçºçããå ŽåããããŸãã
- ã¯ã©ã¹ã¯Kotlinã§ã®ã¿ã³ã³ãã¯ãã§ãã
ãããã«
ãã®ã¢ãããŒãã¯ãè€éãªãªã¹ããšåçš®ã®ãªã¹ãã®äž¡æ¹ã§å®èšŒæžã¿ã§ããã¢ããã¿ãŒãèšè¿°ãããšãæåéã10è¡ã®ã³ãŒãã«ãªããŸãããã¢ãŒããã¯ãã£ãŒã«ãããæ¢åã®ã¯ã©ã¹ãå€æŽããã«ããŒããæ¡åŒµããã³è€éåã§ããŸãã
誰ãããœãŒã¹ã³ãŒããå¿
èŠãšããå Žåã®ããã«ã
ãããžã§ã¯ããžã®ãªã³ã¯ãæäŸã
ãŸã ã ãã£ãŒãããã¯ããåŸ
ã¡ããŠãããŸãã