Skip to content

Commit

Permalink
fix: Improper Item decorations when a data item was added, updated or…
Browse files Browse the repository at this point in the history
… removed from the list
  • Loading branch information
kaushiknsanji committed Nov 14, 2019
1 parent a28a408 commit 47df67d
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.recyclerview.extensions.AsyncDifferConfig;
import android.support.v7.recyclerview.extensions.AsyncListDiffer;
import android.support.v7.recyclerview.extensions.ListAdapter;
import android.support.v7.util.DiffUtil;
import android.support.v7.util.ListUpdateCallback;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
Expand Down Expand Up @@ -166,39 +169,7 @@ private void setupSwipeRefresh() {
private void setupRecyclerView() {
//Creating a Vertical Linear Layout Manager with the default layout order
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(requireContext(),
LinearLayoutManager.VERTICAL, false) {

/**
* Called when items have been added to the adapter. The LayoutManager may choose to
* requestLayout if the inserted items would require refreshing the currently visible set
* of child views. (e.g. currently empty space would be filled by appended items, etc.)
*
* @param recyclerView The {@link RecyclerView} this LayoutManager is bound to.
* @param positionStart The Start position from where the Items were added to the {@link RecyclerView}
* @param itemCount Number of Items added
*/
@Override
public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
if (getChildCount() > 0 && itemCount == 1) {
//When there are some items visible and number of items added is 1

//Getting the last item position in the RecyclerView
int positionLast = getItemCount() - 1;
if (positionLast > positionStart) {
//When the last item position is more than the start position
for (int index = positionStart; index <= positionLast; index++) {
//Remove all the views from RecyclerView Cache till the last item position
//so that the RecyclerView grows in size properly to accommodate the items
//with proper item decoration height
removeView(findViewByPosition(index));
}
//Auto Scroll to the item position in the end
recyclerView.smoothScrollToPosition(positionStart);
}
}

}
};
LinearLayoutManager.VERTICAL, false);

