Custom cursor adapter calling bindView multiple times
Asked Answered
T

3

10

I have been suffering this issue for months and months (but now I am performance tuning). However, I now desperately need to know why my adapter feels it is necessary to run bindView up to 4 times on a record.

I have a custom cursor adapter that populates a gridview.

Some debug to show what's going on:

03-08 14:46:47.980: I/AdapterCursorGrid(20724): newView()
03-08 14:46:48.470: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:48.570: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:48.570: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:48.600: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:48.690: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:49.490: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:49.501: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:49.501: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:49.521: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:49.521: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:50.320: I/AdapterCursorGrid(20724): newView()
03-08 14:46:51.170: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:51.180: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.180: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:51.180: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:51.190: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:51.190: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.190: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:51.200: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:51.870: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:51.896: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.896: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:51.900: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:51.900: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:51.900: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.900: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:51.900: D/AdapterCursorGrid(20724): bindView() Picture creation...

The "Avatar empty..." and "Picture creation..." is simply debug that tells me it is processing and updating 2 particular ImageViews.

Why o why is bindView running so many times? What are the reasons for this and what can I do to resolve this?

Logically speaking I expect bindView to run once (and once each time the adapter is updated), am I wrong in thinking this?

Tip answered 8/3, 2013 at 15:21 Comment(2)
Whatever "Picture creation" is should not be on the main application thread. bindView() needs to return in less than 1ms on a GridView, regardless of whether you feel that it is being called too many times for other reasons. Spending ~600ms in "Picture creation" is very bad. Even if bindView() were called only once per cell, it would be called MxN times (M rows, N columns) when the GridView is initially populated, meaning that your UI is frozen for several seconds at the outset.Mapp
@Mapp Yes, i'll achieve this with an aync task.Tip
C
15

The operating system may call bindView multiple times so that it can measure and lay out the list correctly. This is not a bug so much as the way it has to be. This, along with smoothness of scrolling, is why bindView implementations need to be as efficient as possible. There are some nice tips and tricks you can use detailed on the Android Developers Page.

Caffey answered 8/3, 2013 at 15:30 Comment(5)
Shall I assume this is relevant for a gridView?Tip
Yes. Android may call this as many times as it takes to measure and layout the container properly.Caffey
I currently implement the ViewHolder pattern. I will update the image processing in an async task and then I will review bindView again. The troubling part is the idea that my async task will be kicked off 4 times in its current state. Any other suggestions as to why it is being called 4 times? The GridView is set to fill_parent for both height and width. Scratching head.Tip
The bottom line is that it doesn't really matter why it's being called any number of times. That is not something that you can change as it's built into the operating system. All you can do is make it as smooth as possible. This means using a ViewHolder, using an AsyncTask to load images off of the UI thread and caching those images so you don't need to hit the network to load the same image over and over.Caffey
I agree! This is what I have now achieved. Thank you.Tip
N
12

I also found that bindView was being called many more times than expected. My ListView height was set to wrap_content. After changing it to match_parent the number of calls reduced drastically.

Credit for this solution goes to the answer for this question Custom CursorAdapater's bindView called 77 times...have I done something wrong?

Nambypamby answered 10/6, 2014 at 11:23 Comment(3)
why hasn't this answer been upvoted yet? This is an awesome solution.Dulcie
Well it's not technically a solution to the question asked. I just wanted to highlight that if you're seeing bindView being called an inordinate amount of times, it may be because height is being mistakenly set to wrap_content.Nambypamby
Thanks, you saved my day!! This answer should be the accepted answer :)Circus
P
0

One thing I discovered with ImageView is that changing the image will cause the ImageView to request a layout unless the new and old image are exactly the same size.

Internally the ImageView uses getIntrinsicWidth() and getIntrinsicHeight() to work out whether or not to request a layout.

Try something like this (although it would make more sense to send the current width/height to your async load and resize the bitmap before returning from the background):

public void replaceBitmap(ImageView iv, Bitmap bitmap) {
    Drawable current = iv.getDrawable();
    if (bitmap.getWidth() != current.getIntrinsicHeight()
            || bitmap.getHeight() != current.getIntrinsicHeight()) {
        bitmap = Bitmap.createScaledBitmap(bitmap,
            current.getIntrinsicWidth(), current.getIntrinsicHeight(), true);
    }
    iv.setImageBitmap(bitmap);
}
Photoflood answered 26/4, 2014 at 10:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.