diff --git a/eu.davidea.flexibleadapter/ExampleAdapter.java b/eu.davidea.flexibleadapter/ExampleAdapter.java new file mode 100644 index 00000000..f0789ceb --- /dev/null +++ b/eu.davidea.flexibleadapter/ExampleAdapter.java @@ -0,0 +1,100 @@ +package eu.davidea.flexibleadapter; + +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + + +public class ExampleAdapter extends FlexibleAdapter { + + private static final String TAG = ManageLabelsAdapter.class.getSimpleName(); + + public interface OnItemClickListener { + public void onListItemClick(int position); + public boolean onListItemLongClick(int position); + } + + private LayoutInflater mInflater; + + private OnItemClickListener mClickListener; + + public ManageLabelsAdapter(OnItemClickListener listener) { + this.mClickListener = listener; + updateDataSet(); + } + + public void updateDataSet() { + this.mItems = DatabaseService.getInstance().getUserLabelsList(); + } + + public Label getItem(int position) { + return mItems.get(position); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + Log.d(TAG, "onCreateViewHolder for viewType "+viewType); + if (mInflater == null) { + mInflater = LayoutInflater.from(parent.getContext()); + } + //NOTE: Example for multiple layout. getItemViewType needs to be Overridden + //final int layout = viewType == XXX ? R.layout.item : R.layout.item2; + + View view = mInflater.inflate(R.layout.drawer_list_row, parent, false); + //Eventually set the view's size, margins, paddings and layout parameters + return new ViewHolder(view, mClickListener); + } + + @Override + public void onBindViewHolder(ViewHolder holder, final int position) { + Log.d(TAG, "onBindViewHolder for position "+position); + final Label label = mItems.get(position); + //TODO: Set the proper Icon with tint + holder.mImageView.setImageResource(label.getIconResource()!=null?label.getIconResource():R.drawable.ic_label_grey600_24dp); + holder.mTextView.setText(label.getName()); + holder.mCounterView.setText(String.valueOf(label.getCounter())); + holder.itemView.setActivated(isSelected(position)); + } + + /** + * Provide a reference to the views for each data item. + * Complex data labels may need more than one view per item, and + * you provide access to all the views for a data item in a view holder. + */ + static final class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, + View.OnLongClickListener { + + ImageView mImageView; + TextView mTextView; + TextView mCounterView; + private OnItemClickListener mClickListener; + + public ViewHolder(View view, OnItemClickListener listener) { + super(view); + mClickListener = listener; + + this.mImageView = (ImageView) view.findViewById(R.id.icon); + this.mTextView = (TextView) view.findViewById(R.id.name); + this.mCounterView = (TextView) view.findViewById(R.id.counter); + + this.itemView.setOnClickListener(this); + this.itemView.setOnLongClickListener(this); + } + + @Override + public void onClick(View view) { + mClickListener.onListItemClick(getAdapterPosition()); + } + + @Override + public boolean onLongClick(View view) { + return mClickListener.onListItemLongClick(getAdapterPosition()); + } + + } + +} \ No newline at end of file diff --git a/eu.davidea.flexibleadapter/FlexibleAdapter.java b/eu.davidea.flexibleadapter/FlexibleAdapter.java new file mode 100644 index 00000000..7a6e474d --- /dev/null +++ b/eu.davidea.flexibleadapter/FlexibleAdapter.java @@ -0,0 +1,107 @@ +package eu.davidea.flexibleadapter; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.ViewGroup; + +/** + * This class provides a set of standard methods to handle changes on the data set + * such as adding, removing, moving an item. + * + * Remember to call {@link RecyclerView#scheduleLayoutAnimation()} after adding or + * removing an item from the Adapter when the activity is paused. + * + * VH is your implementation of {@link RecyclerView#ViewHolder}. + * T is your domain object containing the data. + * + * @author Davide Steduto + */ +public abstract class FlexibleAdapter extends SelectableAdapter { + + private static final String TAG = FlexibleAdapter.class.getSimpleName(); + + protected List mItems; + //TODO: Handling the undo functionality + + public FlexibleAdapter() { + + } + + public abstract void updateDataSet(); + + public abstract T getItem(int position); + + @Override + public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); + + @Override + public abstract void onBindViewHolder(VH holder, final int position); + + @Override + public int getItemCount() { + return mItems.size(); + } + + public void addItem(int position, T item) { + mItems.add(position, item); + Log.d(TAG, "addItem notifyItemInserted on position "+position); + notifyItemInserted(position); + } + + private void removeItem(int position) { + Log.d(TAG, "removeItem on position "+position); + mItems.remove(position); + Log.d(TAG, "removeItem notifyItemRemoved on position "+position); + notifyItemRemoved(position); + } + + public void removeItems(List selectedPositions) { + Log.d(TAG, "removeItems Sorting positions --------------"); + // Reverse-sort the list + Collections.sort(selectedPositions, new Comparator() { + @Override + public int compare(Integer lhs, Integer rhs) { + return rhs - lhs; + } + }); + + // Split the list in ranges + while (!selectedPositions.isEmpty()) { + if (selectedPositions.size() == 1) { + removeItem(selectedPositions.get(0)); + //Align the selection list when removing the item + selectedPositions.remove(0); + } else { + int count = 1; + while (selectedPositions.size() > count && selectedPositions.get(count).equals(selectedPositions.get(count - 1) - 1)) { + ++count; + } + + if (count == 1) { + removeItem(selectedPositions.get(0)); + } else { + removeRange(selectedPositions.get(count - 1), count); + } + + for (int i = 0; i < count; ++i) { + selectedPositions.remove(0); + } + } + Log.d(TAG, "removeItems current selection " + getSelectedItems()); + } + } + + private void removeRange(int positionStart, int itemCount) { + Log.d(TAG, "removeRange positionStart="+positionStart+ " itemCount="+itemCount); + for (int i = 0; i < itemCount; ++i) { + mItems.remove(positionStart); + } + Log.d(TAG, "removeRange notifyItemRangeRemoved"); + notifyItemRangeRemoved(positionStart, itemCount); + } + +} \ No newline at end of file diff --git a/eu.davidea.flexibleadapter/SelectableAdapter.java b/eu.davidea.flexibleadapter/SelectableAdapter.java new file mode 100644 index 00000000..9ecb44c5 --- /dev/null +++ b/eu.davidea.flexibleadapter/SelectableAdapter.java @@ -0,0 +1,144 @@ +package eu.davidea.flexibleadapter; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.util.Log; + +/** + * This class provides a set of standard methods to handle the selection on the items of an Adapter. + * + * @author Davide Steduto + */ +public abstract class SelectableAdapter extends RecyclerView.Adapter { + + private static final String TAG = SelectableAdapter.class.getSimpleName(); + + private ArrayList selectedItems; + + public SelectableAdapter() { + selectedItems = new ArrayList(); + } + + /** + * Indicates if the item at position position is selected + * @param position Position of the item to check + * @return true if the item is selected, false otherwise + */ + protected boolean isSelected(int position) { + return selectedItems.contains(Integer.valueOf(position)); + } + + /** + * Toggle the selection status of the item at a given position + * @param position Position of the item to toggle the selection status for + */ + public void toggleSelection(int position) { + Integer positionTapped = Integer.valueOf(position); + int index = selectedItems.indexOf(positionTapped); + if (index != -1) { + Log.d(TAG, "toggleSelection removing selection on position "+position); + selectedItems.remove(index); + } else { + Log.d(TAG, "toggleSelection adding selection on position "+position); + selectedItems.add(positionTapped); + } + Log.d(TAG, "toggleSelection notifyItemChanged on position "+position); + notifyItemChanged(position); + Log.d(TAG, "toggleSelection current selection "+selectedItems); + } + + /** + * This method remove the selection if at the specified + * position the item was previously selected. + * Note: notifyItemChanged on the position is NOT called! + * This is useful when an item is mainly removed from the + * implementation of the Adapter + * @param position + */ + protected void removeSelection(int position) { + Log.d(TAG, "removeSelection on position "+position); + int index = selectedItems.indexOf(Integer.valueOf(position)); + if (index != -1) selectedItems.remove(index); + //The notification is made outside this method + //notifyItemChanged(position); + } + + /** + * Add the selection status for all items. + * The selector container is sequentially filled with All items positions. + */ + public void selectAll() { + Log.d(TAG, "selectAll"); + selectedItems = new ArrayList(getItemCount()); + for (int i = 0; i < getItemCount(); i++) { + selectedItems.add(Integer.valueOf(i)); + Log.d(TAG, "selectAll notifyItemChanged on position "+i); + notifyItemChanged(i); + } + //TODO: Not sure about this call notifyDataSetChanged() when ALL items needs to be refreshed. + //notifyDataSetChanged(); + } + + /** + * Clear the selection status for all items. + * If Adapter size match with the size of the selection list, + * then fast clear is performed. + */ + public void clearSelection() { + if (selectedItems.size() == getItemCount()) { + selectedItems.clear(); + //TODO: Not sure about this call notifyDataSetChanged() when ALL items needs to be refreshed. + Log.d(TAG, "clearSelection notifyDataSetChanged on all position"); + notifyDataSetChanged(); + } else { + Iterator iterator = selectedItems.iterator(); + while (iterator.hasNext()) { + //The notification is done only on items that are currently selected. + //Clearing all items on selectedItems, we loose the position to notify. + //TODO: To verify: Clearing all after the notification actually it does any effect because + // the position called rely on the selectionItems that still contains that position + int i = iterator.next(); + iterator.remove(); + Log.d(TAG, "clearSelection notifyItemChanged on position "+i); + notifyItemChanged(i); + } + } + } + + /** + * Count the selected items + * @return Selected items count + */ + public int getSelectedItemCount() { + return selectedItems.size(); + } + + /** + * Indicates the list of selected items + * @return List of selected items ids + */ + public List getSelectedItems() { + return selectedItems; + } + + /** + * Save the state of the current selection on the items. + * @param outState + */ + public void onSaveInstanceState(Bundle outState) { + outState.putIntegerArrayList(TAG, selectedItems); + } + + /** + * Restore the previous state of the selection on the items. + * @param savedInstanceState + */ + public void onRestoreInstanceState(Bundle savedInstanceState) { + selectedItems = savedInstanceState.getIntegerArrayList(TAG); + } + +} \ No newline at end of file