0

I have a RecyclerView.Adapter together with a list of items. At some point I will make some changes to this list and then I will call notifyDataSetChanged() (or another similar method). How do I know how many ViewHolders will be created prior to making this call? Or how many times onBindViewHolder will be called? Ideally I should know one of these numbers before calling this notify method, but I could also manage with receiving a callback when the adapter finishes to make all the work (inflations, bindings). From my experiments, the adapter is not done with the work right after calling notifyDataSetChanged.

I need to know how many items will be or were inflated. Ideally 'will be'. Some ideas on how I can achieve this?

UPDATE

Stumbled upon: Is there a callback for when RecyclerView has finished showing its items after I've set it with an adapter?, which gave me an answer to the second thing I asked: "I could also manage with receiving a callback when the adapter finishes to make all the work (inflations, bindings)."

I made some modifications to the accepted answer and the following Log only displays after all the recycler's items were inflated (at least in my app):

RecyclerView(context).apply {
layoutManager = object: LinearLayoutManager(context, VERTICAL, false) {
    override fun onLayoutCompleted(state: RecyclerView.State?) {
        super.onLayoutCompleted(state)
        val firstVisibleItemPosition = findFirstVisibleItemPosition()
        val lastVisibleItemPosition = findLastVisibleItemPosition()
        if(firstVisibleItemPosition == -1 || lastVisibleItemPosition == -1)
            return
        Log.d("RECYCLER", "all sync inflations done")
    }
}

}

daniyelp
  • 915
  • 1
  • 11
  • 26
  • 1
    going to ask the obvious question for my own interest, but why do you need to know this ? – a_local_nobody Oct 16 '21 at 16:45
  • @a_local_nobody I want to inflate the RecyclerView items asynchronously. I made them do some callbacks when their asynchronous inflation is done but I don't know for how many items do I need to wait for, so I can discard a progress bar that I started prior updating the RecyclerView. – daniyelp Oct 16 '21 at 16:58
  • `but I don't know for how many items do I need to wait for` why not ? if every item fires a callback to start doing something, then you can keep track of the number of callbacks, right ? as answered below, it might be better to ask a question explaining what you want to achieve, instead of being stuck on a particular solution you're trying to create – a_local_nobody Oct 16 '21 at 16:59
  • @a_local_nobody Yeah. I keep track of how many callbacks are being called. But how do I know if I received all the callbacks? – daniyelp Oct 16 '21 at 17:04

1 Answers1

1

How do I know how many ViewHolders will be created prior to making this call? Or how many times onBindViewHolder will be called?

Sorry, there is no way to know that. It will depend on all sorts of variables which are not known until rendering is being done.

For example, suppose that your RecyclerView is a vertically-scrolling list of rows, and each row shows some text in a TextView. How many rows will be needed will depend on:

  • What the text is, which could be short or long
  • What the row layout is and how it calls for that text to be rendered (font, font scale, padding, margin, etc.)
  • What the font scale is
  • What the RecyclerView size is (which in turn varies based on things like window size, which in turn varies based on things like screen size and whether we are in split-screen or multi-window mode, etc.)

And, in terms of how many ViewHolder objects will be instantiated, that will also depend on whether this is the first time the RecyclerView has been shown or whether you are updating an existing one with new data.

And that is just for very simple rows. More complex rows would add more dependencies.

Ideally I should know one of these numbers before calling this notify method

That is unrealistic. My guess it that is unnecessary. You might consider asking a separate Stack Overflow question where you explain why you feel that you need this, and perhaps we can suggest alternative approaches to solving your perceived problem. IOW, your question feels like an XY problem, where you have guessed at a solution to some other problem.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Very good points. But I also said `I could also manage with receiving a callback when the adapter finishes to make all the work (inflations, bindings).` I think this realistic though. A callback when the Recyclerview `has been updated`. Look for the second post comment to see what I need this for. – daniyelp Oct 16 '21 at 17:11
  • @daniyelp: "I could also manage with receiving a callback when the adapter finishes to make all the work (inflations, bindings)" -- sorry, but I am not aware of such a callback. "I want to inflate the RecyclerView items asynchronously" -- if you literally mean "inflate", `RecyclerView` does not really support that AFAIK. There are ways to *populate* the contents of an item asynchronously, as you can see with Glide, Picasso, and similar image loading libraries. – CommonsWare Oct 16 '21 at 17:22
  • @daniyelp: "But how do I know if I received all the callbacks?" -- there is no "all the callbacks". Most likely, the user is able to scroll your `RecyclerView` at any point. View holders will be created and/or recycled as needed to accommodate that, and that will trigger fresh callbacks. – CommonsWare Oct 16 '21 at 17:23
  • Found `LayoutManager`'s `onLayoutCompleted` method. It seems that it does the job. See my update above. – daniyelp Oct 17 '21 at 20:38
  • @daniyelp: If you think that will work for you, great! Personally, I am skeptical that it will hold up, unless you are somehow blocking user input during this process. – CommonsWare Oct 17 '21 at 20:41
  • Why would I block it? – daniyelp Oct 17 '21 at 20:43
  • 1
    @daniyelp: You would block it to avoid other modifications to the state of the `RecyclerView` that might cause the `LayoutManager` to have to do work. There is nothing in `onLayoutCompleted()` that ties it back to any particular action (changing the list contents, user swipe gestures, window resizing, etc.). Yet, AFAICT, you seem to need that sort of tie to achieve your stated objectives. If you are able to pull that off, great! – CommonsWare Oct 17 '21 at 20:51