//Setting the Layout Manager to use
mRecyclerViewContentList.setLayoutManager(linearLayoutManager);
Expand Down Expand Up @@ -452,7 +423,7 @@ public void launchEditProductSales(int productId, ActivityOptionsCompat activity
/**
* {@link ListAdapter} class for RecyclerView to load the list of Products for Selling.
*/
private static class SalesListAdapter extends ListAdapter<SalesLite, SalesListAdapter.ViewHolder> {
private static class SalesListAdapter extends RecyclerView.Adapter<SalesListAdapter.ViewHolder> {

/**
* {@link DiffUtil.ItemCallback} for calculating the difference between two {@link SalesLite} objects.
Expand Down Expand Up @@ -504,6 +475,11 @@ public boolean areContentsTheSame(SalesLite oldItem, SalesLite newItem) {
return oldItem.equals(newItem);
}
};

//For the RecyclerView instance
private RecyclerView mRecyclerView;
//AsyncListDiffer for computing the difference between two lists in a background thread
private AsyncListDiffer<SalesLite> mDiffer;
//Stores the Typeface used for Product SKU text
private Typeface mProductSkuTypeface;
//Listener for User Actions on Product List items
Expand All @@ -517,13 +493,119 @@ public boolean areContentsTheSame(SalesLite oldItem, SalesLite newItem) {
* to receive event callbacks for User Actions on Item Views
*/
SalesListAdapter(Context context, SalesListUserActionsListener userActionsListener) {
super(DIFF_SALES);
//Registering the User Actions Listener
mActionsListener = userActionsListener;
//Reading the Typeface for Product SKU
mProductSkuTypeface = ResourcesCompat.getFont(context, R.font.libre_barcode_128_text_regular);
}

/**
* Called by RecyclerView when it starts observing this Adapter.
* <p>
* Keep in mind that same adapter may be observed by multiple RecyclerViews.
*
* @param recyclerView The RecyclerView instance which started observing this adapter.
*/
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
//When the RecyclerView is attached to this adapter, save the reference
mRecyclerView = recyclerView;
//Initialize the differ
setupDiffer();
}

/**
* Called when the {@link RecyclerView} starts observing this Adapter.
* Initializes the {@code mDiffer} for computing the difference between
* two lists of {@link SalesLite} objects in a background thread.
*/
private void setupDiffer() {
//Initialize with ListUpdateCallback and AsyncDifferConfig
mDiffer = new AsyncListDiffer<SalesLite>(new ListUpdateCallback() {
/**
* Called when {@code count} number of items are inserted at the given position.
*
* @param position The position of the new item.
* @param count The number of items that have been added.
*/
@Override
public void onInserted(int position, int count) {
//Notify the changes to the adapter
notifyItemRangeInserted(position, count);
//Rebuild item decorations on RecyclerView
invalidateItemDecorations();
}

/**
* Called when {@code count} number of items are removed from the given position.
*
* @param position The position of the item which has been removed.
* @param count The number of items which have been removed.
*/
@Override
public void onRemoved(int position, int count) {
//Notify the changes to the adapter
notifyItemRangeRemoved(position, count);
//Rebuild item decorations on RecyclerView
invalidateItemDecorations();
}

/**
* Called when an item changes its position in the list.
*
* @param fromPosition The previous position of the item before the move.
* @param toPosition The new position of the item.
*/
@Override
public void onMoved(int fromPosition, int toPosition) {
//Notify the changes to the adapter
notifyItemMoved(fromPosition, toPosition);
//Rebuild item decorations on RecyclerView
invalidateItemDecorations();
}

/**
* Called when {@code count} number of items are updated at the given position.
*
* @param position The position of the item which has been updated.
* @param count The number of items which has changed.
*/
@Override
public void onChanged(int position, int count, Object payload) {
//Notify the changes to the adapter
notifyItemRangeChanged(position, count, payload);
//Rebuild item decorations on RecyclerView
invalidateItemDecorations();
}

/**
* Rebuilds the item decorations on {@link RecyclerView}
*/
private void invalidateItemDecorations() {
if (mRecyclerView != null) {
mRecyclerView.postDelayed(mRecyclerView::invalidateItemDecorations, 2);
}
}

},
//Build AsyncDifferConfig instance with the provided 'DiffUtil.ItemCallback' implementation
new AsyncDifferConfig.Builder(DIFF_SALES).build()
);
}

/**
* Called by RecyclerView when it stops observing this Adapter.
*
* @param recyclerView The RecyclerView instance which stopped observing this adapter.
*/
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
//As the RecyclerView is detached from the Adapter, clear the reference
mRecyclerView = null;
}

/**
* Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
* an item.
Expand Down Expand Up @@ -561,6 +643,40 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(position, salesLite);
}

/**
* Retrieves the {@link SalesLite} object at the specified position
* in the list, managed by {@code mDiffer}
*
* @param position The position of the required Item in the list
* @return The {@link SalesLite} object found at the specified position if any; else {@code null}
*/
private SalesLite getItem(int position) {
return mDiffer != null ? mDiffer.getCurrentList().get(position) : null;
}

/**
* Returns the total number of items in the data set held by the adapter.
*
* @return The total number of items in this adapter.
*/
@Override
public int getItemCount() {
return mDiffer != null ? mDiffer.getCurrentList().size() : 0;
}

/**
* Submits a new list of {@link SalesLite} objects to be diffed, and displayed. If a list
* is already being displayed, a difference will be computed on a background thread, which
* will dispatch Adapter.notifyItem events on the main thread.
*
* @param salesList The new list of {@link SalesLite} objects to be displayed.
*/
void submitList(ArrayList<SalesLite> salesList) {
if (mDiffer != null) {
mDiffer.submitList(salesList);
}
}

/**
* ViewHolder class for caching View components of the template item view 'R.layout.item_sales_list'
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
Expand Down Expand Up @@ -177,14 +178,9 @@ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemC
if (getChildCount() > 0 && itemCount == 1) {
//When there are some items visible and number of items added is 1

if (positionStart == getItemCount() - 1 && getItemCount() > 1) {
//When there are more than one Item View and the Item View
//added is in the last position

//Remove the previous Item View cache from RecyclerView to reload the Item View
//with proper item decoration height
removeView(findViewByPosition(positionStart - 1));
}
//Remove all the Item Views from RecyclerView to reload the Item Views
//with proper item decoration height
new Handler().postDelayed(this::removeAllViews, 2);
}
}

Expand All @@ -198,18 +194,12 @@ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemC
@Override
public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
if (getChildCount() > 0 && itemCount == 1) {
//When there are some items visible and number of items added is 1
//When there are some items visible and number of items removed is 1

if (positionStart == getItemCount() && getItemCount() > 1) {
//When there are more than one Item View and the Item View
//removed is from the last position

//Remove the previous Item View cache from RecyclerView to reload the Item View
//with proper item decoration height
removeView(findViewByPosition(positionStart - 1));
}
//Remove all the Item Views from RecyclerView to reload the Item Views
//with proper item decoration height
new Handler().postDelayed(this::removeAllViews, 2);
}

}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
Expand Down Expand Up @@ -174,14 +175,9 @@ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemC
if (getChildCount() > 0 && itemCount == 1) {
//When there are some items visible and number of items added is 1

if (positionStart == getItemCount() - 1 && getItemCount() > 1) {
//When there are more than one Item View and the Item View
//added is in the last position

//Remove the previous Item View cache from RecyclerView to reload the Item View
//with proper item decoration height
removeView(findViewByPosition(positionStart - 1));
}
//Remove all the Item Views from RecyclerView to reload the Item Views
//with proper item decoration height
new Handler().postDelayed(this::removeAllViews, 2);
}
}

Expand All @@ -195,18 +191,12 @@ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemC
@Override
public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
if (getChildCount() > 0 && itemCount == 1) {
//When there are some items visible and number of items added is 1
//When there are some items visible and number of items removed is 1

if (positionStart == getItemCount() && getItemCount() > 1) {
//When there are more than one Item View and the Item View
//removed is from the last position

//Remove the previous Item View cache from RecyclerView to reload the Item View
//with proper item decoration height
removeView(findViewByPosition(positionStart - 1));
}
//Remove all the Item Views from RecyclerView to reload the Item Views
//with proper item decoration height
new Handler().postDelayed(this::removeAllViews, 2);
}

}
};

Expand Down

0 comments on commit 47df67d

Please sign in to comment.