java.lang.NullPointerException when try listview.getChildAt()
Asked Answered
E

3

1

There is ListView with correct values:

public class FragmentTab1 extends SherlockFragment {

ListView list;
LazyAdapter adapter;

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    list = (ListView) getActivity().findViewById(android.R.id.list); //also I tried view.findViewById(android.R.id.list)
    ............

    adapter = new LazyAdapter(getActivity(), mSource);
    list.setAdapter(adapter);
} 
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
    View rootView = inflater.inflate(R.layout.fragmenttab1, container, false);
    return rootView;

}

when I try:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId(); //correct
    int itemCount = list.getCount();  // 10 ps as show Logcat
    if (R.id.save == id) {
        CheckBox cb;
        for(int i = itemCount - 1; i >= 0; i--) {
            cb = (CheckBox)list.getChildAt(i).findViewById(R.id.checkBox1);  //Error here
        }
    }
    return true;
}

xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<CheckBox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Save to database"
    android:id="@+id/checkBox1" />  // same id

and adapter is next:

public class LazyAdapter extends BaseAdapter {

private Activity activity;
ArrayList<Bitmap> bitmapArray = new ArrayList<Bitmap>();
private ArrayList<Data> mObjects;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;

public LazyAdapter(Activity a, ArrayList<Data> mObjects1) {
    activity = a;
    mObjects = mObjects1;
    inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader=new ImageLoader(activity.getApplicationContext());
}

public int getCount() {
    return mObjects.size();
}

public Object getItem(int position) {
    return position;
}

public long getItemId(int position) {
    return position;
}

public View getView(int position, View convertView, ViewGroup parent) {
    Data item = mObjects.get(position);
    View vi=convertView;
    if(convertView==null)
        vi = inflater.inflate(R.layout.item_internet, null);
    TextView text=(TextView)vi.findViewById(R.id.title1);
    ImageView image=(ImageView)vi.findViewById(android.R.id.icon);
    text.setText(item.getmTitle());
    bitmapArray.add(imageLoader.getBitmap());
    imageLoader.DisplayImage(item.getmImageUrl(), image);
    return vi;
}

I receive correct ListView, but receive error when I try click save button from action bar. Probably, I should init CheckBox in adapter? Somebody can help me?

Enroot answered 22/10, 2014 at 12:33 Comment(1)
try to use ViewHolder concept when you use Custom adapter : developer.android.com/training/improving-layouts/…Calcic
M
0

list.getChildAt(i) will be null if the child item is not visible. So check for null before use.

So you cannot retrieve all checked items in this way.

Please post complete .xml and the definition of <Data>.

Merge answered 22/10, 2014 at 13:48 Comment(0)
S
0

I'd think you'd get an indexoutofbounds but since it's null, this might be why: ListView getChildAt returning null for visible children

Also, put a log statement in your for loop to display the value of all variables concerned, so: i and itemCount etc.

And set a breakpoint just before the loop and run debug mode to step over to check the values as it loops through and you'll see what i value caused the nullpointer in the debugger or if you miss it, it will be in logcat

Shaitan answered 24/10, 2014 at 21:53 Comment(0)
I
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, Boolean> cbValues 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;
  • Add InputListener for Input Views, (addTextChangedListener for EditText, setOnCheckedChangeListener for Checkbox, etc.) And on input, update the HashMap key (item.getId() or position) and value (editable.toString() or true or false). Ex. on @Override public void onCheckedChanged, put boolean result cbValues.put(item.getId(), b);
  • 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(cbValues.containsKey(item.getId())){cbItem.setChecked(cbValue.get(cbItem.getId()))};. 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 bool getCheckboxValueForItemId(int itemId) { return cbValues.get(itemId); }` . It will be easy then to select Item from Adapter

Here is the Codes at the end :

public class LazyAdapter extends BaseAdapter {


private Activity activity;
ArrayList<Bitmap> bitmapArray = new ArrayList<Bitmap>();
private ArrayList<Data> mObjects;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;

HashMap<Integer, Boolean> cbValues;

public LazyAdapter(Activity a, ArrayList<Data> mObjects1) {
    activity = a;
    mObjects = mObjects1;
    cbValues = new HashMap<>();
    inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader=new ImageLoader(activity.getApplicationContext());
}

public int getCount() {
    return mObjects.size();
}

public Object getItem(int position) {
    return position;
}

public long getItemId(int position) {
    return position;
}

public View getView(int position, View convertView, ViewGroup parent) {
    Data item = mObjects.get(position);
    View vi=convertView;
    // Remove convertView condition
    //if(convertView==null)

    vi = inflater.inflate(R.layout.item_internet, null);
    TextView text=(TextView)vi.findViewById(R.id.title1);
    ImageView image=(ImageView)vi.findViewById(android.R.id.icon);
    Checkbox cbItem = (Checkbox) vi.findViewById(android.R.id.checkbox1);
    text.setText(item.getmTitle());
    bitmapArray.add(imageLoader.getBitmap());
    imageLoader.DisplayImage(item.getmImageUrl(), image);

    if(cbValues.containsKey(item.getId())) {
        cbItem.setChecked(cbValue.get(cbItem.getId()))};
    }

    cbItem.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
           cbValues.put(item.getId(), b);
       }
    });

    return vi;
}

// itemId : unique identifier for an Item, not the position of Item in Adapter
public bool getCheckboxValueForItemId(int itemId) {
    return cbValues.get(itemId);
}

}
Interpret answered 27/5, 2018 at 13:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.