From 55f9db2d204ab35719acf671c2578513f2bd419d Mon Sep 17 00:00:00 2001
From: Davide Steduto
0 = updateDataSet.
*
1 = filterItems, optionally delayed.
*
2 = deleteConfirmed when Undo timeout is over.
- *
8 = remove the progress item from the list, optionally delayed.
- *
9 = reset flag to load more items, delayed.
Note: text is always trimmed and to lowercase.
* * @param searchText the new text to filter the items * @since 3.1.0 @@ -3082,7 +3083,7 @@ public void setSearchText(String searchText) { *If the items have highlighted text, those items must be refreshed in order to change the * text back to normal. This happens systematically when searchText is reduced in length by * the user.
- * The notification is triggered in {@link #animateTo(List)} when new items are not added. + * The notification is triggered in {@link #animateTo(List, Payload)} when new items are not added. *Default value is {@code false}.
* * @param notifyChange true to trigger {@link #notifyItemChanged(int)} while filtering, @@ -3145,8 +3146,8 @@ public void filterItems(@NonNull ListHelper will be reinitialized.
+ * + * @param itemTouchHelperCallback the custom callback implementation for Item touch + * @return this Adapter, so the call can be chained + * @since 5.0.0-rc1 + */ + public final FlexibleAdapter setItemTouchHelperCallback(ItemTouchHelperCallback itemTouchHelperCallback) { + mItemTouchHelperCallback = itemTouchHelperCallback; + mItemTouchHelper = null; + initializeItemTouchHelper(); + return this; + } + /** * Returns whether ItemTouchHelper should start a drag and drop operation if an item is * long pressed.
@@ -3683,9 +3698,9 @@ public void swapItems(List Useful in conjunction with ViewPager. Default value is {@link #minCollapsibleLevel} (All levels including 0). Default value is {@link #mMinCollapsibleLevel} (All levels including 0). Used by filter and updateDataSet. Note: In case the animations are performed, unchanged items will be notified if
* {@code notifyChangeOfUnfilteredItems} is set true, and payload will be set as a Boolean.
Holder item can be used to display the same modelData in multiple RecyclerViews managed by + * different Adapters, you can implement a derived IFlexible item to HOLD your data model object!
+ * + * In this way you can separate the memory zones of the flags (enabled, expanded, hidden, + * selectable) used by a specific Adapter, to be independent by another Adapter. For instance, + * an item can be Shown and Expanded in a RV, while in the other RV can be Hidden or Not Expanded! + * + * @author Davide Steduto + * @since 19/10/2016 + */ +public class ItemHolder extends AbstractSectionableItemIn this way you can separate the memory zones of the flags (enabled, expanded, hidden, selectable, - * draggable, swipeable, etc...) used by an Adapter, to be independent by another Adapter. - * For instance an item can be Shown and Expanded in a RV, while in the other RV can be Hidden or - * Not Expanded!
- */ -public class FlexibleItemHolderExampleA time, to compare the 2 different approaches, is calculated and displayed in the log. + * To see the logs call {@link #enableLogs(boolean)} before creating the Adapter instance!
+ * Default value is {@code false} (default calculation is used). + * + * @param useDiffUtil true to switch the calculation and use DiffUtil, false to use the default + * calculation. + * @return this Adapter, so the call can be chained + * @see #setDiffUtilCallback(DiffUtilCallback) + */ + public FlexibleAdapter setAnimateChangesWithDiffUtil(boolean useDiffUtil) { + this.useDiffUtil = useDiffUtil; + return this; + } + + /** + * Sets a custom implementation of {@link DiffUtilCallback} for the DiffUtil. Extend to + * implement the comparing methods. + * + * @param diffUtilCallback the custom callback that DiffUtil will call + * @return this Adapter, so the call can be chained + * @see #setAnimateChangesWithDiffUtil(boolean) + */ + public FlexibleAdapter setDiffUtilCallback(DiffUtilCallback diffUtilCallback) { + this.diffUtilCallback = diffUtilCallback; + return this; + } + + private synchronized void animateDiff(@Nullable ListUsed by filter and updateDataSet.
* Note: The animations are skipped in favor of {@link #notifyDataSetChanged()} * when the number of items reaches the limit. See {@link #setAnimateToLimit(int)}. *Note: In case the animations are performed, unchanged items will be notified if - * {@code notifyChangeOfUnfilteredItems} is set true, and payload will be set as a Boolean.
+ * {@code notifyChangeOfUnfilteredItems} is set true, and CHANGE payload will be set. * * @param newItems the new list containing the new items * @see #setNotifyChangeOfUnfilteredItems(boolean) @@ -3454,23 +3517,28 @@ private synchronized void animateTo(@Nullable ListHelper will be reinitialized.
+ *If called, Helper will be reinitialized.
+ * If not called, the default Helper will be used. * * @param itemTouchHelperCallback the custom callback implementation for Item touch * @return this Adapter, so the call can be chained @@ -3604,6 +3706,7 @@ public final FlexibleAdapter setItemTouchHelperCallback(ItemTouchHelperCallback * * @return true if ItemTouchHelper should start dragging an item when it is long pressed, * false otherwise. Default value is {@code false}. + * @see #setLongPressDragEnabled(boolean) * @since 5.0.0-b1 */ public final boolean isLongPressDragEnabled() { @@ -3629,11 +3732,14 @@ public final FlexibleAdapter setLongPressDragEnabled(boolean longPressDragEnable } /** - * Enabled by default. - *To use, it is sufficient to set the HandleView by calling - * {@link FlexibleViewHolder#setDragHandleView(View)}.
+ * Returns whether ItemTouchHelper should start a drag and drop operation by touching its + * handle. + *Default value is {@code false}.
+ * To use, it is sufficient to set the HandleView by calling + * {@link FlexibleViewHolder#setDragHandleView(View)}. * * @return true if active, false otherwise + * @see #setHandleDragEnabled(boolean) * @since 5.0.0-b1 */ public final boolean isHandleDragEnabled() { @@ -3662,6 +3768,7 @@ public final FlexibleAdapter setHandleDragEnabled(boolean handleDragEnabled) { * * @return true if ItemTouchHelper should start swiping an item when user swipes a pointer * over the View, false otherwise. Default value is {@code false}. + * @see #setSwipeEnabled(boolean) * @since 5.0.0-b1 */ public final boolean isSwipeEnabled() { @@ -3889,20 +3996,6 @@ public void onItemSwiped(int position, int direction) { } } - private void initializeItemTouchHelper() { - if (mItemTouchHelper == null) { - if (mRecyclerView == null) { - throw new IllegalStateException("RecyclerView cannot be null. Enabling LongPressDrag or Swipe must be done after the Adapter is added to the RecyclerView."); - } - if (mItemTouchHelperCallback == null) { - if (DEBUG) Log.i(TAG, "Initialize default ItemTouchHelperCallback"); - mItemTouchHelperCallback = new ItemTouchHelperCallback(this); - } - mItemTouchHelper = new ItemTouchHelper(mItemTouchHelperCallback); - mItemTouchHelper.attachToRecyclerView(mRecyclerView); - } - } - /*------------------------*/ /* OTHERS PRIVATE METHODS */ /*------------------------*/ @@ -4419,10 +4512,12 @@ protected void onPreExecute() { @Override protected Void doInBackground(Void... params) { + start = System.currentTimeMillis(); switch (what) { case UPDATE: if (DEBUG) Log.d(TAG, "doInBackground - started UPDATE"); - animateTo(newItems, Payload.CHANGE); + animateDiff(newItems, Payload.CHANGE); + //animateTo(newItems, Payload.CHANGE); if (DEBUG) Log.d(TAG, "doInBackground - ended UPDATE"); break; case FILTER: @@ -4436,7 +4531,7 @@ protected Void doInBackground(Void... params) { @Override protected void onPostExecute(Void result) { - if (mNotifications != null) { + if (diffResult != null || mNotifications != null) { //Execute post data switch (what) { case UPDATE: @@ -4512,4 +4607,97 @@ protected void onPostFilter() { //Dedicated for user implementation } + /** + * The following Lists are available as: + *- {@code protected List oldItems;}
+ *
+ * For example, if your items have unique ids, this method should check their id equality.
+ *
+ * @param oldItemPosition The position of the item in the old list
+ * @param newItemPosition The position of the item in the new list
+ * @return True if the two items represent the same object or false if they are different.
+ */
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+// if (DEBUG) Log.w(TAG, "oldItemPosition=" + oldItemPosition + ", newItemPosition=" + newItemPosition);
+ T oldItem = oldItems.get(oldItemPosition);
+ T newItem = newItems.get(newItemPosition);
+ return oldItem.equals(newItem);
+ }
+
+ /**
+ * Called by the DiffUtil when it wants to check whether two items have the same data.
+ * DiffUtil uses this information to detect if the contents of an item has changed.
+ *
+ * DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
+ * so that you can change its behavior depending on your UI.
+ * For example, if you are using DiffUtil with a
+ * {@link RecyclerView.Adapter RecyclerView.Adapter}, you should
+ * return whether the items' visual representations are the same.
+ *
+ * This method is called only if {@link #areItemsTheSame(int, int)} returns
+ * {@code true} for these items.
+ *
+ * @param oldItemPosition The position of the item in the old list
+ * @param newItemPosition The position of the item in the new list which replaces the
+ * oldItem
+ * @return True if the contents of the items are the same or false if they are different.
+ */
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ return false;
+ }
+
+ /**
+ * When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and
+ * {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil
+ * calls this method to get a payload about the change.
+ *
+ * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
+ * particular field that changed in the item and your
+ * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
+ * information to run the correct animation.
+ *
+ * Default implementation returns {@code null}.
+ *
+ * @param oldItemPosition The position of the item in the old list
+ * @param newItemPosition The position of the item in the new list
+ *
+ * @return A payload object that represents the change between the two items.
+ */
+ @Nullable
+ @Override
+ public Object getChangePayload(int oldItemPosition, int newItemPosition) {
+ return Payload.CHANGE;
+ }
+ }
+
}
\ No newline at end of file
From 1d3fec3a4c310bf06f12a9786adc61da0ffd671d Mon Sep 17 00:00:00 2001
From: Davide Steduto
+ * This handler can launch asynchronous tasks and if you catch the reserved "what",
+ * keep in mind that this code should be executed before that task has been completed.
+ * Note: numbers 0-9 are reserved for the Adapter, use others for new values. This class provides a set of standard methods to handle changes on the data set such as
- * filtering, adding, removing, moving and animating an item. NOTE: This Adapter supports multi level of Expandable, but do not enable Drag&Drop.
- * Something might not work as expected, so better to change approach in favor of a clearer
- * design/layout: Open the sub list in a new Activity/Fragment...
- *
+ * With version 5.0.0, T item must implement {@link IFlexible} item interface as base item
+ * for all view types and new methods have been added in order to:
+ * You can use and override this Handler, but you must keep the "What" by calling super():
- * The listener must be a single instance of a class, usually Activity or Fragment,
- * where you can implement how to handle the different events. PASS ALWAYS A COPY OF THE ORIGINAL LIST: new ArrayList<T>(originalList); The listener must be a single instance of a class, usually Activity or
+ * Fragment, where you can implement how to handle the different events. PROVIDE ALWAYS A COPY OF THE ORIGINAL LIST:
+ * {@code new ArrayList Note: Setting true allows the RecyclerView to rebind only items really changed
- * after a refresh or after swapping Adapter. This increase performance, you loose scrolling
- * animations. This method should be called during the creation of the Activity/Fragment, useful also
* after a screen rotation.
- *
- {@code protected List newItems;}
+ */
+ public static class DiffUtilCallback
30/01/2016 Class now extends {@link SelectableAdapter}
+ *
13/09/2016 {@link #animateView(RecyclerView.ViewHolder, int)} is now automatically called
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public abstract class AnimatorAdapter extends SelectableAdapter {
diff --git a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java
index d3c85eaa..43dcc26a 100644
--- a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java
+++ b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java
@@ -61,19 +61,18 @@
import eu.davidea.viewholders.FlexibleViewHolder;
/**
- * This class is backed by an ArrayList of arbitrary objects of T, where T is
- * your Model object containing the data, with version 5.0.0 it must implement {@link IFlexible}
- * interface. Read on
- * Github for more details.
- *
Instead, this extra level of expansion is useful in situations where information is in
- * read only mode or with action buttons.
+ *
*
* @author Davide Steduto
* @see AnimatorAdapter
@@ -90,7 +89,7 @@
*
10/02/2016 The class is not abstract anymore, it is ready to be used
*
20/02/2016 Sticky headers
*
22/04/2016 Endless Scrolling
- *
09/07/2016 FilterAsyncTask (performance on big list)
+ *
13/07/2016 Update and Filter operations are executed asynchronously (high performance on big list)
*/
@SuppressWarnings({"Range", "unused", "unchecked", "ConstantConditions", "SuspiciousMethodCalls", "WeakerAccess"})
public class FlexibleAdapter
0 = updateDataSet.
- *
1 = filterItems, optionally delayed.
- *
2 = deleteConfirmed when Undo timeout is over.
- *
8 = remove the progress item from the list, optionally delayed.
It is also called after DataSet is updated.
It is also called after the data set is updated.
When an item is disabled, user cannot interact with it.
* * @param position the current position of the item to check * @return true if the item property enabled is set true, false otherwise @@ -396,7 +366,7 @@ public boolean isSelectable(int position) { /** * {@inheritDoc} * - * @param position Position of the item to toggle the selection status for. + * @param position position of the item to toggle the selection status for. * @since 5.0.0-b1 */ @Override @@ -433,13 +403,13 @@ public void toggleSelection(@IntRange(from = 0) int position) { *Examples:
*
- if user initially selects an expandable of type A, then only expandable items of
* type A will be selected.
- *
- if user initially selects a non-expandable of type B, then only items of Type B
+ *
- if user initially selects a non-expandable of type B, then only items of type B
* will be selected.
*
- The developer can override this behaviour by passing a list of viewTypes for which
* he wants to force the selection.
In this case changes will NOT be animated: {@link #notifyDataSetChanged()} will be invoked.
+ *In this case changes will NOT be animated: Warning! + * {@link #notifyDataSetChanged()} will be invoked.
* * @param items the new data set * @see #updateDataSet(List, boolean) @@ -497,16 +468,19 @@ public void updateDataSet(ListOptionally all changes can be animated, limited by the value previously set with - * {@link #setAnimateToLimit(int)} to improve performance on big list.
- * Pass {@code animate = false} to directly invoke {@link #notifyDataSetChanged()} - * without any animations. - *Note: This methods calls {@link #expandItemsAtStartUp()} and - * {@link #showAllHeaders()} if headers are shown.
+ * This method will refresh the entire data set content. Optionally, all changes can be + * animated, limited by the value previously set with {@link #setAnimateToLimit(int)} + * to improve performance on very big list. Should provide {@code animate = false} to + * directly invoke {@link #notifyDataSetChanged()} without any animations! + *+ * Note: The following methods will be also called at the end of the operation: + *
This cannot be overridden since the entire library relies on it.
+ * Returns the object of type T. + *This method cannot be overridden since the entire library relies on it.
* * @param position the position of the item in the list - * @return The custom "Item" object or null if item not found + * @return The T object for the position provided or null if item not found * @since 1.0.0 */ public final T getItem(@IntRange(from = 0) int position) { @@ -550,7 +524,7 @@ public long getItemId(int position) { } /** - * This cannot be overridden since the selection relies on it. + * This method cannot be overridden since the selection relies on it. * * @return the total number of the items currently displayed by the adapter * @see #getItemCountOfTypes(Integer...) @@ -593,9 +567,7 @@ public int getItemCountOfTypesUntil(@IntRange(from = 0) int position, Integer... ListNote: The Comparator should be customized to support all the types of items - * this Adapter is displaying or a ClassCastException will be raised.
- * If Comparator is {@code null} the returned position is 0. + *Note: The {@code Comparator} object should be customized to support all + * types of items this Adapter is managing or a {@code ClassCastException} will be raised.
+ * If the {@code Comparator} is {@code null} the returned position is 0 (first position). * * @param item the item to evaluate the insertion * @param comparator the Comparator object with the logic to sort the list @@ -712,11 +684,11 @@ public FlexibleAdapter setRemoveOrphanHeaders(boolean removeOrphanHeaders) { } /** - * Setting to automatically unlink the just deleted header from items having that header linked. + * Setting to automatically unlink the deleted header from items having that header linked. *Default value is {@code false}.
* - * @param unlinkOnRemoveHeader true to unlink also all items with the just deleted header, - * false otherwise + * @param unlinkOnRemoveHeader true to unlink the deleted header from items having that header + * linked, false otherwise * @return this Adapter, so the call can be chained * @since 5.0.0-b6 */ @@ -793,7 +765,7 @@ public ListDefault value is {@code false} (headers are NOT shown at startup).
+ *Default value is {@code false} (headers are not shown at startup).
* * @param displayHeaders true to display headers, false to keep them hidden * @return this Adapter, so the call can be chained @@ -1060,8 +1033,8 @@ public FlexibleAdapter setDisplayHeadersAtStartUp(boolean displayHeaders) { /** * Shows all headers in the RecyclerView at their linked position. Not intended to be called at - * startup.Note: Headers can only be shown or hidden all together.
* * @see #hideAllHeaders() @@ -1331,13 +1304,13 @@ private boolean isHeaderShared(IHeader header, int positionStart, int itemCount) /*---------------------*/ /** - * Returns the ViewType for all Items depends by the current position. + * Returns the ViewType for all Items depends by the provided position. *You can override this method to return specific values (don't call super) or you can * let this method to call the implementation of {@code IFlexible#getLayoutRes()} so ViewTypes * are automatically mapped (AutoMap).
* * @param position position for which ViewType is requested - * @return if Item is found, any integer value from user layout resource if defined in + * @return if item is found, any integer value from user layout resource if defined in * {@code IFlexible#getLayoutRes()} * @since 5.0.0-b1 */ @@ -1353,7 +1326,7 @@ public int getItemViewType(int position) { /** * You can override this method to create ViewHolder from inside the Adapter or you can let * this method to call the implementation of {@code IFlexible#createViewHolder()} to create - * ViewHolder from inside the Item (AutoMap). + * ViewHolder from inside the item (AutoMap). * {@inheritDoc} * * @return a new ViewHolder that holds a View of the given view type @@ -1504,8 +1477,8 @@ public FlexibleAdapter setEndlessScrollThreshold(@IntRange(from = 1) int thresho /** * This method is called automatically if METHOD A is implemented. If instead you chose the - * classic way (METHOD B) to bind the items, you have to manually call this method at the end - * of {@code onBindViewHolder()}. + * classic way (METHOD B) to bind the items, you have to manually call this method + * at the end of {@code onBindViewHolder()}. * * @param position the current binding position */ @@ -1606,7 +1579,7 @@ private void noMoreLoad() { /*--------------------*/ /** - * @return true if autoCollapseOnExpand is enabled, false otherwise + * @return true if {@code collapseOnExpand} is enabled, false otherwise * @since 5.0.0-b8 */ public boolean isAutoCollapseOnExpand() { @@ -1614,8 +1587,8 @@ public boolean isAutoCollapseOnExpand() { } /** - * Automatically collapse all previous expanded parents before expand the clicked parent. - *Default value is disabled.
+ * Automatically collapse all previous expanded parents before expand the new clicked parent. + *Default value is {@code false} (disabled).
* * @param collapseOnExpand true to collapse others items, false to just expand the current * @return this Adapter, so the call can be chained @@ -1628,7 +1601,7 @@ public FlexibleAdapter setAutoCollapseOnExpand(boolean collapseOnExpand) { } /** - * @return true if autoScrollOnExpand is enabled, false otherwise + * @return true if {@code scrollOnExpand} is enabled, false otherwise * @since 5.0.0-b8 */ public boolean isAutoScrollOnExpand() { @@ -1637,9 +1610,9 @@ public boolean isAutoScrollOnExpand() { /** * Automatically scroll the clicked expandable item to the first visible position.This works ONLY in combination with {@link SmoothScrollLinearLayoutManager} or with - * {@link SmoothScrollGridLayoutManager}.
+ *Default value is {@code false} (disabled).
+ * This works ONLY in combination with {@link SmoothScrollLinearLayoutManager} or with + * {@link SmoothScrollGridLayoutManager}. * * @param scrollOnExpand true to enable automatic scroll, false to disable * @return this Adapter, so the call can be chained @@ -1685,7 +1658,7 @@ public boolean isExpandable(@NonNull T item) { } /** - * @return the level of the minium collapsible level used in MultiLevel expandable + * @return the level of the minimum collapsible level used in MultiLevel expandable * @since 5.0.0-b6 */ public int getMinCollapsibleLevel() { @@ -1719,7 +1692,7 @@ public boolean hasSubItems(@NonNull IExpandable expandable) { } /** - * Retrieves the parent of a child. + * Retrieves the parent of a child for the provided position. *Only for a real child of an expanded parent.
* * @param position the position of the child item @@ -1772,10 +1745,10 @@ public int getExpandablePositionOf(@NonNull T child) { } /** - * Provides the list where the child currently lays. + * Provides the full sub list where the child currently lays. * * @param child the child item - * @return the list of the child element, or a new list if item + * @return the list of the child element, or an empty list if the child item has no parent * @see #getExpandableOf(IFlexible) * @see #getExpandablePositionOf(IFlexible) * @see #getRelativePositionOf(IFlexible) @@ -1789,11 +1762,11 @@ public ListOnly for a real child of an expanded parent.
* * @param child the child item - * @return the position in the parent or -1 if, child is a parent itself or not found + * @return the position in the parent or -1 if the child is a parent itself or not found * @see #getExpandableOf(IFlexible) * @see #getExpandablePositionOf(IFlexible) * @since 5.0.0-b1 @@ -1876,8 +1849,8 @@ public int expand(T item) { * initially expanded. * WARNING! *Multilevel behaviour: all IExpandable subItem, that are expanded, are recursively + * Collapses an {@code IExpandable} item that is already expanded, if no subItem is selected. + *
Multilevel behaviour: all {@code IExpandable} subItem, that are expanded, are recursively * collapsed.
* * @param position the position of the item to collapse @@ -2085,7 +2058,7 @@ public int collapseAll(int level) { /*----------------*/ /** - * Updates/Rebounds the ItemView corresponding to the current position of that item, with + * Updates/Rebounds the itemView corresponding to the current position of that item, with * the new content provided. * * @param item the item with the new content @@ -2099,7 +2072,7 @@ public void updateItem(@NonNull T item, @Nullable Object payload) { } /** - * Updates/Rebounds the ItemView corresponding to the provided position with the new + * Updates/Rebounds the itemView corresponding to the provided position with the new * provided content. Use {@link #updateItem(IFlexible, Object)} if the new content should * be bound on the same position. * @@ -2112,7 +2085,7 @@ public void updateItem(@NonNull T item, @Nullable Object payload) { */ public void updateItem(@IntRange(from = 0) int position, @NonNull T item, @Nullable Object payload) { - if (position < 0 || position >= mItems.size()) { + if (position < 0 || position >= getItemCount()) { Log.e(TAG, "Cannot updateItem on position out of OutOfBounds!"); return; } @@ -2126,7 +2099,7 @@ public void updateItem(@IntRange(from = 0) int position, @NonNull T item, /*----------------*/ /** - * Inserts the given Item at desired position or Add Item at last position with a delay + * Inserts the given item at desired position or Add item at last position with a delay * and auto-scroll to the position. *Scrolling animation is automatically preserved, meaning that, notification for animation * is ignored.
@@ -2159,20 +2132,33 @@ public void run() { } /** - * Inserts the given item in the internal list at the specified position or Adds the item - * at last position. + * Simply append the provided item to the end of the list. + *Convenience method of {@link #addItem(int, IFlexible)} with + * {@code position = getItemCount()}.
+ * + * @param item the item to add + * @return true if the internal list was successfully modified, false otherwise + */ + public boolean addItem(@NonNull T item) { + return addItem(getItemCount(), item); + } + + /** + * Inserts the given item at the specified position or Adds the item to the end of the list + * (no matters if the new position is out of bounds). * - * @param position position of the item to add + * @param position position of the item to add, if negative, items will be added to the end * @param item the item to add * @return true if the internal list was successfully modified, false otherwise - * @see #addItemWithDelay(int, IFlexible, long, boolean) + * @see #addItem(IFlexible) * @see #addItems(int, List) * @see #addSubItems(int, int, IExpandable, List, boolean, Object) + * @see #addItemWithDelay(int, IFlexible, long, boolean) * @since 1.0.0 */ public boolean addItem(@IntRange(from = 0) int position, @NonNull T item) { if (item == null) { - Log.e(TAG, "No items to add!"); + Log.e(TAG, "addItem No items to add!"); return false; } if (DEBUG) Log.v(TAG, "addItem delegates addition to addItems!"); @@ -2182,31 +2168,32 @@ public boolean addItem(@IntRange(from = 0) int position, @NonNull T item) { } /** - * Inserts a set of items in the internal list at specified position or Adds the items - * at last position. - *NOTE: When all headers are shown, the header (if exists) of this item will be - * shown as well, unless it's already shown, so it will not be shown twice.
+ * Inserts a set of items at specified position or Adds the items to the end of the list + * (no matters if the new position is out of bounds). + *NOTE: When all headers are shown, if exists, the header of this item will be + * shown as well, unless it's already shown, so it won't be shown twice.
* - * @param position position inside the list, -1 to add the set the end of the list - * @param items the items to add + * @param position position inside the list, if negative, items will be added to the end + * @param items the set of items to add * @return true if the internal list was successfully modified, false otherwise + * @see #addItem(IFlexible) * @see #addItem(int, IFlexible) * @see #addSubItems(int, int, IExpandable, List, boolean, Object) * @since 5.0.0-b1 */ public boolean addItems(@IntRange(from = 0) int position, @NonNull ListIn order to add subItems, the following condition must be satisfied:
*
- The item resulting from the parent position is actually an {@link IExpandable}.
The new section is a {@link IHeader} item and the position is calculated after sorting
- * the Data Set.
- *
- To add Sections to the top, set null the Comparator object or simply call
+ * Adds and shows an empty section. The new section is a {@link IHeader} item and the
+ * position is calculated after sorting the data set.
+ *
- To add Sections to the top, set null the Comparator object or simply call
* {@link #addSection(IHeader)};
*
- To add Sections to the bottom or in the middle, implement a Comparator
* object able to support all the item types this Adapter is displaying or a
- * ClassCastException will be raised.
+ * ClassCastException will be raised.
This method is opposite of {@link #removeItemsOfType(Integer...)}.
+ * + * @param viewTypes the viewTypes to retain + * @see #clear() + * @see #removeItems(List) + * @see #removeItemsOfType(Integer...) + * @since 5.0.0-rc1 + */ + public void clearAllBut(Integer... viewTypes) { + ListScrolling animation is automatically preserved, meaning that notification for animation * is ignored.
* @@ -2562,7 +2587,8 @@ public void removeItem(@IntRange(from = 0) int position, @Nullable Object payloa } /** - * Convenience method of {@link #removeItems(List, Object)} providing a null payload. + * Convenience method of {@link #removeItems(List, Object)} providing the default + * {@link Payload#CHANGE} for the parent items. * * @see #removeItem(int) * @see #removeItemsOfType(Integer...) @@ -2576,10 +2602,10 @@ public void removeItems(@NonNull ListEvery item is retained for an eventual Undo.
- * Optionally you can pass any payload to notify the parent about the change and optimize the - * view binding. + * Optionally you can pass any payload to notify the parent items about the change to + * optimize the view binding. *This method delegates the removal to removeRange.
* * @param selectedPositions list with item positions to remove @@ -2633,8 +2659,11 @@ public int compare(Integer lhs, Integer rhs) { /** * Selectively removes all items of the type provided as parameter. + *This method is opposite of {@link #clearAllBut(Integer...)}.
* * @param viewTypes the viewTypes to remove + * @see #clear() + * @see #clearAllBut(Integer...) * @see #removeItem(int, Object) * @see #removeItems(List) * @see #removeAllSelectedItems() @@ -2644,9 +2673,7 @@ public void removeItemsOfType(Integer... viewTypes) { ListIf the item, resulting from the passed position:
+ * Removes a list of consecutive items and notify the change. + *If the item, resulting from the provided position:
* - is not expandable with no parent, it is removed as usual.Parent will not be notified about the change, if a child is removed.
+ * Convenience method to remove all items that are currently selected. + *Parent will not be notified about the change if a child is removed.
* + * @see #clear() + * @see #clearAllBut(Integer...) * @see #removeItem(int) * @see #removeItems(List) * @see #removeRange(int, int) @@ -2798,13 +2831,15 @@ public void removeAllSelectedItems() { } /** - * Convenience method to remove all Items that are currently selected.- * Optionally the Parent can be notified about the change, if a child is removed, by passing - * any payload. + * Convenience method to remove all items that are currently selected. + *
Optionally, the parent item can be notified about the change if a child is removed, + * by providing any non-null payload.
* * @param payload any non-null user object to notify the parent (the payload will be * therefore passed to the bind method of the parent ViewHolder), * pass null to not notify the parent + * @see #clear() + * @see #clearAllBut(Integer...) * @see #removeItem(int, Object) * @see #removeItems(List, Object) * @see #removeRange(int, int, Object) @@ -2965,7 +3000,7 @@ public void restoreDeletedItems() { } /** - * Clean memory from items just removed. + * Cleans memory from items just removed. *Note: This method is automatically called after timer is over and after a * restoration.
* @@ -2977,7 +3012,7 @@ public synchronized void emptyBin() { } /** - * Convenience method to start Undo timer with default timeout of 5'' + * Convenience method to start Undo timer with default timeout of 5''. * * @param listener the listener that will be called after timeout to commit the change * @since 3.0.0 @@ -2987,7 +3022,7 @@ public void startUndoTimer(OnDeleteCompleteListener listener) { } /** - * Start Undo timer with custom timeout + * Start Undo timer with custom timeout. * * @param timeout custom timeout * @param listener the listener that will be called after timeout to commit the change @@ -3697,11 +3732,11 @@ public final ItemTouchHelperCallback getItemTouchHelperCallback() { } /** - * Sets a custom callback implementation for Item touch. + * Sets a custom callback implementation for item touch. *If called, Helper will be reinitialized.
* If not called, the default Helper will be used. * - * @param itemTouchHelperCallback the custom callback implementation for Item touch + * @param itemTouchHelperCallback the custom callback implementation for item touch * @return this Adapter, so the call can be chained * @since 5.0.0-rc1 */ @@ -4120,7 +4155,7 @@ private ListDelegates the click event to the listener and checks if selection MODE if - * SINGLE or MULTI is enabled in order to activate the ItemView.
+ *This method receives the click event generated from the itemView to check if one + * of the selection mode ({@code SINGLE or MULTI}) is enabled in order to activate the + * itemView.
* For Expandable Views it will toggle the Expansion if configured so. * * @param position the adapter position of the item clicked - * @return true if the click should activate the ItemView, false for no change. + * @return true if the click should activate the itemView according to the selection mode, + * false for no change to the itemView. * @since 5.0.0-b1 */ boolean onItemClick(int position); @@ -4279,9 +4316,8 @@ public interface OnItemClickListener { public interface OnItemLongClickListener { /** * Called when long tap occurs. - *This method always calls - * {@link FlexibleViewHolder#toggleActivation} - * after listener event is consumed in order to activate the ItemView.
+ *This method always calls {@link FlexibleViewHolder#toggleActivation()} + * after the listener event is consumed in order to activate the itemView.
* For Expandable Views it will collapse the View if configured so. * * @param position the adapter position of the item clicked @@ -4344,7 +4380,7 @@ public interface OnItemMoveListener extends OnActionStateListener { */ public interface OnItemSwipeListener extends OnActionStateListener { /** - * Called when swiping ended its animation and Item is not visible anymore. + * Called when swiping ended its animation and item is not visible anymore. * * @param position the position of the item swiped * @param direction the direction to which the ViewHolder is swiped, one of: @@ -4622,9 +4658,45 @@ protected void onPostFilter() { } /** - * The following Lists are available as: - *- {@code protected List oldItems;}
- *
- {@code protected List newItems;}
+ * Handler callback for delayed actions.
+ *
You can use and override this Callback, current values used by the Adapter:
+ * 0 = async call for updateDataSet. + *Note: numbers 0-9 are reserved for the Adapter, use others.
+ * + * @since 5.0.0-rc1 + */ + public class HandlerCallback implements Handler.Callback { + + @CallSuper + @Override + public boolean handleMessage(Message message) { + switch (message.what) { + case UPDATE: //updateDataSet OR + case FILTER: //filterItems + if (mFilterAsyncTask != null) mFilterAsyncTask.cancel(true); + mFilterAsyncTask = new FilterAsyncTask(message.what, (List- {@code protected List
* For example, if your items have unique ids, this method should check their id equality.
*
@@ -4661,7 +4733,6 @@ public final int getNewListSize() {
*/
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-// if (DEBUG) Log.w(TAG, "oldItemPosition=" + oldItemPosition + ", newItemPosition=" + newItemPosition);
T oldItem = oldItems.get(oldItemPosition);
T newItem = newItems.get(newItemPosition);
return oldItem.equals(newItem);
@@ -4704,7 +4775,6 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
*
* @param oldItemPosition The position of the item in the old list
* @param newItemPosition The position of the item in the new list
- *
* @return A payload object that represents the change between the two items.
*/
@Nullable
diff --git a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/SelectableAdapter.java b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/SelectableAdapter.java
index df57ce87..6479fb2b 100644
--- a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/SelectableAdapter.java
+++ b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/SelectableAdapter.java
@@ -425,17 +425,6 @@ public List To call once! Then call {@link #getSelectedPositions()}. You can override this method to return specific values (don't call super) or you can
+ * You can override this method to return specific values (don't call super) OR you can
* let this method to call the implementation of {@code IFlexible#getLayoutRes()} so ViewTypes
* are automatically mapped (AutoMap). HELP: To know how to implement AutoMap for ViewTypes please refer to the
+ * FlexibleAdapter Wiki Page
+ * on GitHub.
* HELP: To know how to implement AutoMap for ViewTypes please refer to the
+ * FlexibleAdapter Wiki Page
+ * on GitHub.
* NOTE: Should identify a resource Layout reference {@link android.R.layout} used
- * by FlexibleAdapter to auto-map the ViewTypes. HELP: To know how to implement auto-map for ViewTypes please refer to the
- * FlexibleAdapter WikiPage
+ * Delegates the creation of the ViewHolder to the user if AutoMap has been implemented.
+ * HELP: To know how to implement AutoMap for ViewTypes please refer to the
+ * FlexibleAdapter Wiki Page
* on GitHub. HELP: To know how to implement auto-map for ViewTypes please refer to the
- * FlexibleAdapter WikiPage
+ * Binds the data of this item to the given Layout if AutoMap has been implemented.
+ * HELP: To know how to implement AutoMap for ViewTypes please refer to the
+ * FlexibleAdapter Wiki Page
* on GitHub. Attaches the StickyHeaderHelper from the RecyclerView when necessary Attaches the StickyHeaderHelper from the RecyclerView if necessary. Attaches the StickyHeaderHelper from the RecyclerView when necessary Attaches the StickyHeaderHelper from the RecyclerView if necessary. This method returns always false; Extend with "return true" to Not expand or collapse
- * this ItemView onClick events.
- {@code protected List
This method returns always false; Extend with "return true" to add item to the ActionMode - * count.
+ *This method returns always false; Extend with "return true" to add the item to the + * ActionMode count.
* * @return always false, if not overridden * @since 5.0.0-b2 @@ -256,12 +257,14 @@ protected boolean shouldAddSelectionInActionMode() { * actively scrolls the list (forward or backward). *Implement your logic for different animators based on position, selection and/or * direction.
- * Create your {@link Animator}(s), then add it to the list of animators. + * Use can take one of the predefined Animator from {@link AnimatorHelper} or create your own + * {@link Animator}(s), then add it to the list of animators. * + * @param animators NonNull list of animators, which you should add new animators * @param position can be used to differentiate the Animators based on positions * @param isForward can be used to separate animation from top/bottom or from left/right scrolling * @since 5.0.0-b8 - * @see eu.davidea.flexibleadapter.helpers.AnimatorHelper + * @see AnimatorHelper */ public void scrollAnimators(@NonNull ListIn this implementations, View activation is automatically handled in case of Drag: + *
In this implementation, View activation is automatically handled in case of Drag: * The Item will be added to the selection list if not selected yet and mode MULTI is activated.
* * @param position the position of the item touched @@ -357,6 +360,7 @@ public void onItemReleased(int position) { } /** + * @return the boolean value from the item flag, true to allow dragging * @since 5.0.0-b7 */ @Override @@ -366,6 +370,7 @@ public boolean isDraggable() { } /** + * @return the boolean value from the item flag, true to allow swiping * @since 5.0.0-b7 */ @Override From 4b6c34b79ae7a676c61e020fe2bc147e3b1d9281 Mon Sep 17 00:00:00 2001 From: Davide Steduto+ *
Note: Scrollable Headers and Footers (if existent) will be restored in this call.
* Note: The following methods will be also called at the end of the operation: *Note: This method cannot be overridden since the selection and the internal + * methods rely on it.
* - * @return the total number of the items currently displayed by the adapter + * @return the total number of items (headers and footers INCLUDED) held by the adapter + * @see #getItemCount(boolean) * @see #getItemCountOfTypes(Integer...) * @see #getItemCountOfTypesUntil(int, Integer...) * @see #isEmpty() @@ -533,7 +547,28 @@ public long getItemId(int position) { */ @Override public final int getItemCount() { - return mItems != null ? mItems.size() : 0; + return mItems.size(); + } + + /** + * Returns the total number of items in the data set held by the adapter. + *This method cannot be overridden since the entire library relies on it.
* * @param item the item to find * @return the global position in the Adapter if found, -1 otherwise * @since 5.0.0-b1 */ - public int getGlobalPositionOf(@NonNull IFlexible item) { - return item != null && mItems != null && !mItems.isEmpty() ? mItems.indexOf(item) : -1; + public final int getGlobalPositionOf(@NonNull IFlexible item) { + return item != null ? mItems.indexOf(item) : -1; + } + + /** + * Retrieves the position of the Main item in the Adapter list excluding the scrollable Headers. + * If no scrollable Headers are added, the cardinal position coincides with the global position. + *Note:
+ *
- This method cannot be overridden.
+ *
- This method is NOT suitable to call when removing items, if so, you should retrieve
+ * the global position with {@link #getGlobalPositionOf(IFlexible)}.
Headers can be sticky only if they are shown. Command is otherwise ignored!
- * NOTE: + *Headers can be sticky only if they are shown.
+ * Note: *Only for a real child of an expanded parent.
* * @param child the child item - * @return the list of the child element, or an empty list if the child item has no parent + * @return the position in the parent or -1 if the child is a parent itself or not found * @see #getExpandableOf(IFlexible) * @see #getExpandablePositionOf(IFlexible) - * @see #getRelativePositionOf(IFlexible) - * @see #getExpandedItems() * @since 5.0.0-b1 */ - @NonNull - public ListOnly for a real child of an expanded parent.
+ * Provides the full sub list where the child currently lays. * * @param child the child item - * @return the position in the parent or -1 if the child is a parent itself or not found + * @return the list of the child element, or an empty list if the child item has no parent * @see #getExpandableOf(IFlexible) * @see #getExpandablePositionOf(IFlexible) + * @see #getRelativePositionOf(IFlexible) + * @see #getExpandedItems() * @since 5.0.0-b1 */ - public int getRelativePositionOf(@NonNull T child) { - return getSiblingsOf(child).indexOf(child); + @NonNull + public ListIf configured, automatic smooth scroll will be performed when necessary.
* * @param position the position of the item to expand @@ -1953,7 +2128,9 @@ public int expandAll() { public int expandAll(int level) { int expanded = 0; //More efficient if we expand from First expandable position - for (int i = 0; i < mItems.size(); i++) { + int startPosition = Math.max(0, mScrollableHeaders.size() - 1); + int endPosition = getItemCount() - mScrollableFooters.size() - 1; + for (int i = startPosition; i < endPosition; i++) { T item = getItem(i); if (isExpandable(item)) { IExpandable expandable = (IExpandable) item; @@ -2096,6 +2273,7 @@ public void updateItem(@IntRange(from = 0) int position, @NonNull T item, Log.e(TAG, "Cannot updateItem on position out of OutOfBounds!"); return; } + position += mScrollableHeaders.size(); mItems.set(position, item); if (DEBUG) Log.d(TAG, "updateItem notifyItemChanged on position " + position); notifyItemChanged(position, payload); @@ -2128,16 +2306,19 @@ public void addItemWithDelay(@IntRange(from = 0) final int position, @NonNull fi mHandler.postDelayed(new Runnable() { @Override public void run() { - setAnimate(true); - if (addItem(position, item) && scrollToPosition && mRecyclerView != null) { - mRecyclerView.smoothScrollToPosition( - Math.min(Math.max(0, position), getItemCount() - 1)); - } - setAnimate(false); + performDelayedAdd(position, item, scrollToPosition); } }, delay); } + private void performDelayedAdd(int position, T item, boolean scrollToPosition) { + setAnimate(true); + if (addItem(position, item) && scrollToPosition && mRecyclerView != null) { + mRecyclerView.smoothScrollToPosition(Math.min(Math.max(0, position), getItemCount() - 1)); + } + setAnimate(false); + } + /** * Simply append the provided item to the end of the list. *Convenience method of {@link #addItem(int, IFlexible)} with
@@ -2152,9 +2333,9 @@ public boolean addItem(@NonNull T item) {
/**
* Inserts the given item at the specified position or Adds the item to the end of the list
- * (no matters if the new position is out of bounds).
+ * (no matters if the new position is out of bounds!).
*
- * @param position position of the item to add, if negative, items will be added to the end
+ * @param position position inside the list, if negative, items will be added to the end
* @param item the item to add
* @return true if the internal list was successfully modified, false otherwise
* @see #addItem(IFlexible)
@@ -2169,16 +2350,16 @@ public boolean addItem(@IntRange(from = 0) int position, @NonNull T item) {
return false;
}
if (DEBUG) Log.v(TAG, "addItem delegates addition to addItems!");
- List NOTE: When all headers are shown, if exists, the header of this item will be
- * shown as well, unless it's already shown, so it won't be shown twice. Note:
+ *
- When all headers are shown, if exists, the header of this item will be shown as well,
+ * unless it's already shown, so it won't be shown twice.
+ *
- Items will be always added between any scrollable header and footers.
In this case parent item will never be expanded if it is collapsed.
@@ -2474,8 +2666,8 @@ public int addItemToSection(@NonNull ISectionable item, @NonNull IHeader header,
/*----------------------*/
/**
- * Convenience method of {@link #removeRange(int, int, Object)} that removes all items, with
- * {@code null} payload.
This method is opposite of {@link #removeItemsOfType(Integer...)}.
+ * Clears the Adapter list retaining Scrollable Headers and Footers and all items of the + * type provided as parameter. + *Note:- This method is opposite of {@link #removeItemsOfType(Integer...)}.
*
* @param viewTypes the viewTypes to retain
* @see #clear()
@@ -2502,7 +2697,9 @@ public void clearAllBut(Integer... viewTypes) {
if (viewTypeList.size() > 0) {
if (DEBUG) Log.d(TAG, "clearAll retaining views " + viewTypeList);
List This method is opposite of {@link #clearAllBut(Integer...)}. Note:
+ * NOTE: If filter is active, only items that match that filter will be shown(restored). Note: If filter is active, only items that match that filter will be shown(restored). This method filters the provided list with the search text previously set with
+ * This method filters the provided list with the searchText previously set with
* {@link #setSearchText(String)}. NOTE:
+ * Note:
* Default value is {@value mAnimateToLimit} items, number new items. Default value is {@value ANIMATE_TO_LIMIT} items, number new items. NOTE: This will skip LongClick on the view in order to handle the LongPress,
+ * Note: This will skip LongClick on the view in order to handle the LongPress,
* however the LongClick listener will be called if necessary in the new
* {@link FlexibleViewHolder#onActionStateChanged(int, int)}.
* The Activity implementation is organized in this order:
*
* The Fragments may use Activity implementations or may override specific behaviors
* themselves. Fragments have {@code AbstractFragment} in common to have some methods reusable.
- *
+ *
* ...more on
* Demo app Wiki page. A Section should not contain others Sections! Note: THIS ITEM IS NOT A SCROLLABLE HEADER. If you don't have many fields in common better to extend directly from
* {@link eu.davidea.flexibleadapter.items.AbstractFlexibleItem} to benefit of the already
* implemented methods (getter and setters). Note: You now can set a custom container by calling
+ * {@link #setStickyHeaderContainer(ViewGroup)}
- * This handler can launch asynchronous tasks and if you catch the reserved "what",
- * keep in mind that this code should be executed before that task has been completed.
- * This handler can launch asynchronous tasks. Note: numbers 0-9 are reserved for the Adapter, use others for new values. If you don't have many fields in common better to extend directly from
@@ -26,8 +26,6 @@
*/
public class ScrollableLayoutItem extends AbstractItem Note: numbers 0-9 are reserved for the Adapter, use others. This method is called if any limit is reached (targetCount or pageSize
+ * must be set) AND if new data is temporary unavailable (ex. no connection or no
+ * new updates remotely). If no new data, a {@link FlexibleAdapter#notifyItemChanged(int, Object)}
+ * with a payload {@link Payload#NO_MORE_LOAD} is triggered on the progressItem. Use {@code lastPosition} and {@code currentPage} to know what to load next. This method is called if any limit is reached (targetCount or pageSize
+ * must be set) AND if new data is temporary unavailable (ex. no connection or no
+ * new updates remotely). If no new data, a {@link FlexibleAdapter#notifyItemChanged(int, Object)}
+ * with a payload {@link Payload#NO_MORE_LOAD} is triggered on the progressItem. Use {@code lastPosition} and {@code currentPage} to know what to load next. Note:
- *
- This method is opposite of {@link #clearAllBut(Integer...)}.
+ *
- View types of Scrollable Headers and Footers are ignored!
- *
*
* @param unfilteredItems the list to filter
@@ -3259,6 +3471,7 @@ public void filterItems(@NonNull List
5.0.0-b1 Expandable + Child filtering
*
5.0.0-b8 Synchronization animations limit + AsyncFilter
+ *
5.0.0-rc1 Scrollable Headers and Footers adaptation
*/
public void filterItems(@NonNull List
- The item will be collected if the implemented method returns true.
*
- {@code IExpandable} items are automatically picked up and displayed if at least a
* child is collected by the current filter. You DON'T NEED to implement the scan for the
@@ -3449,7 +3663,7 @@ private void resetFilterFlags(List
*
- *
+ *
1 = async call for filterItems, optionally delayed.
*
2 = deleteConfirmed when Undo timeout is over.
*
8 = remove the progress item from the list, optionally delayed.
+ *
9 = remove the progress item from the list and DISABLE the EndlessScroll.
*
5.0.0-rc1 added {@code lastPosition} and {@code currentPage} as parameters
*/
@Override
public void onLoadMore(int lastPosition, int currentPage) {
- //We don't want load more items when searching into the current Collection!
- //Alternatively, for a special filter, if we want load more items when filter is active, the
+ // We don't want load more items when searching into the current Collection!
+ // Alternatively, for a special filter, if we want load more items when filter is active, the
// new items that arrive from remote, should be already filtered, before adding them to the Adapter!
if (mAdapter.hasSearchText()) {
mAdapter.onLoadMoreComplete(null);
return;
}
- //Simulating asynchronous call
+ // Simulating asynchronous call
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
final List
5.0.0-rc1 added {@code lastPosition} and {@code currentPage} as parameters
*/
@Override
public void onLoadMore(int lastPosition, int currentPage) {
Log.i(TAG, "onLoadMore invoked!");
- //Simulating asynchronous call
+ // Simulating asynchronous call
new Handler().postDelayed(new Runnable() {
@SuppressWarnings("unchecked")
@Override
@@ -126,13 +138,20 @@ public void run() {
newItems.add(DatabaseService.newInstagramItem(totalItemsOfType + i));
}
- //Callback the Adapter to notify the change
- //Items will be added to the end of the list
+ // Callback the Adapter to notify the change
+ // Items will be added to the end of the main list
mAdapter.onLoadMoreComplete(newItems);
-
- //Notify user
- String message = "Fetched " + newItems.size() + " new items";
- Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "newItemsSize=" + newItems.size());
+ Log.d(TAG, "EndlessCurrentPage=" + mAdapter.getEndlessCurrentPage());
+ Log.d(TAG, "EndlessPageSize=" + mAdapter.getEndlessPageSize());
+ Log.d(TAG, "EndlessTargetCount=" + mAdapter.getEndlessTargetCount());
+
+ // Notify user
+ if (getActivity() != null && newItems.size() > 0) {
+ Toast.makeText(getActivity(),
+ "Fetched " + newItems.size() + " new items",
+ Toast.LENGTH_SHORT).show();
+ }
}
}, 3000);
}
diff --git a/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentOverall.java b/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentOverall.java
index 9590636b..802013af 100644
--- a/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentOverall.java
+++ b/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentOverall.java
@@ -74,17 +74,17 @@ private void initializeRecyclerView(Bundle savedInstanceState) {
// Prepare the RecyclerView and attach the Adapter to it
mRecyclerView = (RecyclerView) getView().findViewById(R.id.recycler_view);
- mRecyclerView.setItemViewCacheSize(0);//Setting ViewCache to 0 (default=2) will animate items better while scrolling down+up with LinearLayout
+ mRecyclerView.setItemViewCacheSize(0); //Setting ViewCache to 0 (default=2) will animate items better while scrolling down+up with LinearLayout
mRecyclerView.setLayoutManager(createNewStaggeredGridLayoutManager());
mRecyclerView.setAdapter(mAdapter);
- mRecyclerView.setHasFixedSize(true);//Size of RV will not change
+ mRecyclerView.setHasFixedSize(true); //Size of RV will not change
// After Adapter is attached to RecyclerView
mAdapter.setLongPressDragEnabled(true);
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
- if (getView() != null) {//Fix NPE when closing app before the execution of Runnable
+ if (getView() != null) { //Fix NPE when closing app before the execution of Runnable
Snackbar.make(getView(), "Long press drag is enabled", Snackbar.LENGTH_SHORT).show();
}
}
@@ -94,7 +94,7 @@ public void run() {
swipeRefreshLayout.setEnabled(true);
mListener.onFragmentChange(swipeRefreshLayout, mRecyclerView, SelectableAdapter.MODE_IDLE);
- // Add sample HeaderView items on the top (not belongs to the library)
+ // Add 1 Scrollable Header
mAdapter.showLayoutInfo(savedInstanceState == null);
}
@@ -117,7 +117,7 @@ protected GridLayoutManager createNewGridLayoutManager() {
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
- // NOTE: If you use simple integer to identify the ViewType,
+ // NOTE: If you use simple integers to identify the ViewType,
// here, you should use them and not Layout integers
switch (mAdapter.getItemViewType(position)) {
case R.layout.recycler_scrollable_layout_item:
diff --git a/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentSelectionModes.java b/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentSelectionModes.java
index 674fcb0b..feedcccb 100644
--- a/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentSelectionModes.java
+++ b/flexible-adapter-app/src/main/java/eu/davidea/samples/flexibleadapter/fragments/FragmentSelectionModes.java
@@ -61,14 +61,14 @@ public FragmentSelectionModes() {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- //Settings for FlipView
+ // Settings for FlipView
FlipView.resetLayoutAnimationDelay(true, 1000L);
- //Create New Database and Initialize RecyclerView
+ // Create New Database and Initialize RecyclerView
DatabaseService.getInstance().createEndlessDatabase(200);
initializeRecyclerView(savedInstanceState);
- //Settings for FlipView
+ // Settings for FlipView
FlipView.stopLayoutAnimation();
}
@@ -77,21 +77,21 @@ private void initializeRecyclerView(Bundle savedInstanceState) {
//Get copy of the Database list
List
- This method cannot be overridden.
- *
- This method is NOT suitable to call when removing items, if so, you should retrieve
- * the global position with {@link #getGlobalPositionOf(IFlexible)}.
- This method is NOT suitable to call when managing items: ALL insert, remove, move and
+ * swap operations, should done with global position {@link #getGlobalPositionOf(IFlexible)}.
+ *
- This method cannot be overridden.
Using this method, the {@link EndlessScrollListener} won't be called so that you can * handle a click event to load more items upon a user request.
* To correctly implement "Load more upon a user request" check the Wiki page of this library. * * @param progressItem the item representing the progress bar * @return this Adapter, so the call can be chained + * @see #isEndlessScrollEnabled() * @see #setEndlessScrollListener(EndlessScrollListener, IFlexible) * @since 5.0.0-b8 */ - public FlexibleAdapter setEndlessProgressItem(@NonNull T progressItem) { + public FlexibleAdapter setEndlessProgressItem(@Nullable T progressItem) { + endlessScrollEnabled = progressItem != null; if (progressItem != null) { setEndlessScrollThreshold(mEndlessScrollThreshold); + mProgressItem = progressItem; if (DEBUG) { - Log.i(TAG, "Set progressItem=" + progressItem.getClass().getSimpleName()); + Log.i(TAG, "Set progressItem=" + getClassName(progressItem)); Log.i(TAG, "Enabled EndlessScrolling"); } } else if (DEBUG) { Log.i(TAG, "Disabled EndlessScrolling"); } - mProgressItem = progressItem; return this; } /** - * Sets the ProgressItem to be displayed at the end of the list and Sets the callback to - * automatically load more items asynchronously (no further user action is needed but the - * scroll). + * Sets the progressItem to be displayed at the end of the list and Sets the callback to + * automatically load more items asynchronously(your duty) (no further user action is needed + * but the scroll). * * @param endlessScrollListener the callback to invoke the asynchronous loading * @param progressItem the item representing the progress bar * @return this Adapter, so the call can be chained + * @see #isEndlessScrollEnabled() * @see #setEndlessProgressItem(IFlexible) * @since 5.0.0-b6 */ //TODO: Deprecation? use setProgressItem + setEndlessScrollListener public FlexibleAdapter setEndlessScrollListener(@Nullable EndlessScrollListener endlessScrollListener, @NonNull T progressItem) { - if (DEBUG) - Log.i(TAG, "Set endlessScrollListener=" + endlessScrollListener.getClass().getSimpleName()); + if (DEBUG) Log.i(TAG, "Set endlessScrollListener=" + getClassName(endlessScrollListener)); mEndlessScrollListener = endlessScrollListener; return setEndlessProgressItem(progressItem); } @@ -1691,38 +1725,38 @@ public FlexibleAdapter setEndlessScrollThreshold(@IntRange(from = 1) int thresho * at the end of {@code onBindViewHolder()}. * * @param position the current binding position + * @since 5.0.0-b6 + *When noMoreLoad OR onError OR onCancel, pass empty list or null to hide the * progressItem.
* Optionally you can pass a delay time to still display the item with the latest information - * inside. The message has to be handled inside the bindViewHolder of the item. + * inside. The message has to be handled inside the {@code bindViewHolder} of the item. + *A {@link #notifyItemChanged(int, Object)} with payload {@link Payload#NO_MORE_LOAD} + * will be triggered on the progressItem, so you can display a message or change the views in + * this item.
* * @param newItems the list of the new items, can be empty or null * @param delay the delay used to remove the progress item or -1 to disable the * loading forever and to keep the progress item visible. * @since 5.0.0-b8 + *Convenience method of {@link #addItem(int, IFlexible)} with
@@ -2443,17 +2466,13 @@ public boolean addItems(@IntRange(from = 0) int position, @NonNull List Note: Having any Scrollable Headers/Footers visible, the {@code size}
+ * will represents only the main items. Due to Java Generic, it's too complicated and not
- * well manageable if we pass the List<T> object. Override this method to receive touch events with its state. This method is called if any limit is reached (targetCount or pageSize
+ * must be set) AND if new data is temporary unavailable (ex. no connection or no
+ * new updates remotely). If no new data, a {@link FlexibleAdapter#notifyItemChanged(int, Object)}
+ * with a payload {@link Payload#NO_MORE_LOAD} is triggered on the progressItem. Use {@code lastPosition} and {@code currentPage} to know what to load next. Note: numbers 0-9 are reserved for the Adapter, use others. Scrollable Headers have the following characteristic:
+ *
5.0.0-b1 Expandable + Child filtering
*/
protected boolean filterObject(T item, String constraint) {
- if (item instanceof IFilterable) {
- IFilterable filterable = (IFilterable) item;
- return filterable.filter(constraint);
- }
- return false;
+ return item instanceof IFilterable && ((IFilterable) item).filter(constraint);
}
/**
@@ -3821,7 +3822,6 @@ private synchronized void animateDiff(@Nullable List
5.0.0-b8 Synchronization animation limit
*/
private synchronized void animateTo(@Nullable List
- * To get deleted items, use {@link #getDeletedItems()} from the
+ * well manageable if we pass the List<T> object.
+ * To get deleted items, use {@link FlexibleAdapter#getDeletedItems()} from the
* implementation of this method.
5.0.0-rc1 added {@code lastPosition} and {@code currentPage} as parameters
*/
- void noMoreLoad();
+ void onLoadMore(int lastPosition, int currentPage);
}
/**
@@ -4983,8 +5014,7 @@ protected void onPostFilter() {
* 0 = async call for updateDataSet.
*
1 = async call for filterItems, optionally delayed.
*
2 = deleteConfirmed when Undo timeout is over.
- *
8 = remove the progress item from the list, optionally delayed.
- *
9 = remove the progress item from the list and DISABLE the EndlessScroll.
+ *
8 = hide the progress item from the list, optionally delayed.
*
+ *
Scrollable Footers have the following characteristic: + *
Note: This limit is ignored if value is 0.
+ * + * @return the page size limit, if the limit is not set, 0 is returned. + * @see #getEndlessCurrentPage() + * @see #setEndlessPageSize(int) + * @since 5.0.0-rc1 + */ public int getEndlessPageSize() { return mEndlessPageSize; } - public FlexibleAdapter setEndlessPageSize(@IntRange(from = 1) int endlessPageSize) { + /** + * Sets the limit to automatically disable the endless feature when coming items size is less + * than the page size. + *When endless feature is disabled a {@link #notifyItemChanged(int, Object)} with payload + * {@link Payload#NO_MORE_LOAD} will be triggered on the progressItem, so you can display a + * message or change the views in this item.
+ * Default value is 0 (limit is ignored). + * + * @param endlessPageSize the size limit for each page + * @return this Adapter, so the call can be chained + * @since 5.0.0-rc1 + */ + public FlexibleAdapter setEndlessPageSize(@IntRange(from = 0) int endlessPageSize) { mEndlessPageSize = endlessPageSize; return this; } + /** + * The current setting for the endless target item count limit. + *Note: This limit is ignored if value is 0.
+ * + * @return the target items count limit, if the limit is not set, 0 is returned. + * @see #setEndlessTargetCount(int) + * @since 5.0.0-rc1 + */ public int getEndlessTargetCount() { return mEndlessTargetCount; } - public FlexibleAdapter setEndlessTargetCount(@IntRange(from = 1) int endlessTargetCount) { + /** + * Sets the limit to automatically disable the endless feature when the total items count in + * the Adapter is equals or bigger than the target count. + *When endless feature is disabled a {@link #notifyItemChanged(int, Object)} with payload + * {@link Payload#NO_MORE_LOAD} will be triggered on the progressItem, so you can display a + * message or change the views in this item.
+ * Default value is 0 (limit is ignored). + * + * @param endlessTargetCount the total items count limit + * @return this Adapter, so the call can be chained + * @see #getEndlessTargetCount() + * @since 5.0.0-rc1 + */ + public FlexibleAdapter setEndlessTargetCount(@IntRange(from = 0) int endlessTargetCount) { mEndlessTargetCount = endlessTargetCount; return this; } From 4a56e6cdf10d813211527b79fefcf45be86044dd Mon Sep 17 00:00:00 2001 From: Davide Steduto- {@code protected List Scrollable Headers have the following characteristic:
* Scrollable Footers have the following characteristic:
* The value of this enumeration will be passed to the bind method to optimize the view binding
- * in order to update only the inner views interested for the change. The value of this enumeration will be provided to the bind method to optimize the view
+ * binding in order to update only the inner views interested for the change. This class extends {@link FlexibleViewHolder}, which means it will benefit of all implemented
- * methods the super class holds. This class extends {@link FlexibleViewHolder}, which means it will benefit of all
+ * implemented methods the super class holds. This method returns always true; Extend with "return false" to Not expand or collapse
* this ItemView onClick events. This method returns always false; Override with {@code "return true"} to trigger the
+ * notification. If {@link #shouldNotifyParentOnClick()} returns {@code true}, this view is rebound
+ * with payload {@link Payload#EXPANDED}. If {@link #shouldNotifyParentOnClick()} returns {@code true}, this view is rebound
+ * with payload {@link Payload#COLLAPSED}. Note: In Expandable version, it tries to expand, but before,
* it checks if the view {@link #isViewExpandableOnClick()}. Note: In Expandable version, it tries to collapse, but before,
* it checks if the view {@link #isViewCollapsibleOnLongClick()}. Note: In the Expandable version, expanded items are forced to collapse. IMPORTANT NOTE! the change of the background is visible if you added
- * android:background="?attr/selectableItemBackground" on the item layout AND
- * in the style.xml. This must be called every time we want the activation state visible on the ItemView,
- * for instance, after a Click (to add the item to the selection list) or after a LongClick
- * (to activate the ActionMode) or during a Drag (to show that we enabled the Drag). IMPORTANT NOTE! the selected background is visible if you added
+ * {@code android:background="?attr/selectableItemBackground"} on the item layout AND
+ * customized the file {@code style.xml}. Note: This method must be called every time we want the activation state visible
+ * on the itemView, for instance: after a Click (to add the item to the selection list) or
+ * after a LongClick (to activate the ActionMode) or during dragging (to show that we enabled
+ * the Drag). Override to return desired value of elevation on this itemView. This method returns always false; Extend with "return true" to Not expand or collapse
- * this itemView onClick events. This method returns always false; Override with {@code "return true"} to Not expand or
+ * collapse this itemView onClick events. This method returns always false; Extend with "return true" to add the item to the
- * ActionMode count. This method returns always false;Override with {@code "return true"} to add the item
+ * to the ActionMode count. In this implementation, View activation is automatically handled in case of Drag:
- * The Item will be added to the selection list if not selected yet and mode MULTI is activated. In this implementation, View activation is automatically handled if dragged: The Item
+ * will be added to the selection list if not selected yet and mode MULTI is activated. This method is called if any limit is reached (targetCount or pageSize
@@ -171,43 +169,45 @@ public void onLoadMore(int lastPosition, int currentPage) {
public void run() {
final List This method returns always true; Extend with "return false" to Not expand or collapse
+ * this ItemView onClick events. This method returns always true; Extend with "return false" to Not collapse this
+ * ItemView onLongClick events. This method returns always false; Override with {@code "return true"} to trigger the
+ * notification. If {@link #shouldNotifyParentOnClick()} returns {@code true}, this view is rebound
+ * with payload {@link Payload#EXPANDED}. If {@link #shouldNotifyParentOnClick()} returns {@code true}, this view is rebound
+ * with payload {@link Payload#COLLAPSED}. If you don't have many fields in common better to extend directly from
- * {@link eu.davidea.flexibleadapter.items.AbstractFlexibleItem} to benefit of the already
- * implemented methods (getter and setters). If you don't have many fields in common better to extend directly from
- * {@link eu.davidea.flexibleadapter.items.AbstractFlexibleItem} to benefit of the already
- * implemented methods (getter and setters). Adapter needs this method to distinguish them and pick up correct items. Basic Java implementation:
* All Layouts are supported. Internally calls {@link #fetchAccentColor(Context, int)}. All Layouts are supported. IMPORTANT: To be called with delay in {@code holder.itemView.postDelayed()}. Internally calls {@link #fetchAccentColor(Context, int)}. Internally calls {@link #fetchAccentColor(Context, int)}. - {@code protected List This method is automatically called from the Constructor. This method is automatically called from the Constructor. Headers can be sticky only if they are shown. Useful in conjunction with ViewPager. Only for a real child of an expanded parent. Note: If you have to change at runtime the LayoutManager and add
+ * Scrollable Headers too, consider to add them in post, using a {@code delay >= 0},
+ * otherwise scroll animations on all items will not start correctly. Default value is {@code 0ms}. The delay is added on top of the previous delay. Default value is {@code 300ms}. Default value is {@link LinearInterpolator}. Default value is {@code 0} (1st position). Enabling scrolling will disable onlyEntryAnimation. Note: Loading animation can only be performed if the Adapter is initialized
+ * with some items using the constructor. Value is ignored if basic animation on scrolling is disabled. Note:
+ *
- {@code protected List
*
*
- * Adapter must have a reference to its instance to check selection state.
From 730c7f5cf83a85dc2cab387c8bb7fb66aad6a5f9 Mon Sep 17 00:00:00 2001
From: Davide Steduto
diff --git a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/utils/Utils.java b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/utils/Utils.java
index de1f4df0..3c407c6d 100644
--- a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/utils/Utils.java
+++ b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/utils/Utils.java
@@ -25,6 +25,7 @@
import android.os.Build.VERSION_CODES;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
@@ -66,6 +67,29 @@ public static boolean hasJellyBean() {
return Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
}
+ /**
+ * @return the string representation of the provided {@link SelectableAdapter.Mode}
+ */
+ @SuppressLint("SwitchIntDef")
+ public static String getModeName(@SelectableAdapter.Mode int mode) {
+ switch (mode) {
+ case SelectableAdapter.MODE_SINGLE:
+ return "MODE_SINGLE";
+ case SelectableAdapter.MODE_MULTI:
+ return "MODE_MULTI";
+ default:
+ return "MODE_IDLE";
+ }
+ }
+
+ /**
+ * @return the SimpleClassName of the provided object
+ */
+ public static String getClassName(@NonNull Object o) {
+ return o.getClass().getSimpleName();
+ }
+
+
/**
* Sets a spannable text with the accent color (if available) into the provided TextView.
*
* @see #FlexibleAdapter(List)
* @see #FlexibleAdapter(List, Object, boolean)
- * @see #initializeListeners(Object)
+ * @see #addListener(Object)
* @since 5.0.0-b1
*/
public FlexibleAdapter(@Nullable List
- {@code protected List
- You must read {@link #getStickySectionHeadersHolder()}.
+ *
- You must read {@link #getStickyHeaderContainer()}.
*
- Sticky headers are now clickable as any Views, but cannot be dragged nor swiped.
*
- Content and linkage are automatically updated.
*
* @return this Adapter, so the call can be chained
- * @see #getStickySectionHeadersHolder()
+ * @see #getStickyHeaderContainer()
* @since 5.0.0-b6
*/
//TODO: deprecation use setStickyHeaders(true)?
@@ -1305,9 +1319,9 @@ public void run() {
*
* @return ViewGroup layout that will hold the sticky header ItemViews
* @since 5.0.0-b6
+ * @deprecated Use {@link #getStickyHeaderContainer()}
*/
- //TODO: Review the comment for the new stickyHeaders helper
- //TODO: Rename the method to getStickyHeaderContainer
+ @Deprecated
public ViewGroup getStickySectionHeadersHolder() {
if (mStickyContainer == null) {
mStickyContainer = (ViewGroup) Utils
@@ -1318,13 +1332,29 @@ public ViewGroup getStickySectionHeadersHolder() {
}
/**
- * Sets a custom {@link ViewGroup} for Sticky Headers when the default can't be used.
- *
- * Disabling scrolling will disable also reverse scrolling!
+ *
+ *
Check {@link #setStickyHeaders(boolean, ViewGroup)}.
+ * <FrameLayout + * android:layout_width="match_parent" + * android:layout_height="match_parent"> + * + * <android.support.v7.widget.RecyclerView + * android:id="@+id/recycler_view" + * android:layout_width="match_parent" + * android:layout_height="match_parent"/> + * + * </FrameLayout> + *+ * + * @param sticky true to initialize sticky headers with default container, false to disable them + * @return this Adapter, so the call can be chained + * @throws IllegalStateException if this Adapter was not attached to the RecyclerView + * @see #setStickyHeaders(boolean, ViewGroup) + * @see #setDisplayHeadersAtStartUp(boolean) + * @since 5.0.0-rc1 + */ + public FlexibleAdapter setStickyHeaders(final boolean sticky) { + return setStickyHeaders(sticky, mStickyContainer); + } + + /** + * Enables/Disables the sticky header feature with a custom sticky layout container. + *
Important: Read the javaDoc of the overloaded method {@link #setStickyHeaders(boolean)}.
+ * + * @param sticky true to initialize sticky headers, false to disable them + * @param stickyContainer user defined and already inflated sticky layout that will + * hold the sticky header itemViews + * @return this Adapter, so the call can be chained + * @throws IllegalStateException if this Adapter was not attached to the RecyclerView + * @see #setStickyHeaders(boolean) + * @see #setDisplayHeadersAtStartUp(boolean) + * @since 5.0.0-rc1 + */ + public FlexibleAdapter setStickyHeaders(final boolean sticky, @NonNull ViewGroup stickyContainer) { + if (DEBUG) Log.i(TAG, "Set stickyHeaders=" + sticky + " (in Post!)" + + (stickyContainer != null ? " with user defined Sticky Container" : "")); + + // With user defined container + mStickyContainer = stickyContainer; + + // Run in post to be sure about the RecyclerView initialization mHandler.post(new Runnable() { @Override public void run() { // Enable or Disable the sticky headers layout if (sticky) { if (mStickyHeaderHelper == null) { - mStickyHeaderHelper = new StickyHeaderHelper(FlexibleAdapter.this, mStickyHeaderChangeListener); + mStickyHeaderHelper = new StickyHeaderHelper(FlexibleAdapter.this, + mStickyHeaderChangeListener, mStickyContainer); mStickyHeaderHelper.attachToRecyclerView(mRecyclerView); if (DEBUG) Log.i(TAG, "Sticky headers enabled"); } @@ -1319,7 +1383,7 @@ public void run() { * * @return ViewGroup layout that will hold the sticky header ItemViews * @since 5.0.0-b6 - * @deprecated Use {@link #getStickyHeaderContainer()} + * @deprecated Unused. Use {@link #setStickyHeaders(boolean, ViewGroup)}. */ @Deprecated public ViewGroup getStickySectionHeadersHolder() { @@ -1338,10 +1402,10 @@ public ViewGroup getStickySectionHeadersHolder() { * * @return ViewGroup layout that will hold the sticky header itemViews * @since 5.0.0-rc1 + * @deprecated Unused, not needed anymore. */ - //TODO: Integrate the usage of the custom container in the new StickyHeaderHelper - //TODO: Review the comment for the new StickyHeaderHelper: layout is not needed anymore - public final ViewGroup getStickyHeaderContainer() { + @Deprecated + public ViewGroup getStickyHeaderContainer() { return mStickyContainer; } @@ -1350,10 +1414,12 @@ public final ViewGroup getStickyHeaderContainer() { * * @param stickyContainer custom container for Sticky Headers * @since 5.0.0-rc1 + * @deprecated Use {@link #setStickyHeaders(boolean, ViewGroup)}. */ + @Deprecated public FlexibleAdapter setStickyHeaderContainer(@NonNull ViewGroup stickyContainer) { if (mStickyHeaderHelper != null) { - Log.w(TAG, "StickyHeaderHelper has been already initialized! Call this method before enabling StickyHeaders"); + Log.w(TAG, "StickyHeader has been already initialized! Call this method before enabling StickyHeaders"); } if (DEBUG && stickyContainer != null) Log.i(TAG, "Set stickyHeaderContainer=" + getClassName(stickyContainer)); @@ -1362,14 +1428,11 @@ public FlexibleAdapter setStickyHeaderContainer(@NonNull ViewGroup stickyContain } /** - * Sets if all headers should be shown at startup. 2 effects are possible depending by - * the current flag of scrolling animation: - *- if enabled, scrolling animations are performed as configured for the header item;
- *
- if disabled, {@code notifyItemInserted()} is instead performed for each header
- * item.
If called, this method won't trigger {@code notifyItemInserted()} and scrolling + * animations are instead performed if the header item was configured with animation: + * Headers will be loaded/bound along with others items.
+ * Note: Headers can only be shown or hidden all together. *Default value is {@code false} (headers are not shown at startup).
* * @param displayHeaders true to display headers, false to keep them hidden @@ -1378,53 +1441,54 @@ public FlexibleAdapter setStickyHeaderContainer(@NonNull ViewGroup stickyContain * @see #setAnimationOnScrolling(boolean) * @since 5.0.0-b6 */ - //TODO: deprecation, rename to displayHeadersAtStartUp() with animate on loading parameter? public FlexibleAdapter setDisplayHeadersAtStartUp(boolean displayHeaders) { if (!headersShown && displayHeaders) { - showAllHeaders(isAnimationOnScrollingEnabled()); + showAllHeaders(true); } return this; } /** - * Shows all headers in the RecyclerView at their linked position. Not intended to be called at - * startup. - *Note: Headers can only be shown or hidden all together.
+ * Shows all headers in the RecyclerView at their linked position. + *If called at startup, this method will trigger {@code notifyItemInserted} for a + * different loading effect: Headers will be inserted after the items!
+ * Note: Headers can only be shown or hidden all together. * + * @return this Adapter, so the call can be chained * @see #hideAllHeaders() * @see #setDisplayHeadersAtStartUp(boolean) * @since 5.0.0-b1 */ - public void showAllHeaders() { + public FlexibleAdapter showAllHeaders() { showAllHeaders(false); + return this; } /** - * @param init true to skip {@code notifyItemInserted}, false to make the call in Post and - * notify single insertion + * @param init true to skip {@code notifyItemInserted}, false to make the call in Post + * and notify single insertion */ private void showAllHeaders(boolean init) { if (init) { if (DEBUG) Log.i(TAG, "showAllHeaders at startup"); - //No notifyItemInserted! + // No notifyItemInserted! showAllHeadersWithReset(true); } else { if (DEBUG) Log.i(TAG, "showAllHeaders with insert notification (in Post!)"); - //In post, let's notifyItemInserted! + // In post, let's notifyItemInserted! mHandler.post(new Runnable() { @Override public void run() { - //#144 - Check if headers are already shown, discard the call to not duplicate headers + // #144 - Check if headers are already shown, discard the call to not duplicate headers if (headersShown) { - Log.w(TAG, "Double call detected! Headers already shown OR the method setDisplayHeadersAtStartUp() was already called!"); + Log.w(TAG, "Double call detected! Headers already shown OR the method showAllHeaders() was already called!"); return; } showAllHeadersWithReset(false); - //#142 - At startup, when scrolling animation is disabled, insert notifications - // are performed to show headers for the first time. Header item is not visible - // at position 0: it has to be displayed by scrolling to it. This resolves the - // first item below sticky header when enabled as well. + // #142 - At startup, when insert notifications are performed to show headers + // for the first time. Header item is not visible at position 0: it has to be + // displayed by scrolling to it. This resolves the first item below sticky + // header when enabled as well. if (mRecyclerView != null) { int firstVisibleItem = Utils.findFirstCompletelyVisibleItemPosition(mRecyclerView.getLayoutManager()); if (firstVisibleItem == 0 && isHeader(getItem(0)) && !isHeader(getItem(1))) { @@ -1440,23 +1504,21 @@ public void run() { * @param init true to skip the call to notifyItemInserted, false otherwise */ private void showAllHeadersWithReset(boolean init) { - multiRange = true; int position = 0; IHeader sameHeader = null; - //resetHiddenStatus();//Necessary after the filter and the update while (position < getItemCount() - mScrollableFooters.size()) { T item = mItems.get(position); + // Reset hidden status! Necessary after the filter and the update IHeader header = getHeaderOf(item); if (header != sameHeader && header != null && !isExpandable((T) header)) { sameHeader = header; header.setHidden(true); } if (showHeaderOf(position, item, init)) - position++;//It's the same element, skip it + position++; //It's the same element, skip it position++; } headersShown = true; - multiRange = false; } /** @@ -1475,9 +1537,8 @@ private boolean showHeaderOf(int position, @NonNull T item, boolean init) { if (header.isHidden()) { if (DEBUG) Log.v(TAG, "Showing header at position " + position + " header=" + header); header.setHidden(false); - //Skip notifyItemInserted when init=true and insert the header properly! - if (init) performInsert(position, Collections.singletonList((T) header), false); - else addItem(position, (T) header); + //Insert header, but skip notifyItemInserted when init=true! + performInsert(position, Collections.singletonList((T) header), !init); return true; } return false; @@ -1488,6 +1549,7 @@ private boolean showHeaderOf(int position, @NonNull T item, boolean init) { *Headers can be shown or hidden all together.
* * @see #showAllHeaders() + * @see #setDisplayHeadersAtStartUp(boolean) * @since 5.0.0-b1 */ public void hideAllHeaders() { @@ -1543,25 +1605,6 @@ private boolean hideHeader(int position, IHeader header) { return false; } - /** - * Helper method to ensure that all current headers are hidden before they are shown again. - *This method is already called inside {@link #showAllHeadersWithReset(boolean)}.
- * This is necessary when {@link #setDisplayHeadersAtStartUp(boolean)} is set true and also - * if an Activity/Fragment has been closed and then reopened. We need to reset hidden status, - * the process is very fast. - * - * @since 5.0.0-b6 - * @deprecated Not used anymore. - */ - @Deprecated - private void resetHiddenStatus() { - for (T item : mItems) { - IHeader header = getHeaderOf(item); - if (header != null && !isExpandable((T) header)) - header.setHidden(true); - } - } - /** * Internal method to link the header to the new item. *Used by the Adapter during the Remove/Restore/Move operations.
@@ -4805,8 +4848,7 @@ public void onSaveInstanceState(Bundle outState) { outState.putString(EXTRA_SEARCH, this.mSearchText); //Save headers shown status outState.putBoolean(EXTRA_HEADERS, this.headersShown); - //TODO: enableStickyHeaders - //outState.putBoolean(EXTRA_STICKY, areHeadersSticky()); + outState.putBoolean(EXTRA_STICKY, areHeadersSticky()); } } @@ -4826,10 +4868,9 @@ public void onRestoreInstanceState(Bundle savedInstanceState) { showAllHeadersWithReset(true); } this.headersShown = headersShown; - //TODO: enableStickyHeaders -// if (savedInstanceState.getBoolean(EXTRA_STICKY) && !areHeadersSticky()) { -// setStickyHeaders(true); -// } + if (savedInstanceState.getBoolean(EXTRA_STICKY) && !areHeadersSticky()) { + setStickyHeaders(true); + } //Restore selection state super.onRestoreInstanceState(savedInstanceState); if (mScrollableHeaders.size() > 0) { From eb7923702b45019e3dfc05e7549f3f3a92c2791c Mon Sep 17 00:00:00 2001 From: Davide StedutoIn this case changes will NOT be animated: Warning! + * Convenience method of {@link #updateDataSet(List, boolean)} (You should read the comments + * of this method). + *
In this call, changes will NOT be animated: Warning! * {@link #notifyDataSetChanged()} will be invoked.
* * @param items the new data set @@ -491,9 +492,14 @@ public void updateDataSet(ListNote: Scrollable Headers and Footers (if existent) will be restored in this call.
+ * to improve performance on very big list. Should provide {@code animate=false} to + * directly invoke {@link #notifyDataSetChanged()} without any animations, if {@code stableIds} + * is not set! + *Note: + *
Note: This method cannot be overridden since the selection and the internal * methods rely on it.
* * @return the total number of items (headers and footers INCLUDED) held by the adapter - * @see #getItemCount(boolean) + * @see #getMainItemCount() * @see #getItemCountOfTypes(Integer...) * @see #isEmpty() * @since 1.0.0 @@ -573,17 +579,14 @@ public final int getItemCount() { * * Note: This method cannot be overridden since internal methods rely on it. * - * @param withHeadersFooters true to count also headers and footers, false to count only real items * @return the total number of items held by the adapter, with or without headers and footers, * depending by the provided parameter * @see #getItemCount() * @see #getItemCountOfTypes(Integer...)) * @since 5.0.0-rc1 */ - //TODO: Rename to getMainItemCount? - public final int getItemCount(boolean withHeadersFooters) { - return withHeadersFooters ? getItemCount() : - getItemCount() - mScrollableHeaders.size() - mScrollableFooters.size(); + public final int getMainItemCount() { + return getItemCount() - mScrollableHeaders.size() - mScrollableFooters.size(); } /** @@ -592,7 +595,7 @@ public final int getItemCount(boolean withHeadersFooters) { * @param viewTypes the viewTypes to count * @return number of the viewTypes counted * @see #getItemCount() - * @see #getItemCount(boolean) + * @see #getMainItemCount() * @see #isEmpty() * @since 5.0.0-b1 */ @@ -613,7 +616,7 @@ public final int getItemCountOfTypes(Integer... viewTypes) { * @param position the position limit where to stop counting (included) * @param viewTypes the viewTypes to count * @see #getItemCount() - * @see #getItemCount(boolean) + * @see #getMainItemCount() * @see #getItemCountOfTypes(Integer...) * @see #isEmpty() * @since 5.0.0-b5 @@ -1860,7 +1863,7 @@ public boolean isEndlessScrollEnabled() { * @since 5.0.0-rc1 */ public int getEndlessCurrentPage() { - return Math.max(1, mEndlessPageSize > 0 ? getItemCount(false) / mEndlessPageSize : 0); + return Math.max(1, mEndlessPageSize > 0 ? getMainItemCount() / mEndlessPageSize : 0); } /** @@ -2026,7 +2029,7 @@ public void run() { // When the listener is not set, loading more is called upon a user request if (mEndlessScrollListener != null) { if (DEBUG) Log.d(TAG, "onLoadMore invoked!"); - mEndlessScrollListener.onLoadMore(getItemCount(false), getEndlessCurrentPage()); + mEndlessScrollListener.onLoadMore(getMainItemCount(), getEndlessCurrentPage()); } } }); @@ -2065,7 +2068,7 @@ public void onLoadMoreComplete(@Nullable ListConvenience method of {@link #addItem(int, IFlexible)} with - * {@code position = getItemCount()}.
+ * {@code position = getMainItemCount()}. * * @param item the item to add * @return true if the internal list was successfully modified, false otherwise @@ -2773,7 +2776,7 @@ public boolean addItems(@IntRange(from = 0) int position, @NonNull ListWhen noMoreLoad OR onError OR onCancel, pass empty list or null to hide the - * progressItem.
+ * progressItem. When limits are set, endless feature will be disabled. To enable + * again call {@link #setEndlessProgressItem(IFlexible)}. * Optionally you can pass a delay time to still display the item with the latest information * inside. The message has to be handled inside the {@code bindViewHolder} of the item. *A {@link #notifyItemChanged(int, Object)} with payload {@link Payload#NO_MORE_LOAD}
From 0320868e9319c87926e645cd620f93ab12b9ba3b Mon Sep 17 00:00:00 2001
From: Davide Steduto Note: This setting is ignored if the header item has already an elevation. The
+ * header elevation overrides this setting. Note: This setting is ignored if the header item has already an elevation. The
+ * header elevation overrides this setting. INCLUDE the predefined layout after the RecyclerView widget, example:
diff --git a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/helpers/StickyHeaderHelper.java b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/helpers/StickyHeaderHelper.java
index 8f8dcf75..b05801f9 100644
--- a/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/helpers/StickyHeaderHelper.java
+++ b/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/helpers/StickyHeaderHelper.java
@@ -176,8 +176,8 @@ private void configureLayoutElevation() {
// 1. Take elevation from header item layout (most important)
mElevation = ViewCompat.getElevation(mStickyHeaderViewHolder.getContentView());
if (mElevation == 0f) {
- // 2. Default elevation
- mElevation = 21f;
+ // 2. Take elevation settings
+ mElevation = mAdapter.getStickyHeaderElevation();
}
}
From 56f72732d1b6a022b492a42d5c8d05838f70d8f5 Mon Sep 17 00:00:00 2001
From: Davide Steduto +4X;p447uKsz5f;cq6sw&f2
zb#--*RvcSM5IiG=MhQ~EjZHuh9mDL5D8`U8xZv$piV6!t)jcGBq7Zs9ovh;G*-udW
z4-V9_C%TIn2>h@z!6;DhxA*PV<{}}kHO|pwUrz#eVtuWngX X7?NA
zC#9`WJvK*2zmHc
20/02/2016 Sticky headers
*
22/04/2016 Endless Scrolling
*
13/07/2016 Update and Filter operations are executed asynchronously (high performance on big list)
+ *
25/11/2016 Scrollable Headers and Footers; Reviewed EndlessScroll
*/
@SuppressWarnings({"Range", "unused", "unchecked", "ConstantConditions", "SuspiciousMethodCalls", "WeakerAccess"})
public class FlexibleAdapter