3

Am I using this viewholder wrong? I'm getting an NPE on line 165. Is there an obvious reason why that I'm missing? Do I need a group viewholder and a child viewholder if I'm using expandablelistview? I marked line 165 to try to make it easier on the eyes.

Thanks a lot

my expandablelistview that's getting the NPE:

public class MyExpandableListAdapter extends BaseExpandableListAdapter {

    private Context mContext;
    private ArrayList<ContactNameItems> mListDataHeader;
    private ArrayList<Boolean> phoneNumberCheckStates;
    private ArrayList<String> selectedNumbers;

    private HashMap<String, List<ContactPhoneItems>> mListDataChild;

    private ViewHolder mViewHolder;

    public MyExpandableListAdapter(Context context,
            ArrayList<ContactNameItems> listDataHeader,
            HashMap<String, List<ContactPhoneItems>> listDataChild,
            ArrayList<String> listOfNumbers) {

        mContext = context;
        mListDataHeader = listDataHeader;
        mListDataChild = listDataChild;
        selectedNumbers = listOfNumbers;
    }

    @Override
    public int getGroupCount() {
        return mListDataHeader.size();
    }

    @Override
    public ContactNameItems getGroup(int groupPosition) {
        return mListDataHeader.get(groupPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {

        String contactName = getGroup(groupPosition).getName();
        Bitmap contactImage = getGroup(groupPosition).getImage();

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.contact_name_item, null);

            mViewHolder = new ViewHolder();

            mViewHolder.mContactName = (TextView) convertView
                    .findViewById(R.id.lblListHeader);

            mViewHolder.mContactImage = (ImageView) convertView
                    .findViewById(R.id.ivContactPhoto);

            convertView.setTag(mViewHolder);
        } else {

            mViewHolder = (ViewHolder) convertView.getTag();
        }

        if (contactImage != null) {
            mViewHolder.mContactImage.setImageBitmap(contactImage);

        } else {
            mViewHolder.mContactImage.setImageResource(R.drawable.default_contact);
        }

        mViewHolder.mContactName.setText(contactName);

        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
                .size();
    }

    @Override
    public ContactPhoneItems getChild(int groupPosition, int childPosition) {
        return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
                .get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(int groupPosition, final int childPosition,
            boolean isLastChild, View convertView, ViewGroup parent) {

        String numberText = getChild(groupPosition, childPosition).getNumber();
        String typeText = getChild(groupPosition, childPosition).getPhoneType();

        final int mGroupPosition = groupPosition;

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) this.mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.contact_detail_item, null);

            mViewHolder = new ViewHolder();

            mViewHolder.mPhoneNumber = (TextView) convertView
                    .findViewById(R.id.tv_phone_number);

            mViewHolder.mPhoneType = (TextView) convertView
                    .findViewById(R.id.tv_phone_type);

            mViewHolder.mCheckBox = (CheckBox) convertView
                    .findViewById(R.id.checkBox);

            convertView.setTag(mViewHolder);

        } else {

            mViewHolder = (ViewHolder) convertView.getTag();
        }

        mViewHolder.mPhoneNumber.setText(numberText);
        mViewHolder.mPhoneType.setText(typeText);

        phoneNumberCheckStates = new ArrayList<Boolean>();

        for (int i = 0; i < mListDataChild.size(); i++) {

            phoneNumberCheckStates.add(false);
        }

        if (phoneNumberCheckStates.get(childPosition)) {
            mViewHolder.mCheckBox.setChecked(true);
        } else {
            mViewHolder.mCheckBox.setChecked(false);
        }

        mViewHolder.mCheckBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

