Сферический адаптер в вакууме
Довольно странно, что в Android нет стандартных реализаций адаптеров для RecyclerView. Решил написать пару штук.
ViewHolder
public abstract class RecyclerHolder<T> extends RecyclerView.ViewHolder {
public RecyclerHolder(View view) {
super(view);
}
public abstract void bind(T model);
public abstract void unbind();
}
К стандартному ViewHolder'у добавлен интерфейс для передачи информации и для освобождения ресурсов.
Сферический адаптер в вакууме
public abstract class AbsAdapter<M, VH extends RecyclerHolder<M>> extends RecyclerView.Adapter<VH> {
private final Context mContext;
private final Class<VH> mHolderClass;
private final @LayoutRes int mLayout;
public AbsAdapter(Context context, @LayoutRes int layout, Class<VH> holderClass) {
mContext = context;
mLayout = layout;
mHolderClass = holderClass;
}
public AbsAdapter(Context context, Class<VH> holderClass) {
this(context, -1, holderClass);
}
@Override @SuppressWarnings("unchecked")
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
try {
if (mLayout == -1) {
return mHolderClass.getConstructor(ViewGroup.class).newInstance(parent);
} else {
View v = LayoutInflater.from(mContext).inflate(mLayout, parent, false);
return mHolderClass.getConstructor(View.class).newInstance(v);
}
} catch (Exception e) { // old Android can't catch multiple reflection Exceptions
throw new RuntimeException("Incompatible ViewHolder's constructor.", e);
}
}
@Override
public void onViewRecycled(VH holder) {
holder.unbind();
}
}
Если адаптеру был передан id вёрстки, он "надувает" (inflate) View и ищет у ViewHolder'а конструктор такого вида:
public SomeHolder(View itemView) {
super(itemView);
// findViewById...
}
Если же адаптер был порождён без передачи id вёрстки, он ищет конструктор более самостоятельного вида:
public PostHolder(ViewGroup root) {
super(LayoutInflater.from(root.getContext())
.inflate(R.layout.item_post_part_post, root, false));
// findViewById...
}
То есть в первом случае остаётся возможность работать по-старому, передавая адаптеру, например, старый добрый android.R.layout.simple_list_item_1, а во втором случае считается, что ViewHolder сам знает, какую вёрстку использовать.
Адаптер списка
public class ListAdapter<M, VH extends RecyclerHolder<M>> extends AbsAdapter<M, VH> {
private List<M> mModels;
public ListAdapter(Context context, List<M> models, Class<VH> holderClass) {
super(context, -1, holderClass);
mModels = models;
}
public ListAdapter(Context context, List<M> models, @LayoutRes int layout, Class<VH> holderClass) {
super(context, layout, holderClass);
mModels = models;
}
public void changeDataSet(List<M> newModels) {
mModels = newModels;
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(VH holder, int position) {
holder.bind(mModels.get(position));
}
@Override
public int getItemCount() {
return mModels.size();
}
}
Адаптер для работы с внешней информацией
Предназначен для работы с SQLite. Идея отсюда: Маленькая хитрость для отображения большого объёма данных в ListView
public class CursorAdapter<M, VH extends RecyclerHolder<M>> extends AbsAdapter<M, VH> {
private final DataProvider mProvider;
private final RecyclerView mRecyclerView;
public CursorAdapter(RecyclerView recycler, Class<VH> holderClass, DataProvider<M> provider) {
super(recycler.getContext(), -1, holderClass);
mProvider = provider;
mRecyclerView = recycler;
}
public CursorAdapter(Context context, Class<VH> holderClass, DataProvider<M> provider) {
super(context, -1, holderClass);
mProvider = provider;
mRecyclerView = null;
}
@Override @SuppressWarnings("unchecked") // ну бля, опять
public void onBindViewHolder(VH holder, int position) {
holder.bind((M) mProvider.getModelAt(position));
}
@Override
public int getItemCount() {
return mProvider.getCount();
}
public interface DataProvider<M> {
M getModelAt(int pk);
int getCount();
}
}
Пример реализации DataProvider
private class PostsProvider implements CursorAdapter.DataProvider<Social.Post> {
private Cursor mPrimaryKeys;
private PostsProvider() {
mPrimaryKeys = ContentProvider.get().getPostsPks();
}
@Override
public Social.Post getModelAt(int pos) {
mPrimaryKeys.moveToPosition(pos);
return ContentProvider.get().getPost(mPrimaryKeys.getLong(0));
}
@Override
public int getCount() {
return mPrimaryKeys.getCount();
}
public void refresh() {
mPrimaryKeys = ContentProvider.get().getPostsPks();
}
}