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)
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
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);
}
};
}
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
来源:https://stackoverflow.com/questions/21345972/do-i-need-multiple-viewholders-for-expandablelistview