Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
избавиться от if-else уродства
Но это потребует расширения всех классов данных определенным интерфейсомНе обязательно. Можно обернуть объект с данными в объект реализующий этот интерфейс. При такой реализации можно избавиться от приведения элементов к нужному типу. Да и конечная реализация получится гибче.
Спасибо за статью.
Подход выглядит неплохо.
Смутило, что метод
abstract RecyclerView.ViewHolder holder(ViewGroup parent);создает новый холдер, а из названия это совсем неясно. Лучше
abstract RecyclerView.ViewHolder createHolder(Context ctx);Еще я бы избавился от неявных зависимостей и вынес весь класс CellType наружу.
А еще вы используете в качестве ViewType id соответствующего лейаута, но тогда теоретически для каждой сборки приложения id будет отличаться. В некоторых случаях это может быть проблемой.
public class CleanAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final AdapterTypesMap typesMap = new AdapterTypesMap();
private List itemList; // TODO
public CleanAdapter() {
// Здесь компилятор следит чтоб ViewHolder соответствовал классу данных. Т.е. передача AdVo.class, ProgressViewHolder.class вызовет ошибку компиляции
typesMap.putItem(R.layout.cell_progress, R.layout.cell_progress, ProgressVo.class, ProgressViewHolder.class);
typesMap.putItem(R.layout.cell_ad, R.layout.cell_ad, AdVo.class, AdViewHolder.class);
typesMap.putItem(R.layout.cell_user, R.layout.cell_user, UserVo.class, UserViewHolder.class);
}
@Override
public int getItemCount() {
return itemList.size();
}
@Override
public int getItemViewType(int position) {
return typesMap.getItemViewType(itemList.get(position));
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return typesMap.createViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
typesMap.bindViewHolder(holder, itemList.get(position));
}
}
public class AdapterTypesMap {
private final Map<Integer /* view type */, AdapterItemType> viewTypesMap = new HashMap<>();
private final Map<Class /* adapter list item class */, AdapterItemType> listItemTypesMap = new HashMap<>();
public <T, V extends ViewHolderBase<T>> void putItem(int viewType, int resourceId, Class<T> dataItemClass, Class<V> viewHolderClass) {
AdapterItemType adapterItemType = new AdapterItemType(viewType, resourceId, viewHolderClass);
listItemTypesMap.put(dataItemClass, adapterItemType);
viewTypesMap.put(viewType, adapterItemType);
}
public int getItemViewType(Object obj) {
return listItemTypesMap.get(obj).getViewType();
}
public RecyclerView.ViewHolder createViewHolder(ViewGroup parent, int viewType) {
return viewTypesMap.get(viewType).createViewHolder(parent);
}
public void bindViewHolder(RecyclerView.ViewHolder holder, Object obj) {
listItemTypesMap.get(obj).bind(holder, obj);
}
}
public interface IAdapterItemType {
int getViewType();
RecyclerView.ViewHolder createViewHolder(ViewGroup parent);
void bind(RecyclerView.ViewHolder holder, Object item);
}
public class AdapterItemType<T extends ViewHolderBase> implements IAdapterItemType {
private final Class<T> viewHolderClass;
private final int viewType;
private final int resourceId;
public AdapterItemType(int viewType, int resourceId, Class<T> viewHolderClass) {
this.viewHolderClass = viewHolderClass;
this.viewType = viewType;
this.resourceId = resourceId;
}
@Override
public int getViewType() {
return viewType;
}
@Override
public ViewHolderBase createViewHolder(ViewGroup parent) {
try {
View view = LayoutInflater.from(parent.getContext()).inflate(resourceId, parent, false);
// здесь не обойтись без рефлексии
return viewHolderClass.getConstructor(View.class).newInstance(view);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public void bind(RecyclerView.ViewHolder holder, Object item) {
ViewHolderBase holder2 = (ViewHolderBase) holder;
holder2.bind(item);
}
}
public abstract class ViewHolderBase<T> extends RecyclerView.ViewHolder {
public ViewHolderBase(View itemView) {
super(itemView);
}
public abstract void bind(T item);
}
public class UserViewHolder extends ViewHolderBase<UserVo> {
public UserViewHolder(View itemView) {
super(itemView);
}
@Override
public void bind(UserVo item) {
// TODO
}
}
ООП-подход представлен в статье Writing Better Adapters
Про Adapter Delegates автор не слышал? По-моему, второй вариант смотрится тоже не особо красиво.
По-моему, второй вариант смотрится тоже не особо красиво
public class UsersArbitraryCellAdapter extends ArbitraryCellAdapter {
public UsersArbitraryCellAdapter() {
this.arbitraryCellSelector.addCell(new ProgressArbitraryCell());
this.arbitraryCellSelector.addCell(new AdArbitraryCell());
this.arbitraryCellSelector.addCell(new UserArbitraryCell());
}
public void setUsers(List<UserVo> userList) {
// Set users, ads, progress...
}
}
public abstract class ArbitraryCellAdapter
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected ArbitraryCellSelector arbitraryCellSelector = new ArbitraryCellSelector();
protected List itemList = new ArrayList();
@Override
public final int getItemViewType(int position) {
return arbitraryCellSelector.getCell(itemList.get(position)).type();
}
@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return arbitraryCellSelector.getCell(viewType).holder(parent);
}
@Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Object item = itemList.get(position);
arbitraryCellSelector.getCell(item).bind(holder, item);
}
@Override
public final int getItemCount() {
return itemList.size();
}
}
public abstract class ArbitraryCellHolder<T> extends RecyclerView.ViewHolder {
public ArbitraryCellHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public abstract void bind(T item);
}
public final class ArbitraryCellSelector {
private List<Cell> cellList = new ArrayList<>();
public void addCell(Cell cell) {
cellList.add(cell);
}
public void removeCell(Cell cell) {
cellList.remove(cell);
}
public Cell getCell(Object item) {
for (Cell cell : cellList) {
if (cell.is(item)) {
return cell;
}
}
throw new NoSuchRecyclerRowException();
}
public Cell getCell(int viewType) {
for (Cell cell : cellList) {
if (cell.type() == viewType) {
return cell;
}
}
throw new NoSuchRecyclerRowException();
}
public interface Cell {
boolean is(Object item);
int type();
RecyclerView.ViewHolder holder(ViewGroup parent);
void bind(RecyclerView.ViewHolder holder, Object item);
}
}
public class AdArbitraryCell implements ArbitraryCellSelector.Cell {
@Override
public boolean is(Object item) {
return item instanceof AdVo;
}
@Override
public int type() {
return R.layout.cell_ad;
}
@Override
public RecyclerView.ViewHolder holder(ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.cell_ad, parent, false);
return new AdViewHolder(view);
}
@Override
public void bind(RecyclerView.ViewHolder holder, Object item) {
try {
AdViewHolder adViewHolder = (AdViewHolder) holder;
AdVo ad = (AdVo) item;
adViewHolder.bind(ad);
} catch (ClassCastException e) {
L.printStackTrace(e);
}
}
protected class AdViewHolder extends ArbitraryCellHolder<AdVo> {
@BindView(R.id.ad_text_view)
protected TextView adTextView;
public AdViewHolder(View itemView) {
super(itemView);
}
@Override
public void bind(AdVo item) {
adTextView.setText(item.getTitle());
itemView.setOnClickListener(view -> adPublishSubject.onNext(item));
}
}
}
// Other ArbitraryCells...
Про Adapter Delegates автор не слышал?
Clean Recycler Adapter. Часть 1