ListView getChildAt returning null for visible children

AndroidListviewNull

Android Problem Overview


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!

Android Solutions


Solution 1 - Android

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++) 

Hope this helps.

Solution 2 - Android

Try

f_listView.getChildAt(positionOfChildYouWantGet - f_listView.getFirstVisiblePosition());

Solution 3 - Android

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.

Solution 4 - Android

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)

Solution 5 - Android

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.

Solution 6 - Android

>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;
		}
	});

Solution 7 - Android

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.

Solution 8 - Android

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) 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChrisView Question on Stackoverflow
Solution 1 - AndroidC NickView Answer on Stackoverflow
Solution 2 - AndroidJorge MorenoView Answer on Stackoverflow
Solution 3 - AndroidA. AbiriView Answer on Stackoverflow
Solution 4 - AndroidKhalil KitarView Answer on Stackoverflow
Solution 5 - AndroidNikola DespotoskiView Answer on Stackoverflow
Solution 6 - AndroidAnju DahiyaView Answer on Stackoverflow
Solution 7 - AndroidKeitelDOGView Answer on Stackoverflow
Solution 8 - Androidcitizen connView Answer on Stackoverflow