5

I have a listView with a custom adapter. When something happens (a click in a child) I do some calculation things and modify the child View. IF some condition has been fulfilled then other child unrelated to the clicked child should be modified.

This sometimes works, but sometimes fails and the DDMS says that the view is null...

Let me show you the code:

        if(invalidaEste != -1)
        {
            try
            {
                View v = lv_data.getChildAt(invalidaEste);
                if( v== null)
                {
                    Log.e("MY_LOG", "SIZE " + lv_data.getCount());
                    Log.e("MY_LOG", "IS_NULL " + String.valueOf(invalidaEste)); 
                }

                if(invalidaEste >= lv_data.getFirstVisiblePosition() &&
                   invalidaEste <= lv_data.getLastVisiblePosition())
                {
                    RelacionFacturaPago rpf = (RelacionFacturaPago)lv_data.getAdapter().getItem(invalidaEste);
                    TextView tv = (TextView)v.findViewById(R.id.tv_pendiente);
                    tv.setText(Formato.double2Screen(rpf.getPorPagar()));
                }
            }
            catch (Exception e)
            {
                Log.e("MY_LOG", "FAIL");
                Log.e("MY_LOG", String.valueOf(invalidaEste));
            }

        }

invalidaEste is the view that I want to modify. When v is null I log the index to check if it is OK. Always is smaller or equal than the listView.getCount()

Why is this happening?

More data: The code is inside of the onAnimationStart(Animation animation) of an AnimationListener listener.

Desenfoque
  • 804
  • 2
  • 11
  • 30
  • the adapter has `null` values? – zapl Apr 25 '12 at 15:37
  • possible duplicate of [ListView getChildAt returning null for visible children](http://stackoverflow.com/questions/6766625/listview-getchildat-returning-null-for-visible-children) – rds Oct 06 '13 at 17:10

4 Answers4

9

Because of view recycling, listView.getChildAt() will only return a view for the positions it is displaying, and maybe one more. Maybe if you share more of your code we can help you figure out how to best tackle the problem.

dmon
  • 30,048
  • 8
  • 87
  • 96
  • The null view is a visible view. – Desenfoque Apr 25 '12 at 16:25
  • 1
    mmm... I think that I see your point... The problem is triggered when the view with index 0 is not visible... I'm misunderstanding the getChildAt() method... – Desenfoque Apr 25 '12 at 16:41
  • Is this actually broken? I see that you're already this check: `if(invalidaEste >= lv_data.getFirstVisiblePosition() && invalidaEste <= lv_data.getLastVisiblePosition())`, and any other view should be updated by the adapter when you scroll it into view. – dmon Apr 25 '12 at 17:01
  • Yeah... I thought that getChildAt() returns the view related to the adapter index. That is wrong. getChildAt() and getChildCount() work with the visible view and not with all the views... – Desenfoque Apr 25 '12 at 17:07
  • 1
    Thanks.... I did use (invalidaEste-lv_data.getFirstVisiblePosition()) as index and now works OK...!! – Desenfoque Apr 25 '12 at 17:25
  • 1
    Can you guys tell me what is invalidaEste? I'm also facing same issue – tejas Aug 12 '13 at 07:14
  • Hi @tejas try with position or the param name which is an int and indicates the raw position – ziniestro Dec 29 '14 at 05:30
4

Dmon and Azertiti are both correct... once your list is scrolled you find yourself in trouble. If the view isn't visible, then it doesn't exist (i.e. has been recycled by Android). You'll re-build the view once it's scrolled in.

Doing something like this should work:

View view;

int nFirstPos = lv_data.getFirstVisiblePosition();
int nWantedPos = invalidaEste - nFirstPos;

if ((nWantedPos >= 0) && (nWantedPos <= lv_data.getChildCount())
{
 view = lv_data.getChildAt(nWantedPos);
 if (view == null)
  return;
 // else we have the view we want
}
CSmith
  • 13,318
  • 3
  • 39
  • 42
1

If that child is not visible on screen it means there is no View for it. I believe this is your not working case.

A good practice is to change the data behind your list adapter and call notifyDataSetChanged() on the adapter. This will inform the list the adapter has changed and paint again the views.

If you really want to manually update the view I guess the only solution is to retain the new values somewhere and wait until the View becomes visible. At that point you have a valid reference and can do the updates.

azertiti
  • 3,150
  • 17
  • 19
  • Thanks for your answer.... As you can see in the code, I use the view (v) only when the index belong to a visible item. I'm not using notifyDataSetChanged() because it cancels the animations in the listView. – Desenfoque Apr 25 '12 at 16:20
  • I see you get the child when invalidaEste != -1. This doesn't mean it's an index with visible view but an index that might be a good one. Is there any code on top of that which you wanted to paste and is not visible ? – azertiti Apr 25 '12 at 16:31
  • invalidaEste != -1 only tell me that is a valid index. I use if(invalidaEste >= lv_data.getFirstVisiblePosition() && invalidaEste <= lv_data.getLastVisiblePosition()) to check if it is visible. – Desenfoque Apr 25 '12 at 16:35
1

I test it with one line of code when I need to go within a valid position of a Listview. here is your variables used as an example. where lv_data is the ListView and tv is your TextView.

if ( lv_data.getChildAt(lv_data.getPositionForView(tv)) != null) {
    int position = lv_data.getPositionForView(tv);
}
Ken Montagna
  • 171
  • 1
  • 1
  • 5