Commit 903df460 authored by Matt Jones's avatar Matt Jones Committed by Commit Bot

Introduce SimpleRecyclerViewAdapter

This patch adds a basic adapter for RecyclerView that follows chrome's
MVC pattern. The API it exposes is intended to be as similar as
possible to the existing ModelListAdapter.

Bug: 909779
Change-Id: Ibda434edadf365c185433614f4a0d1da9e4b9862
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1650522
Commit-Queue: Matthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#682823}
parent 5034647d
......@@ -283,6 +283,7 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java",
"java/src/org/chromium/ui/modelutil/ListObservable.java",
"java/src/org/chromium/ui/modelutil/ListObservableImpl.java",
"java/src/org/chromium/ui/modelutil/MVCListAdapter.java",
"java/src/org/chromium/ui/modelutil/ModelListAdapter.java",
"java/src/org/chromium/ui/modelutil/PropertyKey.java",
"java/src/org/chromium/ui/modelutil/PropertyListModel.java",
......@@ -291,6 +292,7 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/modelutil/PropertyObservable.java",
"java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java",
"java/src/org/chromium/ui/modelutil/SimpleList.java",
"java/src/org/chromium/ui/modelutil/SimpleRecyclerViewAdapter.java",
"java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java",
"java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java",
"java/src/org/chromium/ui/resources/HandleViewResources.java",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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.view.View;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
/**
* An interface that defined a simple API for list adapters in MVC. This class also contains a small
* number of common utilities shared between implementations. In general, the only means of
* interaction with these kinds of list adapters post-initialization should be to register the
* different types of views that can be shown by the list, i.e.:
* {@link #registerType(int, ViewBuilder, ViewBinder)}.
*/
public interface MVCListAdapter {
/** A basic container for {@link PropertyModel}s with a type. */
class ListItem {
/** The type of view that the {@code model} is associated with. */
public final int type;
/** The model to be managed by a list. */
public final PropertyModel model;
/**
* Build a new item to managed by a {@link ModelListAdapter}.
* @param type The view type the model will bind to.
* @param model The model that will be bound to a view.
*/
public ListItem(int type, PropertyModel model) {
this.type = type;
this.model = model;
}
}
/** A basic observable list for this adapter to use. This more or less acts as a typedef. */
class ModelList extends ListModelBase<ListItem, Void> {}
/**
* An interface to provide a means to build specific view types.
* @param <T> The type of view that the implementor will build.
*/
interface ViewBuilder<T extends View> {
/**
* @return A new view to show in the list.
*/
T buildView();
}
/**
* Register a new view type that this adapter knows how to show.
* @param typeId The ID of the view type. This should not match any other view type registered
* in this adapter.
* @param builder A mechanism for building new views of the specified type.
* @param binder A means of binding a model to the provided view.
*/
<T extends View> void registerType(
int typeId, ViewBuilder<T> builder, ViewBinder<PropertyModel, T, PropertyKey> binder);
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
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. */
private 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) {
notifyItemInserted(index);
}
@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) {
notifyItemChanged(index);
}
@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);
}
@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;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(mViewBuilderMap.get(viewType).first.buildView(),
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);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment