Skip to content

Instantly share code, notes, and snippets.

@tschuchortdev
Created April 22, 2016 13:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschuchortdev/072a83dc24b65c77329fee72e9023990 to your computer and use it in GitHub Desktop.
Save tschuchortdev/072a83dc24b65c77329fee72e9023990 to your computer and use it in GitHub Desktop.
generic RecyclerAdapter that can hold all types of views without modifications
/**
* example implementation of GenericRecyclerAdapter.Item
**/
public class ExampleItem extends GenericRecyclerAdapter.Item {
public String title;
public ExampleItem(String title)
{
this.title = title;
}
@Override
protected View createView(LayoutInflater inflater, ViewGroup parent)
{
return inflater.inflate(R.layout.example_item, parent, false);
}
@Override
public void bindViewHolder(GenericRecyclerAdapter.ViewHolder holder)
{
holder.<TextView>findViewById(R.id.example_item_title).setText(title);
}
}
package com.awesomecalc.awesomecalculator.adapter;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Arrays;
/**
* generic RecyclerView adapter that can accept all types of Items and handle clicks
*/
public class GenericRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
protected final ArrayList<Item> mItems;
private final SparseArray<ViewHolderFactory> mViewHolderFactories; //map factories for creating an appropriate viewholder to the item types
protected OnClickListener mOnClickListener;
public interface OnClickListener {
void onItemClick(View view, Item item, int position);
}
protected interface ViewHolderFactory {
RecyclerView.ViewHolder createViewHolder(ViewGroup parent, Item.AdapterItemClickListener clickCallback);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
SparseArray<View> holdedViews = new SparseArray<>();
public ViewHolder(View itemView)
{
super(itemView);
holdViews(itemView);
}
private void holdViews(View itemView)
{
if (itemView instanceof ViewGroup && ((ViewGroup) itemView).getChildCount() > 0)
for(int i=0; i< ((ViewGroup) itemView).getChildCount(); ++i) {
View nextChild = ((ViewGroup) itemView).getChildAt(i);
holdedViews.put(nextChild.getId(), nextChild);
if (nextChild instanceof ViewGroup)
holdViews(nextChild);
}
}
public <T extends View> T findViewById(int id)
{
return (T) holdedViews.get(id);
}
}
public static abstract class Item implements ViewHolderFactory, View.OnClickListener {
private View mView;
private ViewHolder mHolder;
private AdapterItemClickListener mClickListener;
public interface AdapterItemClickListener {
void onAdapterItemClick(View view, Item item, int adapterPosition);
}
public RecyclerView.ViewHolder createViewHolder(ViewGroup parent, AdapterItemClickListener clickListener)
{
mView = createView(LayoutInflater.from(parent.getContext()), parent);
mView.setOnClickListener(this);
mClickListener = clickListener;
mHolder = new ViewHolder(mView);
return mHolder;
}
@Override
public void onClick(View v)
{
//use getLayoutPosition() instead of getAdapterPosition() because an already layed out view has been clicked
//although the adapter contents may have already changed and have not yet been layed out
if(mClickListener != null)
mClickListener.onAdapterItemClick(mView, this, mHolder.getLayoutPosition());
}
/**
* inflate or create your view and add it to the parent. Only initialize the view/childs to
* a state that is the same in all items.
* @param inflater
* @param parent
* @return
*/
abstract protected View createView(LayoutInflater inflater, ViewGroup parent);
/**
* initialize the view to an item specific state (icon, title etc.) by using findViewById()
* on the ViewHolder
* @param holder
*/
abstract public void bindViewHolder(ViewHolder holder);
public View getView()
{
return mView;
}
/**
* get a unique identifier for the item type/class that can be used by the adapter
* @return unique identifier of the item type/class
*/
final public int getViewTypeId()
{
//hashCode() is not guaranteed to be even practically unique over all class objects. But since an adapter will
//rarely have more than 10 different Items, we can assume a reasonably low probability of collision.
//should collision still occur, method can be made abstract and all derived classes have to supply a UUID
return this.getClass().hashCode();
}
}
public GenericRecyclerAdapter()
{
this(null);
}
public GenericRecyclerAdapter(Item[] items)
{
mViewHolderFactories = new SparseArray<>();
if(items == null || items.length == 0) {
mItems = new ArrayList<>();
}
else {
mItems = new ArrayList<>(Arrays.asList(items));
//hashCode isn't guaranteed to be unique but with only so few item types, collision is very unlikely
for(Item item : items)
if(mViewHolderFactories.get(item.getViewTypeId()) == null)
mViewHolderFactories.put(item.getClass().hashCode(), item);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
return mViewHolderFactories.get(viewType).createViewHolder(parent, (View clickedView, Item clickedItem, int clickedPosition) ->
{
if(mOnClickListener != null)
mOnClickListener.onItemClick(clickedView, clickedItem, clickedPosition);
});
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
mItems.get(position).bindViewHolder((ViewHolder)holder);
}
@Override
public int getItemCount()
{
return mItems.size();
}
public void add(Item newItem)
{
insertAt(newItem, getItemCount());
}
public void add(Item[] newItems)
{
insertAt(newItems, getItemCount());
}
public void insertAt(Item[] newItems, int position)
{
for (int i = 0; i < newItems.length; i++)
insertAt(newItems[i], position + i);
}
public void insertAt(Item newItem, int position)
{
mItems.add(position, newItem);
if(mViewHolderFactories.get(newItem.getViewTypeId()) == null)
mViewHolderFactories.put(newItem.getViewTypeId(), newItem);
notifyItemInserted(position);
}
public void remove(Item oldItem)
{
removeAt(mItems.indexOf(oldItem));
}
public void removeAt(int position)
{
mItems.remove(position);
notifyItemRemoved(position);
}
public void removeRange(int start, int end)
{
for(; start < end; start++)
removeAt(start);
}
@Override
public int getItemViewType(int position)
{
return mItems.get(position).getViewTypeId();
}
public void setOnClickListener(OnClickListener listener)
{
mOnClickListener = listener;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment