package com.skocken.efficientadapter.lib.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.skocken.efficientadapter.lib.util.AdapterUpdater; import com.skocken.efficientadapter.lib.viewholder.EfficientViewHolder; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; class AdapterHelper<T> { private final Class<? extends EfficientViewHolder<? extends T>> mViewHolderClass; private final int mLayoutResId; private final List<T> mObjects; private EfficientAdapter.OnItemClickListener<T> mOnItemClickListener; private EfficientAdapter.OnItemLongClickListener<T> mOnItemLongClickListener; /** * Constructor * * @param objects The objects to represent in the RecyclerView. */ AdapterHelper(T... objects) { this(new ArrayList<>(Arrays.asList(objects))); } /** * Constructor * * @param objects The objects to represent in the RecyclerView. */ AdapterHelper(List<T> objects) { this(0, null, objects); } /** * Constructor * * @param layoutResId layout resource id to inflate for all objects in this adapter * @param viewHolderClass the view holder class to instantiate for all objects * @param objects the objects to put in this adapter */ AdapterHelper(int layoutResId, Class<? extends EfficientViewHolder<? extends T>> viewHolderClass, T... objects) { this(layoutResId, viewHolderClass, new ArrayList<>(Arrays.asList(objects))); } /** * Constructor * * @param layoutResId layout resource id to inflate for all objects in this adapter * @param viewHolderClass the view holder class to instantiate for all objects * @param objects the objects to put in this adapter */ AdapterHelper(int layoutResId, Class<? extends EfficientViewHolder<? extends T>> viewHolderClass, List<T> objects) { if (objects == null) { mObjects = new ArrayList<>(); } else { mObjects = objects; } mViewHolderClass = viewHolderClass; mLayoutResId = layoutResId; } /** * Called by the view to display the data at the specified position. * * <p>The default implementation of this method will call {@link EfficientViewHolder#onBindView(Object, * int)} * on the {@link EfficientViewHolder} and set the click listeners if necessary.</p> * * @param viewHolder The ViewHolder which should be updated to represent the contents of the * item at the given position in the data set. * @param position The position of the item within the adapter's data set. * @param adapter The adapter source */ public void onBindViewHolder(EfficientViewHolder<T> viewHolder, int position, EfficientAdapter<T> adapter) { T object = get(position); viewHolder.onBindView(object, position); viewHolder.setAdapter(adapter); setClickListenerOnView(viewHolder); setLongClickListenerOnView(viewHolder); } /** * Register a callback to be invoked when an item in this AbsViewHolderAdapter has * been long-clicked. * <p/> * Your view holder must allow the click by overriding the method "isClickable()" * * @param listener The callback that will be invoked. */ void setOnItemLongClickListener(EfficientAdapter.OnItemLongClickListener<T> listener) { mOnItemLongClickListener = listener; } /** * Register a callback to be invoked when an item in this AbsViewHolderAdapter has * been clicked. * <p/> * Your view holder must allow the click by overriding the method "isClickable()" * * @param listener The callback that will be invoked. */ void setOnItemClickListener(EfficientAdapter.OnItemClickListener<T> listener) { mOnItemClickListener = listener; } /** * Get the callback to be invoked when an item in this adapter has been clicked. * @return listener The callback that will be invoked on item click. */ public EfficientAdapter.OnItemClickListener<T> getOnItemClickListener() { return mOnItemClickListener; } /** * Get the callback to be invoked when an item in this adapter has been long-clicked. * @return listener The callback that will be invoked on item long click. */ public EfficientAdapter.OnItemLongClickListener<T> getOnItemLongClickListener() { return mOnItemLongClickListener; } /** * Determine if the object provide is in this adapter * * @return true if the object is in this adapter */ boolean hasItem(T object) { return mObjects.contains(object); } /** * Searches this {@code List} for the specified object and returns the index of the * first occurrence. * * @param object the object to search for. * @return the index of the first occurrence of the object or -1 if the * object was not found. */ int indexOf(T object) { return mObjects.indexOf(object); } /** * Adds the specified Collection at the end of the array. * * @param collection The Collection to add at the end of the array. */ int addAll(Collection<? extends T> collection) { int positionOfInsert = mObjects.size(); mObjects.addAll(collection); return positionOfInsert; } /** * Adds the specified items at the end of the array. * * @param items The items to add at the end of the array. */ int addAll(T... items) { int positionOfInsert = mObjects.size(); Collections.addAll(mObjects, items); return positionOfInsert; } /** * Adds the specified object at the end of the array. * * @param object The object to add at the end of the array. */ int add(T object) { int positionOfInsert = mObjects.size(); mObjects.add(object); return positionOfInsert; } /** * Adds the specified object at the specified position of the array. * * @param position The position of object to add * @param object The object to add at the end of the array. */ int add(int position, T object) { mObjects.add(position, object); return position; } /** * Remove the object at the specified position of the array. * * @param position The position of object to add */ T removeAt(int position) { return mObjects.remove(position); } /** * Remove the specified object of the array. * * @param object The object to add at the end of the array. */ int remove(T object) { int positionOfRemove = mObjects.indexOf(object); if (positionOfRemove >= 0) { T objectRemoved = removeAt(positionOfRemove); if (objectRemoved != null) { return positionOfRemove; } } return -1; } /** * Move object */ void move(int from, int to) { mObjects.add(to, mObjects.remove(from)); } /** * Update the adapter list with this new list. * Using this method, instead of clear/addAll will allow the implementation to compute the best way to update the * elements. * For example, if you have only one item which was in the previous list and which is not on the new, the * Updater has an opportunity to just call `remove` on this item. * @param efficientAdapter the adapter to update. * @param list the new list of item to be into this adapter. */ void updateWith(EfficientAdapter<T> efficientAdapter, List<T> list) { new AdapterUpdater<>(efficientAdapter).update(list); } /** * Remove all elements from the list. */ int clear() { int nbObjectRemoved = mObjects.size(); mObjects.clear(); return nbObjectRemoved; } /** * Returns whether this {@code List} contains no elements. * * @return {@code true} if this {@code List} has no elements, {@code false} * otherwise. * @see #size */ boolean isEmpty() { return size() == 0; } /** * Returns the number of elements in this {@code List}. * * @return the number of elements in this {@code List}. */ int size() { return mObjects.size(); } /** * @return a copy of the {@code List} of elements. */ List<T> getObjects() { return new ArrayList<>(mObjects); } T get(int position) { return mObjects.get(position); } View inflateView(ViewGroup parent, int layoutResId) { if (parent == null) { return null; } Context context = parent.getContext(); if (context == null) { return null; } LayoutInflater inflater = LayoutInflater.from(context); return inflater.inflate(layoutResId, parent, false); } int getLayoutResId() { return mLayoutResId; } Class<? extends EfficientViewHolder<? extends T>> getViewHolderClass() { return mViewHolderClass; } void setClickListenerOnView(EfficientViewHolder viewHolder) { View view = viewHolder.getView(); View.OnClickListener listener = viewHolder.getOnClickListener(mOnItemClickListener != null); view.setOnClickListener(listener); if (listener == null) { view.setClickable(false); } } void setLongClickListenerOnView(EfficientViewHolder viewHolder) { View view = viewHolder.getView(); View.OnLongClickListener listener = viewHolder.getOnLongClickListener(mOnItemClickListener != null); view.setOnLongClickListener(listener); if (listener == null) { view.setLongClickable(false); } } EfficientViewHolder<? extends T> generateViewHolder(View v, Class<? extends EfficientViewHolder<? extends T>> viewHolderClass, EfficientAdapter<T> adapter) { Constructor<?>[] constructors = viewHolderClass.getDeclaredConstructors(); if (constructors == null) { throw new IllegalArgumentException( "Impossible to found a constructor for " + viewHolderClass.getSimpleName()); } for (Constructor<?> constructor : constructors) { Class<?>[] parameterTypes = constructor.getParameterTypes(); if (parameterTypes == null) { continue; } try { Object viewHolder; if (isAssignableFrom(parameterTypes, View.class)) { //single or static inner class ViewHolder viewHolder = constructor.newInstance(v); } else if (isAssignableFrom(parameterTypes, EfficientAdapter.class, View.class)) { // inner class ViewHolder inside EfficientAdapter viewHolder = constructor.newInstance(adapter, v); } else { viewHolder = null; } if (viewHolder instanceof EfficientViewHolder) { return (EfficientViewHolder<T>) viewHolder; } } catch (Exception e) { throw new RuntimeException( "Impossible to instantiate " + viewHolderClass.getSimpleName(), e); } } throw new IllegalArgumentException( "Impossible to found a constructor with a view for " + viewHolderClass.getSimpleName()); } static boolean isAssignableFrom(Class<?>[] parameterTypes, Class<?>... classes) { if (parameterTypes == null || classes == null || parameterTypes.length != classes.length) { return false; } for (int i = 0; i < parameterTypes.length; i++) { if (!classes[i].isAssignableFrom(parameterTypes[i])) { return false; } } return true; } void throwMissingLayoutResId(int viewType) { throw new IllegalArgumentException("No default layout found for the view type '" + viewType + "'."); } void throwMissingViewHolder(int viewType) { throw new IllegalArgumentException( "You must supply a view holder class for the element for view type " + viewType); } private void onClickItem(EfficientAdapter<T> efficientAdapter, EfficientViewHolder viewHolder) { if (mOnItemClickListener != null) { T object = (T) viewHolder.getObject(); View view = viewHolder.getView(); int position = viewHolder.getLastBindPosition(); mOnItemClickListener.onItemClick(efficientAdapter, view, object, position); } } private void onLongClickItem(EfficientAdapter<T> efficientAdapter, EfficientViewHolder viewHolder) { if (mOnItemLongClickListener != null) { T object = (T) viewHolder.getObject(); View view = viewHolder.getView(); int position = viewHolder.getLastBindPosition(); mOnItemLongClickListener.onLongItemClick(efficientAdapter, view, object, position); } } }