How to stop blinking on recycler view with architecture components paging library
Asked Answered
E

5

21

I have a chat-like activity where I am using a RecyclerView with a PagedListAdaper to load a bunch of messages. I'm using a PositionalDataSource to load the data. The loading it's self works fine but when I send a message, I invalidate my datasource and the list gets remade. My problem is that it blinks when it does that:

this is how it looks

I have tried adding setHasStableIds(true) and overriding getItemId which would work on a simple adapter but it doesn't seem to work here. I also cannot seem to be able to just add an item to the getCurrentList() because it's not supported. Also, I'm not using a database, just making requests to a server.

So my questions are, is there a better way of doing this besides invalidating the data source? Is there a way to stop the list from blinking when sending a message? Or is this library just not suited for my chat activity?

Edit:

my diff callback

private val DIFF_CALLBACK: DiffCallback<MessageModel> = object : DiffCallback<MessageModel>() {
        override fun areItemsTheSame(@NonNull oldMessage: MessageModel, @NonNull newMessage: MessageModel) =
                oldMessage.id == newMessage.id


        override fun areContentsTheSame(@NonNull oldMessage: MessageModel, @NonNull newMessage: MessageModel) =
                oldMessage.equals(newMessage)
    }

Edit2 I fixed it:

So I managed to fix it by using PagedListAdapterHelper and setting it's list after the items loaded:

private var mHelper: PagedListAdapterHelper<MessageModel>? = null

init {
    mHelper = PagedListAdapterHelper(this, DIFF_CALLBACK)
    setHasStableIds(true)
}
fun setList(pagedList: PagedList<MessageModel>) {
    pagedList.addWeakCallback(pagedList.snapshot(), object:PagedList.Callback() {
        override fun onChanged(position: Int, count: Int) {

        }

        override fun onInserted(position: Int, count: Int) {
            mHelper?.setList(pagedList)
        }

        override fun onRemoved(position: Int, count: Int) {

        }

    })
}
Eurystheus answered 25/1, 2018 at 8:53 Comment(6)
what is PagedListAdapterHelper?Geosphere
@Eurystheus have you got success? i am also looking for thisMorice
PagedListAdapterHelper deprecated in Paging ComponentFoliate
@Eurystheus I'm sleepy tooEnchanting
@Eurystheus have you got success? i am also looking for thisAgora
@aj0822ArpitJoshi, hi~Do you find the way?Agora
D
4

PagedListAdapterHelper deprecated in Paging Component so we should use AsyncPagedListDiffer instead of it.

Code example:

    import android.arch.paging.AsyncPagedListDiffer;
    import android.arch.paging.PagedList;
    import android.arch.paging.PagedListAdapter;
    import android.support.annotation.NonNull;
    import android.support.v7.util.DiffUtil;
    import android.view.LayoutInflater;
    import android.view.ViewGroup;


    public class PagedItemsArrayAdapter extends PagedListAdapter<Item, MyViewHolder> {

            private final AsyncPagedListDiffer<Item> mDiffer;

            public PagedItemsArrayAdapter() {
                super(DIFF_CALLBACK);
                mDiffer = new AsyncPagedListDiffer<>(this, DIFF_CALLBACK);
                setHasStableIds(true);
            }

            @Override
            public long getItemId(int position) {
               Item item = mDiffer.getItem(position);
               return item.id;
            }

            @Override
            public int getItemCount() {
                return mDiffer.getItemCount();
            }

            @Override
            public void submitList(PagedList<Item> pagedList) {

                pagedList.addWeakCallback(pagedList.snapshot(), new PagedList.Callback() {
                    @Override
                    public void onChanged(int position, int count) {
                    }

                    @Override
                    public void onInserted(int position, int count) {
                        mDiffer.submitList(pagedList);
                    }

                    @Override
                    public void onRemoved(int position, int count) {

                    }
                });

            }


            @Override
            public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int position) {
                   Item item = mDiffer.getItem(position);
                   viewHolder.onBind(item); 
            }

            private static DiffUtil.ItemCallback<Item> DIFF_CALLBACK = new DiffUtil.ItemCallback<Item>() {
                @Override
                public boolean areItemsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
                    return oldItem.getId() == newItem.getId();
                }

                @Override
                public boolean areContentsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
                    return oldItem.equals(newItem);
                }
            };
Disparity answered 30/1, 2019 at 14:3 Comment(4)
I don't think this will work if orientation changes.Sommerville
The callback is never getting calledUnderslung
hi, I found th callback is never called, because addWeakCallback after onInserted, have any other [email protected]Agora
This should be the accepted answer. Thanks for this!Alicaalicante
O
3

Short answer: make sure to call the callback of PositionalDataSource<T>.loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<T> callback) synchronously in loadInitial without wrapping it in some kind of asynchronous success handler.

Explanation:

Blinking can be caused by asynchronous loads in your initial load i.e. in PositionalDataSource<T>.loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<T> callback).

The following will happen when you do so:

Your data source gets invalidated which leads to the creation of a new PagedList (when created with LivePagedListBuilder). This newly created paged list will be passed to your adapter but it is still empty because you didn't call the callback directly in your initial load. This will lead to an empty list for as long as it takes for your callback to be called. This ultimately results in a flicker effect.

Oral answered 25/3, 2018 at 18:11 Comment(3)
So you're saying that if I call the callback.onResult() on the main-thread, then the LivePagedListBuilder won't create a new empty PagedList instance, when the DataSource is invalidated?<br/> <br/> Because I've tried that and did a mHandler.post(callback.onResult()) ,where mHandler is from the MainLooper and it still created a new PagedList instance instead of using the old one. <br/> I took a look through the invalidate() code and to me it just seemed that it will always create a new PagedList when invalidated, but maybe I'm wrong.Quarterdeck
I am calling it synchronously and it still blinksEvelinaeveline
Used rx in loadInitial etc. Changed to blocking. Still blinks.Sommerville
O
2

You can disable the item animations.

mRecyclerView.setItemAnimator(null);
Opalina answered 11/7, 2018 at 19:51 Comment(4)
then animation is gone, but flickering stays. It shows for a second not initialized state.Evelinaeveline
For me it fixes the flickering, but it turns off all the other animations I wanted to keep (when an item is inserted, removed...)Intervene
also have a look at this question https://mcmap.net/q/660201/-implmenent-paging-library-3-0-filter-search-functionality/3496570Lapides
This solution doesn't stop the flickering after a datasource invalidation.Alicaalicante
B
0

you should use sync in loadInitial and loadAfter, ie:

mLoadState.postValue(NetworkState.Loading);
try {
    mLoadState.postValue(NetworkState.DONE);
    ResultMessage<List<Achievement>> info = call.execute().body();
    if (info != null && info.isSuccess()) {
        List<Achievement> list = info.getData();
        callback.onResult(list, null, FIRST_PAGE + 1);
    }
} catch (IOException e) {
    mLoadState.postValue(NetworkState.ERROR);
    mRetry.postValue(() -> loadInitial(params, callback));
    Log.d(TAG, "fail!");
}
Brescia answered 20/10, 2020 at 2:52 Comment(0)
S
0

We are using paging library 3 with room. We were getting flickering, blinking and scrolling effects after we wrote some next page data on database. Because we set the PagingConfig's initialLoadSize different than pageSize. We have started to use same value for both of them and our problem has been fixed.

Swashbuckler answered 3/2, 2021 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.