Created
April 22, 2016 13:09
-
-
Save tschuchortdev/072a83dc24b65c77329fee72e9023990 to your computer and use it in GitHub Desktop.
generic RecyclerAdapter that can hold all types of views without modifications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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