blob: d9a5e06f59af4704fb8a6a01e3cb0f8ae9c6a302 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui.modelutil;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.ui.modelutil.ListObservable.ListObserver;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
/**
* A simple adapter for {@link RecyclerView}. This is the RecyclerView version of the
* {@link ModelListAdapter} with the API surfaces being identical in terms of managing items in the
* view. In summary, use {@link #registerType(int, ViewBuilder, ViewBinder)} to tell the list
* adapter how to display a particular item. Updates to the {@link ListObservable} list provided in
* the constructor will immediately be reflected in the list.
*/
public class SimpleRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleRecyclerViewAdapter.ViewHolder>
implements MVCListAdapter {
/**
* A simple {@link ViewHolder} that keeps a view, view binder, and an MCP that relate the two.
*/
public class ViewHolder extends RecyclerView.ViewHolder {
/** The model change processor currently associated with this view and model. */
private PropertyModelChangeProcessor<PropertyModel, View, PropertyKey> mCurrentMcp;
/** The view binder that knows how to apply a model to the view this holder owns. */
private ViewBinder<PropertyModel, View, PropertyKey> mBinder;
/** A handle to the model currently held by this view holder. */
public PropertyModel model;
/**
* @param itemView The view to manage.
* @param binder The binder that knows how to apply a model to the view.
*/
public ViewHolder(View itemView, ViewBinder<PropertyModel, View, PropertyKey> binder) {
super(itemView);
mBinder = binder;
}
/**
* Set the model for this view holder to manage.
* @param model The model that should be bound to the view.
*/
void setModel(PropertyModel model) {
if (mCurrentMcp != null) mCurrentMcp.destroy();
this.model = model;
if (this.model == null) return;
mCurrentMcp = PropertyModelChangeProcessor.create(model, itemView, mBinder);
}
}
/** The data that is shown in the list. */
protected final ModelList mListData;
/** The observer that watches the data for changes. */
private final ListObserver<Void> mListObserver;
/** A map of view types to view binders. */
private final SparseArray<Pair<ViewBuilder, ViewBinder>> mViewBuilderMap = new SparseArray<>();
public SimpleRecyclerViewAdapter(ModelList data) {
mListData = data;
mListObserver =
new ListObserver<Void>() {
@Override
public void onItemRangeInserted(ListObservable source, int index, int count) {
notifyItemRangeInserted(index, count);
}
@Override
public void onItemRangeRemoved(ListObservable source, int index, int count) {
notifyItemRangeRemoved(index, count);
}
@Override
public void onItemRangeChanged(
ListObservable<Void> source,
int index,
int count,
@Nullable Void payload) {
notifyItemRangeChanged(index, count);
}
@Override
public void onItemMoved(ListObservable source, int curIndex, int newIndex) {
notifyItemMoved(curIndex, newIndex);
}
};
mListData.addObserver(mListObserver);
}
/** Clean up any state that needs to be. */
public void destroy() {
mListData.removeObserver(mListObserver);
}
/** @return The ModelList describing RecyclerView children. */
public ModelList getModelList() {
return mListData;
}
@Override
public int getItemCount() {
return mListData.size();
}
@Override
public <T extends View> void registerType(
int typeId, ViewBuilder<T> builder, ViewBinder<PropertyModel, T, PropertyKey> binder) {
assert mViewBuilderMap.get(typeId) == null;
mViewBuilderMap.put(typeId, new Pair<>(builder, binder));
}
@Override
public int getItemViewType(int position) {
return mListData.get(position).type;
}
/**
* Create a new view of the desired type.
*
* @param parent Parent view.
* @param typeId Type of the view to create.
* @return Created view.
*/
protected View createView(ViewGroup parent, int typeId) {
return mViewBuilderMap.get(typeId).first.buildView(parent);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(createView(parent, viewType), mViewBuilderMap.get(viewType).second);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.setModel(mListData.get(position).model);
}
@Override
public void onViewRecycled(ViewHolder holder) {
holder.setModel(null);
}
}