/*this is line 165*/    if (mViewHolder.mCheckBox.isChecked()) {  /*this is line 165*/
                    phoneNumberCheckStates.set(childPosition, true);

                    selectedNumbers.add(mListDataChild
                            .get(mListDataHeader.get(mGroupPosition).getName())
                            .get(childPosition).getNumber());

                } else {
                    phoneNumberCheckStates.set(childPosition, false);

                    selectedNumbers.remove(mListDataChild
                            .get(mListDataHeader.get(mGroupPosition).getName())
                            .get(childPosition).getNumber());
                }
            }
        });

        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    public ArrayList<Boolean> getCheckedNumbers() {

        return phoneNumberCheckStates;
    }

    public ArrayList<String> getSelectedNumbers() {

        return selectedNumbers;
    }

    private class ViewHolder {

        TextView mContactName;
        TextView mPhoneNumber;
        TextView mPhoneType;
        ImageView mContactImage;
        CheckBox mCheckBox;
    }
}

if it helps, here's the Log:

01-25 04:34:31.695: E/AndroidRuntime(7074): FATAL EXCEPTION: main
01-25 04:34:31.695: E/AndroidRuntime(7074): java.lang.NullPointerException
01-25 04:34:31.695: E/AndroidRuntime(7074):     at com.psesto.journeysend.contactpicker.MyExpandableListAdapter$1.onClick(MyExpandableListAdapter.java:165)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.view.View.performClick(View.java:4204)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.widget.CompoundButton.performClick(CompoundButton.java:100)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.view.View$PerformClick.run(View.java:17355)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.os.Handler.handleCallback(Handler.java:725)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.os.Handler.dispatchMessage(Handler.java:92)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.os.Looper.loop(Looper.java:137)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at android.app.ActivityThread.main(ActivityThread.java:5041)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at java.lang.reflect.Method.invoke(Method.java:511)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
01-25 04:34:31.695: E/AndroidRuntime(7074):     at dalvik.system.NativeStart.main(Native Method)
Psest328
  • 6,575
  • 11
  • 55
  • 90

3 Answers3

6

I accepted Towlie288's answer because it pointed me in the right direction. Here's the code change that made everything work:

public class MyExpandableListAdapter extends BaseExpandableListAdapter {

    private Context mContext;
    private ArrayList<ContactNameItems> mListDataHeader;
    private ArrayList<String> selectedNumbers;

    private HashMap<String, List<ContactPhoneItems>> mListDataChild;

    private ChildViewHolder childViewHolder;
    private GroupViewHolder groupViewHolder;

    public MyExpandableListAdapter(Context context,
            ArrayList<ContactNameItems> listDataHeader,
            HashMap<String, List<ContactPhoneItems>> listDataChild,
            ArrayList<String> listOfNumbers) {

        mContext = context;
        mListDataHeader = listDataHeader;
        mListDataChild = listDataChild;
        selectedNumbers = listOfNumbers;

    }

    @Override
    public int getGroupCount() {
        return mListDataHeader.size();
    }

    @Override
    public ContactNameItems getGroup(int groupPosition) {
        return mListDataHeader.get(groupPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {

        String contactName = getGroup(groupPosition).getName();
        Bitmap contactImage = getGroup(groupPosition).getImage();

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.contact_name_item, null);

            groupViewHolder = new GroupViewHolder();

            groupViewHolder.mContactName = (TextView) convertView
                    .findViewById(R.id.lblListHeader);

            groupViewHolder.mContactImage = (ImageView) convertView
                    .findViewById(R.id.ivContactPhoto);

            convertView.setTag(groupViewHolder);
        } else {

            groupViewHolder = (GroupViewHolder) convertView.getTag();
        }

        if (contactImage != null) {
            groupViewHolder.mContactImage.setImageBitmap(contactImage);

        } else {
            groupViewHolder.mContactImage
                    .setImageResource(R.drawable.default_contact);
        }

        groupViewHolder.mContactName.setText(contactName);

        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
                .size();
    }

    @Override
    public ContactPhoneItems getChild(int groupPosition, int childPosition) {
        return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
                .get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(int groupPosition, final int childPosition,
            boolean isLastChild, View convertView, ViewGroup parent) {

        String numberText = getChild(groupPosition, childPosition).getNumber();
        String typeText = getChild(groupPosition, childPosition).getPhoneType();

        final int mGroupPosition = groupPosition;

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) this.mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.contact_detail_item, null);

