/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.example.github.ui.common;
import android.annotation.SuppressLint;
import android.databinding.ViewDataBinding;
import android.os.AsyncTask;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.List;
/**
* A generic RecyclerView adapter that uses Data Binding & DiffUtil.
*
* @param <T> Type of the items in the list
* @param <V> The of the ViewDataBinding
*/
public abstract class DataBoundListAdapter<T, V extends ViewDataBinding>
extends RecyclerView.Adapter<DataBoundViewHolder<V>> {
@Nullable
private List<T> items;
// each time data is set, we update this variable so that if DiffUtil calculation returns
// after repetitive updates, we can ignore the old calculation
private int dataVersion = 0;
@Override
public final DataBoundViewHolder<V> onCreateViewHolder(ViewGroup parent, int viewType) {
V binding = createBinding(parent);
return new DataBoundViewHolder<>(binding);
}
protected abstract V createBinding(ViewGroup parent);
@Override
public final void onBindViewHolder(DataBoundViewHolder<V> holder, int position) {
//noinspection ConstantConditions
bind(holder.binding, items.get(position));
holder.binding.executePendingBindings();
}
@SuppressLint("StaticFieldLeak")
@MainThread
public void replace(List<T> update) {
dataVersion ++;
if (items == null) {
if (update == null) {
return;
}
items = update;
notifyDataSetChanged();
} else if (update == null) {
int oldSize = items.size();
items = null;
notifyItemRangeRemoved(0, oldSize);
} else {
final int startVersion = dataVersion;
final List<T> oldItems = items;
new AsyncTask<Void, Void, DiffUtil.DiffResult>() {
@Override
protected DiffUtil.DiffResult doInBackground(Void... voids) {
return DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return oldItems.size();
}
@Override
public int getNewListSize() {
return update.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
T oldItem = oldItems.get(oldItemPosition);
T newItem = update.get(newItemPosition);
return DataBoundListAdapter.this.areItemsTheSame(oldItem, newItem);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
T oldItem = oldItems.get(oldItemPosition);
T newItem = update.get(newItemPosition);
return DataBoundListAdapter.this.areContentsTheSame(oldItem, newItem);
}
});
}
@Override
protected void onPostExecute(DiffUtil.DiffResult diffResult) {
if (startVersion != dataVersion) {
// ignore update
return;
}
items = update;
diffResult.dispatchUpdatesTo(DataBoundListAdapter.this);
}
}.execute();
}
}
protected abstract void bind(V binding, T item);
protected abstract boolean areItemsTheSame(T oldItem, T newItem);
protected abstract boolean areContentsTheSame(T oldItem, T newItem);
@Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
}