views:

118

answers:

1

I have a ListActivity whose ArrayAdapter-subclass creates a (droid-fu) WebImageView. The problem is that whenever the list grows longer than can be displayed on a single page, whatever it is that calls getView() always starts from 0 and counts to 4 or 5 (depending on whether or not partial rows are visible), even when it's actually displaying an item like #12 or 14 at the top of the screen... but onClick() registers properly.

Put another way, suppose the list has 27 rows in it, and I have the app littered with Log.d statements to document everything it's doing. When the ListActivity begins, I can see it call getView with position = 0, 1, 2, 3, 4, and 5. If I touch one of the rows, onClick() fires with the correct position 0..4 (5 is offscreen) as well. So far, so good.

Now, flick the list upward by a few rows and watch the log output. Once again, getView() gets called for position=0, 1, 2, 3, 4, and 5. But if I touch one of the rows, onClick() fires for a position like 9 or 14 (which, in fact, is correct).

In other words, somewhere along the way, whatever is responsible for making the call to the ListAdapter's getView() method to render the list is forgetting about its current offset, and is always counting from 0 instead of from whatever row is, in fact, at the top of the screen.

Does anybody have any idea where to even begin looking for what might be going wrong here? Perhaps a clue about where those calls to getView() are coming from, and how the value of the position arg used for that call is determined? God forbid, is it possible to put a breakpoint at the start of getView(), then step BACKWARDS with Eclipse to see where the call to the ArrayAdapter's getView() method came from?

Update: forgot about github & managed to get a copy of my program now:

private class ChatInfoAdapter extends ArrayAdapter<ChatInfo> {
    private ArrayList<ChatInfo> items;

    public ChatInfoAdapter(Context context, int resourceID, ArrayList<ChatInfo> items) {
        super(context, resourceID, items);

        this.items = items; 
        // ^^^hmmm... could THIS be the problem somehow?
    }   

    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("getView", "postion=" + position);
        View v = convertView;
        if (v==null) {
            LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.row, null);
        }

        ChatInfo info = items.get(position); 
        // ^^^ grabbing from local "items", not superclass' ArrayList. Hmmm...
        if (info != null) {
            TextView x = (TextView) v.findViewById(R.id.x);
            TextView y = (TextView) v.findViewById(R.id.y);
            //...
            String url = info.getUrl();
            //...
            WebImageView wiv = new WebImageView(getBaseContext(), iconUrl, true, arg, arg2);
            // ^^^ my slightly-hacked droid-fu WebImageView that takes some extra args
            // Even if I completely screwed it up, this class should have no effect on
            // the value of "position" when getView gets called at runtime, right?
            // Or is there a method of ImageView that might be getting mangled somewhere
            // along the line that SHOULD be reporting its position, but instead might
            // be returning 0 (which might explain why position always starts from 0).
            LinearLayout placeholder = (LinearLayout) v.findViewById(R.id.placeholder);
            placeholder.addView(wiv);
            v.setTag(info);
        }
    }

    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        Log.d("onListItemClick", "position=" + position);
        //...
    }
}

So... going back to the example list with ~20+ items... when it first appears, I see a flurry of activity like this in the log:

getView: position=0 getView: position=1 getView: position=2 getView: position=3 getView: position=4 getView: position=5 (possibly in a different order, or repeated a few times)

At that point, if I click on the last row, I'll see a message in the log like: onListItemClick: position=4

So far, so good. But if I scroll the list a bit, so an item ~2/3 of the way down is shown at the top of the screen, and I click the row at the bottom of the screen, I'll see a flurry of activity like THIS in the log:

getView: position=0 getView: position=1 getView: position=2 getView: position=3 getView: position=4 getView: position=5 (possibly in a different order, or repeated a few times) ... onListItemClick: position=20

In other words, it knows I clicked row 20, but the positions shown by the calls to getView never make it above position=6 or position=7, and they always begin from position=0.

(added other comments below, and made video illustrating problem)

A: 

How exactly do you check what positions are being used with getView()? ListView would not work at all if getView() did not receive the correct positions. And from what you are saying your list is displaying the correct items so the adapter must have received the correct positions in getView(). Are you talking about getChildAt()/getChildCount() maybe?

Romain Guy
@Romain: If one were to understand and read up on how ListViews are actually drawn on the screen what class in the Android source code should one refer to? Thanks.
Samuh
ListView.java :)
Romain Guy
Update:I'm now mostly convinced this is a bug in droid-fu WebImageView (or one of the classes it depends on). I made a sequence of 22 thumbnail-sized jpeg images, put them on the server with sequential filenames, and temporarily replaced the code that was populating the list with server data so it created a list with 21 elements, each of whose image was in the form d-NNN_thumb.jpg, with associated note equal to "Note NNN" (where "NNN" is 0..21). When I ran it, I confirmed that everything besides the WebImageView is rendering properly. More importantly, I discovered something I didn't
Bitbang3r
Notice last night or this morning when observing the output... it IS calling getView() with the offset of the row that's about to be revealed. HOWEVER... immediately after doing that, it proceeds to call it at least 5 more times, and always fetches offsets 0 through 5 when it does. So... on one hand, there's increasing evidence that it's a bug in droid-fu WebImageView. However, there's also compelling evidence that something is inducing the ListActivity to correctly fetch the next row, then flush everything and fetch the first 5 rows (0..4) *anyway* in no obvious order.
Bitbang3r
Update: here's a screen capture of the bug manifesting itself in the Android Emulator made using Wink. The big red digits are the jpeg images named d-NNN_thumb.jpg I mentioned earlier. The two gray numbers to the right are generated by the same object that allegedly generated the URL for the red-digit image on the left. This shows that either the droid-fu cache is returning the wrong image, or somehow the "position" value is getting changed between the time it determines the ArrayList source row and time time it gets passed to getView as the position arg.http://tinyurl.com/36bs9zb
Bitbang3r
Is your ListView's height set to wrap_content? If so, that's why you are observing this. Using wrap_content with ListView is very wrong and very costly. Also I have no clue what the hell is this Droid-Fu WebImageView you keep talking about :)
Romain Guy
Ah, sorry. I just kind of assumed everyone knew what it was. Basically, Matthias Käppler wrote a cool library that automates the whole "fetch the image over the internet in the background, displaying a placeholder animation in the meantime" process (among other things) -- http://brainflush.wordpress.com/2009/11/23/droid-fu-part-2-webimageview-and-webgalleryadapter/Checking height now...
Bitbang3r
Nope. It's fill_parent: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ListView android:id="@+id/android:list" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </LinearLayout>
Bitbang3r
@Romain: duh! :)
Samuh