Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any Observers written In RecyclerView.Adapter to know if dataset has been changed?

Tags:

I have implemented my RecyclerView with it's Custom Adapter as follows

Global Declarations as follows

private LinearLayoutManager linearLayoutManager;
private int pastVisibleItems, visibleItemCount, totalItemCount;
private CustomRecyclerViewAdapter customRecyclerViewAdapter;

First I created Adapter Instance inside onCreate() method which has Empty Array inside it and set it to recyclerView

linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
    Utility.ItemDecorationConst);
recyclerView.addItemDecoration(dividerItemDecoration);
customRecyclerViewAdapter = new CustomRecyclerViewAdapter(getActivity());

recyclerView.setAdapter(customRecyclerViewAdapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

    visibleItemCount = linearLayoutManager.getChildCount();
    totalItemCount = linearLayoutManager.getItemCount();
    pastVisibleItems = linearLayoutManager.findFirstVisibleItemPosition();
    if (loading) {
        if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
            loading = false;
            customRecyclerViewAdapter.addProgressBarEntry();
            controller.getNextPage(PublisherAppContainerFragment.this);
        }
    }
}
});

After rendering complete View when I get data from AsyncTask for filling in recyclerView

I call following method of the Adapter to fill data

customRecyclerViewAdapter.addAll(myArray);

note : addAll() is not any overridden method

following is code of my CustomRecyclerViewAdapter

class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {
    ArrayList<MyModel> arrayList = new ArrayList<>();
    Context context;

    public CustomRecyclerViewAdapter(Context context) {
        this.context = context;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewHolder viewHolder = null;
        //inflated some view
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //binded data to holder
    }

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

    public void addAll(ArrayList myArray) {
        this.arrayList.addAll(myArray)
    }

    public void clear() {
        arrayList.clear();
    }
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public CardView cardView;

        public ViewHolder(View view) {
        super(view);
            this.cardView = (CardView) view.findViewById(R.id.card_view);
            this.cardView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
        //handle operations
        }
    }
}

So whenever I get data from AsynTask I call method addAll() and recyclerView works like charm.

Now, My question is how it's working very well even though I have never called notifyDataSetChanged() on the adapter. Are there any previously registered Observers for the adapter? who observes if the dataset which has been returned in public int getItemCount() has been changed?

As I have read from documentation

void notifyDataSetChanged ()

Notify any registered observers that the data set has changed.

that means even though there are some observers registered you need to notify them using notifyDataSetChanged(). Right?

I also called

boolean flag = customRecyclerViewAdapter.hasObservers();

to know if there are any observers registered? Flag is True.

So anyone would please help me understand how exactly these things work?

like image 892
Nikhil Avatar asked Aug 31 '16 06:08

Nikhil


Video Answer


2 Answers

If you look at the source of RecyclerView setAdapter call you will find a method setAdapterInternal(adapter, false, true);which is responsible for

Replaces the current adapter with the new one and triggers listeners.

This method is responsible for swapping the old adapter with the new one and internally it also registers for the custom Data Observer. This is the reason you are getting the flag as true

like image 195
Gautam Avatar answered Oct 11 '22 06:10

Gautam


Based on what I can see of your code, I would say that there are not any observers attached to your RecyclerView that are picking up changes and keeping the list updated. What is more likely is that you are just getting "lucky" as when you scroll through the list the layout manager is continually calling getItemCount() on the adapter to determine if it should show more items. Whenever you call addAll(), you silently update the item count and it just happens to appear that observers were notified of the changes.

This is definitely a bug, and you would more likely see its effects in your implementation if you were dependent on a particular observer to monitor some aspect of the list, or doing more than just appending new items to the bottom (for example altering or inserting between existing items). The correct implementation as you pointed out is to call notifyDataSetChanged() whenever the list is updated, or even better be more specific with what changed if you can. For example, you can use:

public void addAll(ArrayList myArray) {
    int positionStart = getItemCount() - 1;
    this.arrayList.addAll(myArray);
    notifyItemRangeInserted(positionStart, myArray.size());
}

public void clear() {
    int oldSize = getItemCount();
    arrayList.clear();
    notifyItemRangeRemoved(0, oldSize);
}
like image 21
happydude Avatar answered Oct 11 '22 06:10

happydude