Сферический адаптер в вакууме

Довольно странно, что в 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();
        }
    }

← клик, если это интересно   |   ↓ место для вопросов и идей