Sometimes listView.getChildAt(int index) returns NULL (Android)
Asked Answered
P

4

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.

Phthalocyanine answered 25/4, 2012 at 15:31 Comment(2)
the adapter has null values?Rhinitis
possible duplicate of ListView getChildAt returning null for visible childrenGuinna
J
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.

Justify answered 25/4, 2012 at 15:37 Comment(7)
The null view is a visible view.Phthalocyanine
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...Phthalocyanine
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.Justify
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...Phthalocyanine
Thanks.... I did use (invalidaEste-lv_data.getFirstVisiblePosition()) as index and now works OK...!!Phthalocyanine
Can you guys tell me what is invalidaEste? I'm also facing same issueOsyth
Hi @Osyth try with position or the param name which is an int and indicates the raw positionHenn
D
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
}
Dingus answered 25/4, 2012 at 18:36 Comment(0)
S
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.

Steinman answered 25/4, 2012 at 15:38 Comment(3)
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.Phthalocyanine
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 ?Steinman
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.Phthalocyanine
K
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);
}
Kendall answered 15/9, 2015 at 21:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.