Skip to content

Instantly share code, notes, and snippets.

@zplesac
Created September 27, 2016 10:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zplesac/4987d43fbeef8a2dc18c4a127fdcc3e5 to your computer and use it in GitHub Desktop.
Save zplesac/4987d43fbeef8a2dc18c4a127fdcc3e5 to your computer and use it in GitHub Desktop.
package co.infinum.bigbetworld.adapters.recyclerview;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Created by Željko Plesac on 14/09/16.
*/
public abstract class AbstractRecyclerAdapter<E> extends RecyclerView.Adapter<AbstractRecyclerAdapter.ViewHolder> {
public static final int TYPE_HEADER = 111;
public static final int TYPE_FOOTER = 222;
public static final int TYPE_ITEM = 333;
private Context context;
private List<E> items;
List<View> headers = new ArrayList<>();
List<View> footers = new ArrayList<>();
protected OnClickListener<E> listener;
protected OnNextPageListener nextPageListener;
private UpdateItemsTask updateItemsTask;
public AbstractRecyclerAdapter(Context context, Collection<E> list) {
this.context = context;
this.items = new ArrayList<>(list);
}
@Override
public final ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Check if we have to inflate ItemViewHolder of HeaderFooterHolder
if (viewType == TYPE_ITEM) {
return onCreateItemViewHolder(parent);
} else {
FrameLayout frameLayout = new FrameLayout(parent.getContext());
frameLayout
.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
return new HeaderFooterViewHolder(frameLayout);
}
}
protected abstract ViewHolder onCreateItemViewHolder(ViewGroup parent);
@Override
public void onBindViewHolder(AbstractRecyclerAdapter.ViewHolder holder, int position) {
//check what type of view our position is
if (position < headers.size()) {
View v = headers.get(position);
//add our view to a header view and display it
prepareHeaderFooter((HeaderFooterViewHolder) holder, v);
} else if (position >= headers.size() + items.size()) {
View v = footers.get(position - items.size() - headers.size());
//add our view to a footer view and display it
prepareHeaderFooter((HeaderFooterViewHolder) holder, v);
} else {
E item = get(position);
holder.bind(item, position, Collections.emptyList());
}
}
@Override
public void onBindViewHolder(AbstractRecyclerAdapter.ViewHolder holder, int position, List<Object> payloads) {
//check what type of view our position is
if (position < headers.size()) {
View v = headers.get(position);
//add our view to a header view and display it
prepareHeaderFooter((HeaderFooterViewHolder) holder, v);
} else if (position >= headers.size() + items.size()) {
View v = footers.get(position - items.size() - headers.size());
//add our view to a footer view and display it
prepareHeaderFooter((HeaderFooterViewHolder) holder, v);
} else {
E item = get(position);
holder.bind(item, position, payloads);
}
}
@Override
public int getItemCount() {
return headers.size() + items.size() + footers.size();
}
public void add(E item) {
int position = items.size();
items.add(item);
notifyItemInserted(position);
}
public void addAll(Collection<E> collection) {
int position = items.size();
items.addAll(collection);
notifyItemRangeInserted(position, collection.size());
}
public void add(E item, int index) {
index = index - headers.size() - footers.size();
items.add(index, item);
notifyItemInserted(index);
}
public void addAll(Collection<E> collection, int index) {
index = index - headers.size() - footers.size();
items.addAll(index, collection);
notifyItemRangeInserted(index, collection.size());
}
public void remove(E item) {
int position = items.indexOf(item);
if (items.remove(item)) {
notifyItemRemoved(position);
}
}
public void removeAll(Collection<E> collection) {
if (items.removeAll(collection)) {
notifyDataSetChanged();
}
}
public void remove(int index) {
index = index - headers.size() - footers.size();
if (items.remove(index) != null) {
notifyItemRemoved(index);
}
}
public E get(int index) {
index = index - headers.size() - footers.size();
return items.get(index);
}
public Collection<E> getAll() {
return new ArrayList<>(items);
}
public void set(E item, int index) {
index = index - headers.size() - footers.size();
items.set(index, item);
notifyItemChanged(index);
}
public void reset(Collection<E> collection) {
reset(collection, null);
}
public void reset(Collection<E> collection, @Nullable DiffUtil.Callback callback) {
items.clear();
items.addAll(collection);
if (callback != null) {
updateItemsTask = new UpdateItemsTask();
updateItemsTask.execute(callback);
} else {
notifyDataSetChanged();
}
}
public void setOnClickListener(OnClickListener<E> listener) {
this.listener = listener;
}
public void setOnNextPageListener(OnNextPageListener nextPageListener) {
this.nextPageListener = nextPageListener;
}
public Context getContext() {
return context;
}
public void cancel() {
if (updateItemsTask != null) {
updateItemsTask.cancel(true);
}
}
private class UpdateItemsTask extends AsyncTask<DiffUtil.Callback, Void, DiffUtil.DiffResult> {
@Override
protected DiffUtil.DiffResult doInBackground(DiffUtil.Callback... params) {
if (params != null) {
return DiffUtil.calculateDiff(params[0]);
} else {
return null;
}
}
@Override
protected void onPostExecute(DiffUtil.DiffResult diffResult) {
super.onPostExecute(diffResult);
if (diffResult != null) {
diffResult.dispatchUpdatesTo(AbstractRecyclerAdapter.this);
}
}
}
// region Headers and Footers
public class HeaderFooterViewHolder extends ViewHolder {
FrameLayout base;
public HeaderFooterViewHolder(View itemView) {
super(itemView);
this.base = (FrameLayout) itemView;
}
@Override
protected void bind(E item, int position, List<Object> payloads) {
this.base = (FrameLayout) itemView;
}
}
//add a footer to the adapter
public void addFooter(View footer) {
if (!footers.contains(footer)) {
footers.add(footer);
//animate
notifyItemInserted(headers.size() + items.size() + footers.size() - 1);
}
}
//remove a footer from the adapter
public void removeFooter(View footer) {
if (footers.contains(footer)) {
//animate
notifyItemRemoved(headers.size() + items.size() + footers.indexOf(footer));
footers.remove(footer);
if (footer.getParent() != null) {
((ViewGroup) footer.getParent()).removeView(footer);
}
}
}
//add a header to the adapter
public void addHeader(View header) {
if (!headers.contains(header)) {
headers.add(header);
//animate
notifyItemInserted(headers.size() - 1);
}
}
//remove a header from the adapter
public void removeHeader(View header) {
if (headers.contains(header)) {
//animate
notifyItemRemoved(headers.indexOf(header));
headers.remove(header);
if (header.getParent() != null) {
((ViewGroup) header.getParent()).removeView(header);
}
}
}
private void prepareHeaderFooter(HeaderFooterViewHolder vh, View view) {
//empty out our FrameLayout and replace with our header/footer
vh.base.removeAllViews();
vh.base.addView(view);
}
@Override
public int getItemViewType(int position) {
//check what type our position is, based on the assumption that the order is headers > items > footers
if (position < headers.size()) {
return TYPE_HEADER;
} else if (position >= headers.size() + items.size()) {
return TYPE_FOOTER;
}
return TYPE_ITEM;
}
// endregion
protected abstract class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
protected abstract void bind(E item, int position, List<Object> payloads);
}
public interface OnClickListener<E> {
void onClick(int index, E item);
}
public interface OnNextPageListener {
void onScrolledToNextPage();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment