Search code examples
androidandroid-listviewandroid-listfragment

Nullpointer exception when trying to access to ListView 's children


I've created a Listfragment, which displays a List of views . Here is the ListFragment :

public class AroundYou extends ListFragment {
private int[] images = new int[] {
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin,
        R.drawable.pinguin
};

private String[] names = new String[] {
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu",
        "Pingu"         
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {

    // Each row in the list stores country name, currency and flag
    List<HashMap<String,String>> aList = new ArrayList<HashMap<String,String>>();

    for(int i=0;i<10;i++){
        HashMap<String, String> hm = new HashMap<String,String>();
        hm.put("name", names[i]);
        hm.put("image",Integer.toString(images[i]));
        aList.add(hm);
    }

    // Keys used in Hashmap
    String[] from = { "name","image"};

    // Ids of views in listview_layout
    int[] to = { R.id.name,R.id.image};

    // Instantiating an adapter to store each items
    // R.layout.listview_layout defines the layout of each item
    SimpleAdapter adapter = new SimpleAdapter(getActivity().getBaseContext(), aList, R.layout.around_you_line_of_list, from, to);

    setListAdapter(adapter);

    return super.onCreateView(inflater, container, savedInstanceState);
}
}

Each row of the ListView displays an image and a text . It's the same image/text for the moment but I will put differents image/text later . The xml file of a row is around_you_line_of_list.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

<ImageView
    android:id="@+id/image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    android:contentDescription="@string/hello_world"
    android:paddingBottom="10dp"
    android:paddingRight="10dp"
    android:paddingTop="10dp" />

<TextView
    android:id="@+id/name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1" />

</LinearLayout>

This works fine . But, I wanted then in the ListFragment to modify the images of each row of the ListView . So I did it in the function onActivityCreated . And I got a NullPointerException when I am calling getChildAt( int ) . Why ? I mean, I'm calling the ListView AFTER onCreateView, and then I just want to : take a child view of this ListView, taking the imageview of this child, modifiying the imageview .

How can I access to the child views ?...

     public void onActivityCreated(Bundle savedInstanceState)
    {
        super.onActivityCreated(savedInstanceState);
        ListView listView = getListView();
        for(int i = 0;i < 10;i++){
//I'm getting a null pointer exception at this line .
            View view = listView.getChildAt(i);
            ImageView imageview = (ImageView) view.findViewById(R.id.image);

            BitmapDrawable drawable = (BitmapDrawable)imageview.getDrawable();

//here I 'm getting a bitmap from the drawable, and then applying a function that modify
//the bitmap .
            Bitmap foreign_image = drawable.getBitmap();
            Canvas canvas = null;
            Bitmap bitmap = setBitmapClippedCircle(foreign_image,200,200,canvas);
//here I'm putting the result in the imageview .
            imageview.setImageBitmap(bitmap);

        }

    }

If you need some more code (like the activity wich is displaying the fragment, or other stuff), or informations, say it to me :)


Solution

  • If you're seeking to modify the data that is presented, you must modify the data that backs the list, not the list itself.

    There are three components: your data, your adapter which transforms your data into views that the list view can use, and the list view itself which requests views from the adapter based on list position.

    If you need to change what's presented, change the data. Update the adapter with the new, changed, dataset, and call adapter.notifyDatasetChanged(). This will trigger your list view to request data from the adapter again, as its stale.

    From where you are in your code now, you can do one of two things.

    • Instead of using a SimpleAdapter directly, create a class that extends BaseAdapter and use that. Add a method to change your data (setImages(List images) for example) which also includes the super call to notifyDatasetChanged() as the last statement.
    • create a new instance of SimpleAdapter with your new data and setListAdapter() again with the new adapter instance.

    The first is preferred, but the tl:Dr; is "don't modify your views directly; it's an AdapterView because it's backed by a data source already."