7

Every time I call invalidate data my DIFF_UTIL is not used. The logs are not shown and the entire list is updated with new data causing the screen to move positions etc. Not sure what the issue is here.

I have PagedListAdapter with a LiveData<PagedList<Post>> postList and I call postList.getValue().getDataSource().invalidate(); to refresh my data.

I have DIFF_UTIL

 public static DiffUtil.ItemCallback<Post> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<Post>() {

                @Override
                public boolean areItemsTheSame(Post oldItem, Post newItem) {
                    Log.d(TAG, "areItemsTheSame with result: "
                            + (oldItem.getId() == newItem.getId()));
                    Log.d(TAG, "areItemsTheSame Old Item is: " + oldItem.toString());
                    Log.d(TAG, "areItemsTheSame New Item is: " + newItem.toString());
                    return oldItem.getId() == newItem.getId();
                }

                @Override
                public boolean areContentsTheSame(Post oldItem, Post newItem) {
                    Log.d(TAG, "areContentsTheSame with result: " + oldItem.equals(newItem));
                    Log.d(TAG, "areContentsTheSame Old Item is: " + oldItem.toString());
                    Log.d(TAG, "areContentsTheSame New Item is: " + newItem.toString());
                    return oldItem.equals(newItem);
                }
            };

I have my Adapter:

import...

public class PostAdapter extends PagedListAdapter<Post, PostAdapter.PostViewHolder> {
    interface...

    protected PostAdapter() {
        super(DIFF_CALLBACK);
    }

    @NonNull
    @Override
    public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater
                .from(parent.getContext()).inflate(R.layout.feed_card_view, parent, false);
        return new PostViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
        Post post = getItem(position);
        Log.d(TAG, "onBindViewHolder with position: " + position);
        if (post != null) {
            setUpPost(holder, post);
            Log.d(TAG, "onBindViewHolder with post: " + post.toString());
        } else {
            holder.clear();
            Log.d(TAG, "onBindViewHolder post is null");
        }
    }

    private void setUpPost(@NonNull PostViewHolder holder, @NonNull Post post) {
       ...do some things to set up post
    }

    class PostViewHolder extends RecyclerView.ViewHolder {
       Buttons etc..

        public PostViewHolder(View itemView) {
            super(itemView);
            button b = findviewbyid...
        }

        void clear() {
            button.invalidate()..
        }
    }
}

My Fragment

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_feed, container, false);
        feedViewModel = ViewModelProviders.of(getActivity()).get(FeedViewModel.class);

        RecyclerView recyclerView = v.findViewById(R.id.post_list);
        PostAdapter adapter = new PostAdapter(this);

        feedViewModel.postList.observe(this, pagedList -> {
            try {
                //refresh current list
                adapter.submitList(pagedList);
            } catch (Exception e) {}
        });


        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this.getActivity()));

        return v;
    }

My ViewModel

public class FeedViewModel extends AndroidViewModel {
    private LiveData<PagedList<Post>> postList;
    private FeedRepository feedRepository;

    public FeedViewModel(final Application application) {
        super(application);
        feedRepository = new FeedRepository();
        postList = feedRepository.getPosts();
    }

    public void refreshPosts() {
        feedRepository.refreshPosts();
    }

    ... 

    public LiveData<PagedList<Post>> getPostList() {
        return postList;
    }
}

The Repository

public class FeedRepository {
    private LiveData<PagedList<Post>> postList;

    private static final String TAG = FeedRepository.class.getName();
    private static final int NUMBER_OF_POSTS_TO_GET = 2;
    private static final boolean ENABLE_POST_PAGE_HOLDER = true;
    private static final int NUMBER_OF_POSTS_TO_GET_IN_ADVANCE = 50;
    private static final int NUMBER_OF_POSTS_ON_FIRST_LOAD = 20;

    public FeedRepository() {
        //Prefs on PagedList
        PagedList.Config myPagingConfig = new PagedList.Config.Builder()
                .setPageSize(NUMBER_OF_POSTS_TO_GET)
                .setEnablePlaceholders(ENABLE_POST_PAGE_HOLDER)
                .setPrefetchDistance(NUMBER_OF_POSTS_TO_GET_IN_ADVANCE)
                .setInitialLoadSizeHint(NUMBER_OF_POSTS_ON_FIRST_LOAD)
                .build();

        PostDataSourceFactory postDataSourceFactory = new PostDataSourceFactory();

        postList = new LivePagedListBuilder(
                postDataSourceFactory, myPagingConfig).build();
    }

    public LiveData<PagedList<Post>> getPosts() {
        return postList;
    }

    public void refreshPosts() {
        Log.d(TAG, "refreshPosts, invalidateDataSources()");
        postList.getValue().getDataSource().invalidate();
    }
}

DataSource.Factory

public class PostDataSourceFactory extends DataSource.Factory<String, Post> {
   public PostDataSourceFactory() {
    }

    @NonNull
    @Override
    public DataSource<String, Post> create() {
        PostDataSource postDataSource = new PostDataSource();
        return postDataSource;
    }
}

I following the google example here: https://developer.android.com/reference/android/arch/paging/PagedListAdapter.html

MobDev
  • 1,219
  • 1
  • 12
  • 25

2 Answers2

2

For anyone else having issue with the "DiffUtil.ItemCallback<*>()" part, I was having the same problem.

Every time I wanted to refresh my data, I would call invalidate() on the "DataSource" object, depending on the type of what data source you are inhering from, but the 2 callbacks "areItemsTheSame()" & "areContentsTheSame()" were not getting triggered.

So I came across this link: How to stop blinking on recycler view with architecture components paging library

What did the trick for me was to use an "AsyncPagedListDiffer" and when submitting the "pagedList". The below code is in Kotlin.

fun submitList(pagedList: PagedList<*?>) {
  pagedList.addWeakCallback(pagedList.snapshot(), object : PagedList.Callback() {
    override fun onChanged(position: Int, count: Int) {
    }

    override fun onInserted(position: Int, count: Int) {
      mDiffer.submitList(pagedList)
    }

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

  })
}

By adding this things my diff callback started to work

Matt Ke
  • 3,599
  • 12
  • 30
  • 49
Andi Al
  • 21
  • 1
  • 4
  • Works fine. I had the same issue which caused wrong data displayed : https://stackoverflow.com/questions/69426368/pagedlistadapter-doesnt-display-right-item-after-refreshing-sometimes-not-alw – Thomas Pires Oct 05 '21 at 18:45
0

You can Ctrl + Click on submitList function, and see what the core does inside.

public void submitList(final List<T> newList) {
    if (newList == mList) {
        // nothing to do if the newList & old List have the same address in memory 
        return;
    }
....
}

So, try to submit a new list data & recheck

submitList(new ArrayList<>(list))

For more info, you can check the full answer at this post

NamNH
  • 1,752
  • 1
  • 15
  • 37