ListView getChildAt returning null for visible children
Asked Answered
S

9

69

I'm getting some strange behavior from a listview/the getChildAt method.

I have a HashSet, iconsToUpdate, of icons that have been changed in the database. I want to iterate over the visible rows to see if any of their icons need to be updated to reflect the new icons. I don't need to test the icons not currently in view as they will be drawn properly when rendered.

My problem is that getChildAt is returning null when it seems like it shouldn't. I know that getChildAt can only return views that are currently visible, but it is returning null for some of the visible rows.

Here is my code that iterates over the visible rows:

Logger.debug("First visible index: " + f_listView.getFirstVisiblePosition());
Logger.debug("Last visible index: " + f_listView.getLastVisiblePosition());
for (int i = f_listView.getFirstVisiblePosition(); i <= f_listView.getLastVisiblePosition(); i++) {
    String tag = "asdf"; // Remove when bug is fixed.
    if (f_listView == null) {
        Logger.debug("f_listView is null");
    } else if (f_listView.getChildAt(i) == null) {
        Logger.debug("Child at index " + i + " is null");
    } else {
        tag = (String) f_listView.getChildAt(i).getTag();
        Logger.debug("Successful at index " + i + ", tag is: " + tag);
    }
    if (iconsToUpdate.contains(tag)) {
        setIcon(i, f_aim.getInHouseIcon(tag));
    }
}

Here is the log corresponding to a run of this loop:

D/...: First visible index: 3
D/...: Last visible index: 8
D/...: Successful at index 3, tag is: ...
D/...: Successful at index 4, tag is: ...
D/...: Successful at index 5, tag is: ...
D/...: Child at index 6 is null
D/...: Child at index 7 is null
D/...: Child at index 8 is null

It should be noted that the first and last visible indexes are being correctly reported, as I am viewing rows 3-8 when I run this. Rows 6, 7, 8 are being rendered properly. How are they being displayed if they are null?

Also, I do not know if this is important, but row 5 is the last visible row when I am at the top of the listview.

Any info as to why these rows are being returned as null would be greatly appreciated.

Thanks!

Socialism answered 20/7, 2011 at 18:26 Comment(0)
W
137

listView.getChildAt(i) works where 0 is the very first visible row and (n-1) is the last visible row (where n is the number of visible views you see).

The get last/first visible return the position in the dataAdapter you have. So you since you start at position 3, with what looks like 6 visible views, that's when get for positions 6-8 you get null.

In your example getChildAt(0) would return position 3. What I usually do is store the position on my views so I can lookup on my dataAdapter later if I need values.

I think your for loop should look like this:

for (int i = 0; i <= f_listView.getLastVisiblePosition() - f_listView.getFirstVisiblePosition(); i++)
Wellintentioned answered 20/7, 2011 at 18:55 Comment(7)
Cool, glad I can help. Please accept too if you can, I sure do love getting points :)Wellintentioned
Aha! Thanks. Would be useful if the documentation made clear that getChildAt(i) uses the VISIBLE position instead the logical position. Would have saved quite a few hairs on my head.Jann
Exactly, this is not described in documentation. @CNick: any references for this?Locklear
I tried this same for loop, but it returned me null. Any thoughts on this?Hannahhannan
wow, you fixed a bug i've been working on for an entire half-dayEpistasis
Shouldn't here be i < f_listView.getLastVisiblePosition() - f_listView.getFirstVisiblePosition() --> "<" instead of "<=" ? Take empty listview for example, in the case of "<=" you will get one execution of loop and should be none...Righteous
Excellent! Solved my problem.Sacral
C
14

Try

f_listView.getChildAt(positionOfChildYouWantGet - f_listView.getFirstVisiblePosition());
Clareclarence answered 9/5, 2015 at 18:11 Comment(1)
careful answer,Chequerboard
F
3

When you call listView1.getLastVisiblePosition() and listView1.getFirstVisiblePosition(), the listview returns the positions of the items that are partially visible in the listview. For example, the first item may be half visible and the last item may be half visible. In this case, even though you can see part of the item in the listview, the adapter has not yet called the getView() function for the item and therefore the item is still considered null.

Finial answered 20/7, 2011 at 18:51 Comment(1)
Can anyone confirm whether this is true?Flat
M
2

In my case i have a listView and the first item is full visible but when I do getFirstVisiblePosition() the result is 1 !

It gets more weird when I scroll and make the first item half visible then my getView show that getFirstVisiblePosition() is 0.

this my code :

