From 8bad310b87ee250968e825e9f6b3b4209dfabbae Mon Sep 17 00:00:00 2001 From: Davide Steduto Date: Sun, 1 Nov 2015 16:42:20 +0100 Subject: [PATCH] v4.0.1 Configuration for JCenter #9. Refactored module names, package signatures and gradle build files. --- README.md | 1 + .../eu/davidea/common/FlexibleAdapter.java | 401 ------------------ .../eu/davidea/common/SelectableAdapter.java | 236 ----------- 3 files changed, 1 insertion(+), 637 deletions(-) delete mode 100644 flexibleAdapter/src/main/java/eu/davidea/common/FlexibleAdapter.java delete mode 100644 flexibleAdapter/src/main/java/eu/davidea/common/SelectableAdapter.java diff --git a/README.md b/README.md index c59b4515..d33c29f3 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ public class MainActivity extends AppCompatActivity { - Refactored module names, package signatures and gradle build files. Code remains unchanged. - Configuration for JCenter, now FlexibleAdapter is a lightweight standalone library! **Note:** FastScroller and ItemAnimators are excluded from the library, but you can see them in the example App. +- New icon. ###### v4.0 - 2015.10.18 - Added **FilterAsyncTask** to asynchronously load the list (This might not work well and binding is excluded from Async). diff --git a/flexibleAdapter/src/main/java/eu/davidea/common/FlexibleAdapter.java b/flexibleAdapter/src/main/java/eu/davidea/common/FlexibleAdapter.java deleted file mode 100644 index a5da3888..00000000 --- a/flexibleAdapter/src/main/java/eu/davidea/common/FlexibleAdapter.java +++ /dev/null @@ -1,401 +0,0 @@ -package eu.davidea.common; - -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.support.v7.widget.RecyclerView; -import android.util.Log; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; - -/** - * 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(); - public static final long UNDO_TIMEOUT = 5000L; - - public interface OnUpdateListener { - void onLoadComplete(); - //void onProgressUpdate(int progress); - } - - /** - * Lock used to modify the content of {@link #mItems}. Any write operation performed on the array should be - * synchronized on this lock. - */ - private final Object mLock = new Object(); - - protected List mItems; - protected List mDeletedItems; - protected List mOriginalPosition; - //Searchable fields - protected static String mSearchText; //Static: It can exist only 1 searchText - protected OnUpdateListener mUpdateListener; - protected Handler mHandler; - - public FlexibleAdapter() { - } - - /** - * Constructor for Asynchronous loading.
- * Experimental: not working very well, it might be slow. - * - * @param listener {@link OnUpdateListener} - */ - public FlexibleAdapter(Object listener) { - if (listener instanceof OnUpdateListener) - this.mUpdateListener = (OnUpdateListener) listener; - else - Log.w(TAG, "Listener is not an instance of OnUpdateListener!"); - } - - /** - * Convenience method to call {@link #updateDataSet(String)} with {@link null} as param. - */ - public void updateDataSet() { - updateDataSet(null); - }; - /** - * This method will refresh the entire DataSet content.
- * The parameter is useful to filter the DataSet. It can be removed - * or the type can be changed accordingly (String is the most used value).
- * Pass null value in case not used. - * - * @param param A custom parameter to filter the DataSet - */ - public abstract void updateDataSet(String param); - - /** - * This method execute {@link #updateDataSet(String)} asynchronously - * with {@link FilterAsyncTask}.

- * Note: {@link #notifyDataSetChanged()} is automatically called at the end of the process. - * - * @param param A custom parameter to filter the DataSet - */ - public void updateDataSetAsync(String param) { - if (mUpdateListener == null) { - Log.w(TAG, "OnUpdateListener is not initialized. UpdateDataSet is not using FilterAsyncTask!"); - updateDataSet(param); - return; - } - new FilterAsyncTask().execute(param); - } - - /** - * Returns the custom object "Item". - * - * @param position The position of the item in the list - * @return The custom "Item" object or null if item not found - */ - public T getItem(int position) { - if (position < 0 || position >= mItems.size()) return null; - return mItems.get(position); - } - - /** - * Retrieve the position of the Item in the Adapter - * - * @param item The item - * @return The position in the Adapter if found, -1 otherwise - */ - public int getPositionForItem(T item) { - return mItems != null && mItems.size() > 0 ? mItems.indexOf(item) : -1; - } - - public boolean contains(T item) { - return mItems != null && mItems.contains(item); - } - - @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 != null ? mItems.size() : 0; - } - - public void updateItem(int position, T item) { - if (position < 0) return; - synchronized (mLock) { - mItems.set(position, item); - } - Log.d(TAG, "updateItem notifyItemChanged on position "+position); - notifyItemChanged(position); - } - - /** - * Insert given Item at position or Add Item at last position. - * - * @param position Position of the item to add - * @param item The item to add - */ - public void addItem(int position, T item) { - if (position < 0) return; - - //Insert Item - if (position < mItems.size()) { - Log.d(TAG, "addItem notifyItemInserted on position " + position); - synchronized (mLock) { - mItems.add(position, item); - } - - //Add Item at the last position - } else { - Log.d(TAG, "addItem notifyItemInserted on last position"); - synchronized (mLock) { - mItems.add(item); - position = mItems.size(); - } - } - - notifyItemInserted(position); - } - - /* DELETE ITEMS METHODS */ - - /** - * The item is retained in a list for an eventual Undo. - * - * @param position The position of item to remove - * @see #startUndoTimer() - * @see #restoreDeletedItems() - * @see #emptyBin() - */ - public void removeItem(int position) { - if (position < 0) return; - if (position < mItems.size()) { - Log.d(TAG, "removeItem notifyItemRemoved on position " + position); - synchronized (mLock) { - saveDeletedItem(position, mItems.remove(position)); - } - notifyItemRemoved(position); - } else { - Log.w(TAG, "removeItem WARNING! Position OutOfBound! Review the position to remove!"); - } - } - - /** - * Every item is retained in a list for an eventual Undo. - * - * @param selectedPositions List of item positions to remove - * @see #startUndoTimer() - * @see #restoreDeletedItems() - * @see #emptyBin() - */ - public void removeItems(List selectedPositions) { - Log.d(TAG, "removeItems reverse 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) { - synchronized (mLock) { - saveDeletedItem(positionStart, mItems.remove(positionStart)); - } - } - Log.d(TAG, "removeRange notifyItemRangeRemoved"); - notifyItemRangeRemoved(positionStart, itemCount); - } - - /* UNDO METHODS */ - - /** - * Save temporary Items for an eventual Undo. - * - * @param position The position of the item to retain. - */ - public void saveDeletedItem(int position, T item) { - if (mDeletedItems == null) { - mDeletedItems = new ArrayList(); - mOriginalPosition = new ArrayList(); - } - Log.d(TAG, "Recycled "+getItem(position)+" on position="+position); - mDeletedItems.add(item); - mOriginalPosition.add(position); - } - - /** - * @return The list of deleted items - */ - public List getDeletedItems() { - return mDeletedItems; - } - - public boolean isRestoreInTime() { - return mDeletedItems != null && mDeletedItems.size() > 0; - } - - /** - * Restore items just removed. - */ - public void restoreDeletedItems() { - stopUndoTimer(); - //Reverse insert (list was reverse ordered on Delete) - for (int i = mOriginalPosition.size()-1; i >= 0; i--) { - addItem(mOriginalPosition.get(i), mDeletedItems.get(i)); - } - emptyBin(); - } - - /** - * Clean memory from items just removed.
- * Note: This method is automatically called after timer is over and after a restoration. - */ - public void emptyBin() { - if (mDeletedItems != null) { - mDeletedItems.clear(); - mOriginalPosition.clear(); - } - } - - /** - * Convenience method to start Undo timer with default timeout of 5'' - */ - public void startUndoTimer() { - startUndoTimer(0); - } - - /** - * Start Undo timer with custom timeout - * - * @param timeout Custom timeout - */ - public void startUndoTimer(long timeout) { - mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() { - public boolean handleMessage(Message message) { - emptyBin(); - return true; - } - }); - mHandler.sendMessageDelayed(Message.obtain(mHandler), timeout > 0 ? timeout : UNDO_TIMEOUT); - } - - /** - * Stop Undo timer. - *
Note: This method is automatically called in case of restoration. - */ - private void stopUndoTimer() { - if (mHandler != null) { - mHandler.removeCallbacksAndMessages(null); - mHandler = null; - } - } - - public static boolean hasSearchText() { - return mSearchText != null && mSearchText.length() > 0; - } - - public static String getSearchText() { - return mSearchText; - } - - public static void setSearchText(String searchText) { - if (searchText != null) - mSearchText = searchText.trim().toLowerCase(Locale.getDefault()); - else mSearchText = ""; - } - - /** - * DEFAULT IMPLEMENTATION, OVERRIDE TO HAVE OWN FILTER! - * - *

- * Performs filtering on the provided object and returns true, if the object should be in the filtered collection, - * or false if it shouldn't. - * - * @param myObject The object to be inspected - * @param constraint Constraint, that the object has to fulfil - * @return true, if the object should be in the filteredResult, false otherwise - */ - protected boolean filterObject(T myObject, String constraint) { - final String valueText = myObject.toString().toLowerCase(); - - //First match against the whole, non-splitted value - if (valueText.startsWith(constraint)) { - return true; - } else { - final String[] words = valueText.split(" "); - - //Start at index 0, in case valueText starts with space(s) - for (String word : words) { - if (word.startsWith(constraint)) { - return true; - } - } - } - //No match, so don't add to collection - return false; - } - - public class FilterAsyncTask extends AsyncTask { - - private final String TAG = FilterAsyncTask.class.getSimpleName(); - - @Override - protected Void doInBackground(String... params) { - Log.i(TAG, "doInBackground - started FilterAsyncTask!"); - updateDataSet(params[0]); - Log.i(TAG, "doInBackground - ended FilterAsyncTask!"); - return null; - } - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - mUpdateListener.onLoadComplete(); - notifyDataSetChanged(); - } - } - -} \ No newline at end of file diff --git a/flexibleAdapter/src/main/java/eu/davidea/common/SelectableAdapter.java b/flexibleAdapter/src/main/java/eu/davidea/common/SelectableAdapter.java deleted file mode 100644 index f1e6ac2d..00000000 --- a/flexibleAdapter/src/main/java/eu/davidea/common/SelectableAdapter.java +++ /dev/null @@ -1,236 +0,0 @@ -package eu.davidea.common; - -import android.os.Bundle; -import android.support.v7.widget.RecyclerView; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * 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(); - /** - * Default mode for selection - */ - public static final int MODE_SINGLE = 1; - /** - * Multi selection will be activated - */ - public static final int MODE_MULTI = 2; - - private ArrayList selectedItems; - private int mode; - - public SelectableAdapter() { - this.selectedItems = new ArrayList(); - this.mode = MODE_SINGLE; - } - - /** - * Set the mode of the selection, MODE_SINGLE is the default: - *
    - *
  • if {@link #MODE_SINGLE}, it will switch the selection position (previous selection is cleared automatically); - *
  • if {@link #MODE_MULTI}, it will add the position to the list of the items selected. - *
- * NOTE: #mModeMultiJustFinished is set true when #MODE_MULTI is finished. - * - * @param mode MODE_SINGLE or MODE_MULTI - */ - public void setMode(int mode) { - this.mode = mode; - } - - /** - * The current selection mode of the Adapter. - * - * @return current mode - * @see #MODE_SINGLE - * @see #MODE_MULTI - */ - public int getMode() { - return mode; - } - - /** - * 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. - */ - public boolean isSelected(int position) { - return selectedItems.contains(Integer.valueOf(position)); - } - - /** - * Convenience method to never invalidate the Item. - * - * @param position Position of the item to toggle the selection status for. - * @see #toggleSelection(int, boolean) - */ - public void toggleSelection(int position) { - toggleSelection(position, false); - } - - /** - * Toggle the selection status of the item at a given position.
- * The behaviour depends on the selection mode previously set with {@link #setMode}. - * - *

- * Optionally the item can be invalidated.
- * However it is preferable to set false and to handle the Activated/Selected State of - * the ItemView in the Click events of the ViewHolder after the selection is registered and - * up to date: Very Useful if the item has views with own animation to perform! - * - *

- * Usage: - *
    - *
  • If you don't want any item to be selected/activated at all, just don't call this method.
  • - *
  • To have actually the item visually selected you need to add a custom Selector Drawable to your layout/view of the Item. - * It's preferable: android:background="?attr/selectableItemBackground" in your layout, pointing to a custom Drawable in the style.xml - * (note: prefix ?android:attr seems to not work).
  • - *
  • In onClick, enable the Activated/Selected State of the ItemView of the ViewHolder after the listener consumed the event: - * itemView.setActivated(mAdapter.isSelected(getAdapterPosition()));
  • - *
  • In onBindViewHolder, adjust the selection status: holder.itemView.setActivated(isSelected(position));
  • - *
  • If invalidate is set true, {@link #notifyItemChanged} is called and {@link #onBindViewHolder} will be automatically called - * afterwards overriding any animation in the ItemView!
  • - *
T - * - * @param position Position of the item to toggle the selection status for. - * @param invalidate Boolean to indicate if the row must be invalidated and item rebinded. - */ - public void toggleSelection(int position, boolean invalidate) { - if (position < 0) return; - if (mode == MODE_SINGLE) clearSelection(); - - int index = selectedItems.indexOf(position); - 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(position); - } - if (invalidate) { - Log.d(TAG, "toggleSelection notifyItemChanged on position "+position); - notifyItemChanged(position); - } - Log.d(TAG, "toggleSelection current selection " + selectedItems); - } - - /** - * Deprecated! Reasons: - *
- Use {@link #toggleSelection} for normal situation instead. - *
- Use {@link #getSelectedItems}.iterator() to avoid java.util.ConcurrentModificationException. - * - *

- * This method is used only after a removal of an Item and useful only in certain - * situations such when not all selected Items can be removed (business exceptions dependencies) - * while others still remains selected. - *
For normal situations use {@link #toggleSelection} instead. - * - *

- * Remove the selection if at the specified - * position the item was previously selected. - * - *

- * Note: notifyItemChanged on the position is NOT called to avoid double call - * when removeItems! - * - * @param position - */ - @Deprecated - protected void removeSelection(int position) { - if (position < 0) return; - Log.d(TAG, "removeSelection on position "+position); - int index = selectedItems.indexOf(Integer.valueOf(position)); - if (index != -1) selectedItems.remove(index); - //Avoid double notification: - //Usually the notification is made in FlexibleAdapter.removeItem(); - //notifyItemChanged(position); - } - - /** - * Convenience method when there is nothing to skip. - */ - public void selectAll() { - selectAll(-1); - } - /** - * Add the selection status for all items. - * The selector container is sequentially filled with All items positions. - *
Note: All items are invalidated and rebinded! - * - * @param skipViewType ViewType for which we don't want selection - */ - public void selectAll(int skipViewType) { - Log.d(TAG, "selectAll"); - selectedItems = new ArrayList(getItemCount()); - for (int i = 0; i < getItemCount(); i++) { - if (getItemViewType(i) == skipViewType) continue; - selectedItems.add(i); - Log.d(TAG, "selectAll notifyItemChanged on position "+i); - notifyItemChanged(i); - } - } - - /** - * Clear the selection status for all items one by one to not kill animations in the items. - *

- * Note 1: Items are invalidated and rebinded!
- * Note 2: This method use java.util.Iterator to avoid java.util.ConcurrentModificationException. - */ - public void clearSelection() { - Iterator iterator = selectedItems.iterator(); - while (iterator.hasNext()) { - //The notification is done only on items that are currently selected. - 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