            childViewHolder = new ChildViewHolder();

            childViewHolder.mPhoneNumber = (TextView) convertView
                    .findViewById(R.id.tv_phone_number);

            childViewHolder.mPhoneType = (TextView) convertView
                    .findViewById(R.id.tv_phone_type);

            childViewHolder.mCheckBox = (CheckBox) convertView
                    .findViewById(R.id.checkBox);

            childViewHolder.mCheckBox.setOnCheckedChangeListener(checkListener);

            convertView.setTag(childViewHolder);

        } else {

            childViewHolder = (ChildViewHolder) convertView.getTag();
        }

        childViewHolder.mPhoneNumber.setText(numberText);
        childViewHolder.mPhoneType.setText(typeText);

        ContactPhoneItems cpi = getChild(mGroupPosition, childPosition);

        childViewHolder.mCheckBox.setTag(cpi);
        childViewHolder.mCheckBox.setChecked(cpi.getSelected());

        // for managing the state of the boolean
        // array according to the state of the
        // CheckBox

        childViewHolder.mCheckBox
                .setOnClickListener(new View.OnClickListener() {

                    String contactNumber = mListDataChild
                            .get(mListDataHeader.get(mGroupPosition).getName())
                            .get(childPosition).getNumber();

                    public void onClick(View v) {

                        boolean isChecked = ((CheckBox) v).isChecked();

                        if (isChecked) {

                            selectedNumbers.add(contactNumber);

                        } else {

                            selectedNumbers.remove(contactNumber);
                        }

                        getChild(mGroupPosition, childPosition).setSelected(isChecked);
                        notifyDataSetChanged();
                    }
                });

        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    public ArrayList<String> getSelectedNumbers() {

        return selectedNumbers;
    }

    public final class GroupViewHolder {

        TextView mContactName;
        ImageView mContactImage;
    }

    public final class ChildViewHolder {

        TextView mPhoneNumber;
        TextView mPhoneType;
        CheckBox mCheckBox;
    }

    OnCheckedChangeListener checkListener = new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView,
                boolean isChecked) {

            ContactPhoneItems c = (ContactPhoneItems) buttonView.getTag();
            c.setSelected(isChecked);
        }
    };
}
Psest328
  • 6,575
  • 11
  • 55
  • 90
  • 1
    Sounds good with me. Just a side note: I noticed you used inflate(R.layout.contact_detail_item, null); instead of inflate(R.layout.contact_detail_item, parent, false); Later one seems to be a better option if I'm correctly informed. – Tobias Reich Jan 14 '16 at 13:59
  • By the way, your final int mGroupPosition is never used? Or do I miss something? – Tobias Reich Jan 14 '16 at 14:03
  • It is used, just do a search for it. But with recyclerview, I consider this to be a little outdated. – Psest328 Jan 20 '16 at 17:25
2

You have one ViewHolder reference in your Adapter for all your Views. This makes no sense because every View in the List has its own instance of the ViewHolder which you get by View.getTag().

You could set an int[] with the positions you need as a Tag of CheckBox

    int[] positions = new int[2];
    positions[0] = childPosition;
    positions[1] = groupPosition;
    mViewHolder.mCheckBox.setTag(positions);

and in onClick()

    CheckBox box = (CheckBox) v;
    int[] posTag = (int[]) v.getTag();

Then you have the CheckBox for the state and the positions for the rest

Towlie288
  • 418
  • 1
  • 5
  • 9
-1

This happens if a Child's view is reusing a Group's view, also the ViewHolder of it. Obviously, it cannot find mCheckBox in line 165, because it has not been set.
Simply adding a flag in ViewHolder, to check whether it is a Child's ViewHolder could solve your problem. No need to have two kinds of ViewHolder here.
Hope is helps

Zhenghong Wang
  • 2,117
  • 17
  • 19
  • I'm sorry, but I don't know what you mean by adding a flag. Sorry, self-taught here. Learning as I go – Psest328 Jan 25 '14 at 04:30