public View getView(final int position, View convertView, final ViewGroup parent) {
        row = convertView;
        final AtomPaymentHolder holder = new AtomPaymentHolder();

        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, parent, false);
        row.setTag(items.get(position));
        holder.songitem = items.get(position);
        final int firstPosition = MainActivity.listviewSong.getFirstVisiblePosition() - MainActivity.listviewSong.getHeaderViewsCount(); 
        final int lastPosition = MainActivity.listviewSong.getLastVisiblePosition();

        if(MusicService.indexService>= firstPosition && MusicService.indexService<= lastPosition){
            MainActivity.listviewSong.getChildAt(MusicService.indexService-firstPosition).setBackgroundColor(getContext().getResources().getColor(R.color.pressed_color));
        }

(when I selected the next item and scroll, it works good)

Midships answered 27/8, 2015 at 9:41 Comment(0)
T
0

They are partially visible maybe. But when the getView() tries to recycle the views it gets FULLY visible ones other takes them as null. for example if you scroll the list and the top item is partially visible and the bottom is partially visible so these are nulls but you still see them.

Telethermometer answered 20/7, 2011 at 18:41 Comment(2)
Hmmm, well, all rows 3-8 are completely visible. Wouldn't be able to have three consecutive partially visible rows, so I think there is something more going on here. Thanks for the response though!Socialism
Try starting from i=0 ? and check the outputTelethermometer
C
0

I tried by ever mean in OnListItemClickListener(),but fails. At last I made some modification in my customized adapter for listview. Here in getView(),I apply clickListener on the item which i added into the list frequently. n do all required functionality there. Here is my Code, where i add Image View in the list n so apply listener on imageview.

I Think it will help those who want to change the color when specific List Item is >selected. Go For it..

In getView() of customized Adapter //---------------------------------code------------------------------------------


LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

View rowView = inflater.inflate(R.layout.icon_image_layout, parent, false); ImageView imageView = (ImageView) rowView.findViewById(R.id.Icon_ImageView); imageView.setClickable(true); final int pos=position; imageView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub // TODO Auto-generated method stub try{ if(previous_view!=null) previous_view.setBackgroundColor(Color.WHITE); }catch (Exception e) { System.out.println("Exception Occurs Previous View"); } v.setBackgroundColor(Color.RED); MainActivity.imageView.setImageResource(MainActivity.Image_Name[pos]); previous_view=v; return false; } });
Cozenage answered 2/5, 2013 at 7:59 Comment(0)
T
0

I know this is very old post. But I'm answering because people are still looking for a work around on ListView getChildAt() null pointer exception.

This is because the ArrayApdater is REMOVING and RECYCLING the views that are not visible yet on the ListView because of height. So that if you have 10 item views, and ListView can display 4 - 5 at a the time :

The Adapter REMOVE the item views at position 5 to 9, so that any attempt to adapter.getChildAt(5... to 9) will cause null pointer exception

The Adapter also RECYCLE the item view, so that any reference you made on position 3 for example will be lost when you scroll down to 5 to 9, and also any Input that you make on position 3 (EditText, Checkbox, etc.) will be recycled when you scroll down to 5 to 9 and will be reused at another position later (ex position 1, 2 or 3, etc.) with the same value

The only way I found to control this is to forget about getting the View and to have :

Attribute HashMap<Integer, ImageView> iconViews or any type you want for handling the values you want to use for each item on the list. The first type must be unique for item like item->getId() or position. Initialize it with new HashMap<>() in the Constructor; in getViews make iconViews.put(position, iconView); Prevent Adapter from using recycled convertView, remove condition if(convertView == null) so that adapter always inflate a brand new view instance. Because the view instance is new each time, you must set the value from HashMap each time also like if it already contains the key if(iconViews.containsKey(position)){iconView = iconViews.get(position))};. Probably in this case there is not tons of Items, so that smooth scrolling won't be a must.

And finally create public methods to get the Values outside of Adapter passing item->getId() Integer as parameter. Ex : public ImageView getIconViewAt(int position) { return iconViews.get(position); } . It will be easy then to select Item from Adapter

See more from my answer.

Twohanded answered 27/5, 2018 at 15:19 Comment(0)
A
0

Try

if (f_listView.getChildCount() > 0){
           f_listView.getChildAt(i) 
}
Afterguard answered 17/6, 2022 at 16:7 Comment(0)
W
-2

Isn't it because you are saying

f_listView.getChildAt(i) 

And you should be retrieving the item at that position?

f_listView.getItemAtPosition(i) 
Whitman answered 20/7, 2011 at 18:49 Comment(2)
I think not. I believe that getItemAtPosition gets the data at that row (from the adapter), not the viewProvence
Yes, steelbytes. In case of CursorAdapter it returns cursor.Ld

© 2022 - 2024 — McMap. All rights reserved.