Skip to content

Instantly share code, notes, and snippets.

@yolapop
Last active August 16, 2017 16:15
Show Gist options
  • Save yolapop/00477a3152efdd0f11fdd59a913b4eaf to your computer and use it in GitHub Desktop.
Save yolapop/00477a3152efdd0f11fdd59a913b4eaf to your computer and use it in GitHub Desktop.
Generic FastAdapter's AbstractItem to be used for all types of ViewHolders
/**
* Functional interface for binding data to a view
*/
public interface ViewBinder<V extends View> {
void bindView(V view, ViewItem<V> item);
}
/**
* Functional interface for generating a View
*/
public interface ViewGenerator<V extends View> {
V generateView(Context ctx, ViewGroup parent);
}
import android.content.Context;
import android.support.annotation.IdRes;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.ViewGroup;
import com.mikepenz.fastadapter.FastAdapter;
import com.mikepenz.fastadapter.IClickable;
import com.mikepenz.fastadapter.IExpandable;
import com.mikepenz.fastadapter.ISubItem;
import com.mikepenz.fastadapter.items.AbstractItem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Usage:
*
* public ViewItem<TextView> textViewItem(String title) {
* return new ViewItem<>(TextView.class.hashCode(),
* (ctx, parent) -> new TextView(ctx)
* .withBinder((view, item) -> {
* view.setText(title);
* });
* }
*
* Then you add this ViewItem to FastItemAdapter
*/
public class ViewItem<V extends View> extends AbstractItem<ViewItem<V>, ViewWrapper<V>> implements
IExpandable<ViewItem, ViewItem>, ISubItem<ViewItem, ViewItem>, IClickable<ViewItem<V>> {
private boolean mExpanded = false;
private int type;
private ViewItem mParent;
private List<ViewItem> mSubItems;
private ViewGenerator<V> generator;
private ViewBinder<V> binder, unbinder;
private Map<Class, Object> typedTags;
/**
* View to rotate when the item is clicked, perfect for expandable animation
*/
@IdRes private int idViewToRotate;
private int rotation = 0;
private FastAdapter.OnClickListener<ViewItem<V>> onItemClickListener;
private FastAdapter.OnClickListener<ViewItem<V>> defaultOnItemClickListener = (v, adapter, item, position) -> {
View viewToRotate = getViewToRotate(v);
if (item.getSubItems() != null && viewToRotate != null && rotation != 0) {
if (item.isExpanded()) {
ViewCompat.animate(viewToRotate).rotation(rotation).start();
} else {
ViewCompat.animate(viewToRotate).rotation(0).start();
}
return onItemClickListener == null || onItemClickListener.onClick(v, adapter, item, position);
}
return onItemClickListener != null && onItemClickListener.onClick(v, adapter, item, position);
};
/**
* Build the view item. there are 2 mandatory parameters: it's type and the view generator
*
* @param type must be globally unique
* @param generator functional interface for generating the view when needed
*/
public ViewItem(int type, ViewGenerator<V> generator) {
this.generator = generator;
this.type = type;
}
/**
* Utility function to create list of view items in a declarative way. It also filters out nulls from the input
*
* @param items
* @return
*/
public static List<AbstractItem> list(AbstractItem... items) {
ArrayList<AbstractItem> list = new ArrayList<>(items.length);
for (AbstractItem i : items) {
if (i == null) continue;
list.add(i);
}
return list;
}
/**
* Utility function to create list of view items in a declarative way. It also filters out nulls from the input
*
* @param items
* @return
*/
public static List<AbstractItem> list(List<AbstractItem>... items) {
ArrayList<AbstractItem> list = new ArrayList<>();
for (List<AbstractItem> i : items) {
if (i == null) continue;
list.addAll(i);
}
return list;
}
/**
* Functional interface to set the view contents that will be called on
* {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)}
*
* @param binder
* @return
*/
public ViewItem<V> withBinder(ViewBinder<V> binder) {
this.binder = binder;
return this;
}
/**
* Functional interface to unset the view contents that will be called on
* {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)}
*
* @param binder
* @return
*/
public ViewItem<V> withUnbinder(ViewBinder<V> unbinder) {
this.unbinder = unbinder;
return this;
}
@Override
public ViewItem<V> withOnItemClickListener(FastAdapter.OnClickListener<ViewItem<V>> onItemClickListener) {
this.onItemClickListener = onItemClickListener;
return this;
}
/**
* In case you want a rotating icon (e.g. arrow) when this item is expanded
*/
public ViewItem<V> withViewToRotate(@IdRes int id) {
this.idViewToRotate = id;
return this;
}
public ViewItem<V> withRotation(int rotation) {
this.rotation = rotation;
return this;
}
@Override
public FastAdapter.OnClickListener<ViewItem<V>> getOnItemClickListener() {
return defaultOnItemClickListener;
}
/**
* Do not use.
* @return
*/
@Deprecated
@Override
public int getLayoutRes() {
return 0; //this is ignored, we're overriding generateView instead
}
/**
* The type of view holder.
* @return
*/
@Override
public int getType() {
return type;
}
/**
* The logic to bind our data to the view
*/
@Override
public void bindView(ViewWrapper<V> holder, List<Object> payloads) {
super.bindView(holder, payloads);
if (binder != null) {
binder.bindView(holder.view, this);
}
View viewToRotate = getViewToRotate(holder.view);
if (viewToRotate != null && rotation != 0 && isExpanded()) {
ViewCompat.setRotation(viewToRotate, rotation);
} else if (viewToRotate != null) {
ViewCompat.setRotation(viewToRotate, 0);
}
}
/**
* This optional function unbinds the view to the default state
*
* @param holder
*/
@Override
public void unbindView(ViewWrapper<V> holder) {
super.unbindView(holder);
if (unbinder != null) {
unbinder.bindView(holder.view, this);
}
View viewToRotate = getViewToRotate(holder.view);
if (viewToRotate != null) {
viewToRotate.clearAnimation();
}
}
/**
* Generates the view when needed
* @param ctx
* @param parent
* @return
*/
@Override
public View generateView(Context ctx, ViewGroup parent) {
return generator.generateView(ctx, parent);
}
/**
* Do not use.
*
* @param parent
* @return
*/
@Deprecated
@Override
public ViewWrapper<V> getViewHolder(ViewGroup parent) {
return getViewHolder(generateView(parent.getContext(), parent));
}
/**
* Do not use.
*
* @param v
* @return
*/
@Deprecated
@Override
public ViewWrapper<V> getViewHolder(View v) {
return new ViewWrapper<>(v);
}
private View getViewToRotate(View parent) {
return parent.findViewById(idViewToRotate);
}
/**
* @return true if the view item is currently expanded
*/
@Override
public boolean isExpanded() {
return mExpanded;
}
/**
* Set the view item as expanded or not.
*
* @param expanded
* @return
*/
@Override
public ViewItem<V> withIsExpanded(boolean expanded) {
mExpanded = expanded;
return this;
}
/**
* Set the subitems. Subitems will be shown when view item is expanded
*
* @param subItems
* @return
*/
@Override
public ViewItem<V> withSubItems(List<ViewItem> subItems) {
this.mSubItems = subItems;
for (ViewItem subItem : subItems) {
subItem.withParent(this);
}
return this;
}
/**
* Set the subitems. Subitems will be shown when view item is expanded
*
* @param subItems
* @return
*/
public ViewItem<V> withSubItemsOf(ViewItem... items) {
return withSubItems(Arrays.asList(items));
}
@Override
public List<ViewItem> getSubItems() {
return this.mSubItems;
}
@Override
public boolean isAutoExpanding() {
return true;
}
/**
* Get the parent view item if it exists.
*
* @return
*/
@Override
public ViewItem<V> getParent() {
return mParent;
}
/**
* Do not use this. use the parent's withSubItems instead.
*
* @param parent
* @return
*/
@Deprecated
@Override
public ViewItem withParent(ViewItem parent) {
mParent = parent;
return this;
}
/**
* Add tag of arbitrary type. This object can contain multiple tags as long as each key (class)
* is different. Tags set with this method will not collide with ones that are set with
* {@link #withTag(Object)}.
*
* @return this object, for chaining
*/
public <T> ViewItem<V> withTag(Class<T> key, T value) {
if (typedTags == null) { // lazy creation
synchronized (this) { // thread-safe
if (typedTags == null) { // there's a small chance it's been set
typedTags = new HashMap<>(2);
}
}
}
typedTags.put(key, value);
return this;
}
/**
* Get the tag set by {@link #withTag(Class, Object)} or null if doesn't exist.
*/
public <T> T getTag(Class<T> key) {
if (typedTags == null) {
return null;
}
//noinspection unchecked
return (T) typedTags.get(key);
}
}
public class ViewWrapper<V extends View> extends RecyclerView.ViewHolder {
public final V view;
public ViewWrapper(View itemView) {
super(itemView);
view = (V) itemView;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment