Reputation: 21237
I have an ExpandableListView. Each child has a CheckBox and a TextView. When a user taps a child row, the CheckBox is supposed to change its state (checked vs unchecked). This works correctly if the user taps directly on the check box. However, if the user taps on the text view (same row, immediately to the right of the check box), I get a null pointer error as soon as I try to refer to the check box. Can anyone see what is wrong?
EDIT: After reading the suggestion below, I did some investigation and realized that I can implement my clickListener inside my adapter under getChildView(). This solves my issue with the null pointer as I can easily get a reference to the child view.
However, it creates another issue that I see no elegant solution to. Each time a child is clicked, I need to make changes to the listview itself. The data for this list resides in an ArrayList whose scope is within the Activity (not in the Adapter). If my clickListener is in the Adapter, how can I call back to the Activity to make changes to the ArrayList?
This strikes me as a catch-22. If I want to be able to manipulate my data, I can't get a reference to the child view. But if I want a reference to my child view, my data is out of scope, so I can't manipulate it. How do people resolve this? I must be missing something.
I'll throw in the relevant adapter code where you can see the beginning of my attempt to add a child onClickListener.
Thanks again!
public class Settings extends Activity {
//this is the list I need to access from the adapter if my click listener is there
private ArrayList<Categories> categoriesList = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smart_settings);
db = new EventsDB(this);
expListGroups = new ArrayList<ExpandListGroup>();
setGroups();
/* Set up the data adapter */
expAdapter = new ExpandListAdapter(Settings.this, expListGroups);
populateExpandableGroups();
}
public void setGroups() {
/* Create lists of the individual line items */
categoriesList = db.getCategoriesForClient(client);
locationsList = db.getLocationsForClient(client);
/* Add an item to the locations list to allow the user to add a new location */
locationsList.add(new ClientSmartFinderLocations(client, "Add Location", null, false));
ExpandListGroup categoryGroup = new ExpandListCategory("Choose Categories", categoriesList);
ExpandListGroup locationGroup = new ExpandListLocation("Choose Locations", locationsList);
expListGroups.add(categoryGroup);
expListGroups.add(locationGroup);
}
private void populateExpandableGroups() {
expandableList = (ExpandableListView) findViewById(R.id.expandable_list);
expandableList.setAdapter(expAdapter);
//I've removed this section and moved it to the adapter, per my edit above
// expandableList.setOnChildClickListener(new OnChildClickListener() {
//
// @Override
// public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
//
// /* Update the finder setting for this client */
// String category = categoriesList.get(childPosition).getCategory();
// Boolean isSelected = !categoriesList.get(childPosition).getIsSelected();
// db.setClientCategory(client, category, isSelected);
//
// /* Update the check box to provide feedback to the user */
// View view = parent.getChildAt(childPosition - parent.getFirstVisiblePosition() + 1);
// CheckBox checkBox = (CheckBox) view.findViewById(R.id.check_box);
//
// //error occurs here
// checkBox.setChecked(!checkBox.isChecked());
// return true;
// }
// });
expAdapter.notifyDataSetChanged();
}
public class ExpandListAdapter extends BaseExpandableListAdapter {
//other methods
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View view, final ViewGroup parent) {
view = getCategoryChildView(groupPosition, childPosition, view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
TextView tv = (TextView) v.findViewById(R.id.expand_list_item);
CheckBox checkBox = (CheckBox) v.findViewById(R.id.check_box);
String category = tv.getText().toString();
Boolean isSelected = !checkBox.isSelected();
db = new EventsDB(context);
db.setClientCategory(client, category, isSelected);
checkBox.setChecked(!checkBox.isChecked());
//here I need to do some things that require me to manipulate the categoriesList from the Activity class - but it is out of scope
}
});
return view;
}
}
<CheckBox
android:id="@+id/check_box"
android:layout_marginLeft="30dp"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/expand_list_item"
android:paddingLeft="10dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/smart_finder_settings_font_size"
android:textColor="#FFFFFF" />
Upvotes: 1
Views: 1732
Reputation: 132
user55410 is on the right track. In your Settings() Activity, make categoryList static, then when you change it with the getter/setter methods from the new instance you create in your it will modify all instances of Settings.categoryList.
Upvotes: 0
Reputation: 61
Im not a pro, so take that in mind. but what about storing the data you need the clicklistener to manipulate in a gobal application class? Maybe not the most elegant, but might work.
Upvotes: 0
Reputation: 21237
I think that I have a solution. If I create an instance of the Settings activity class within the Adapter, I can add the usual getter/setter methods to the Settings class. I can then call these methods from the adapter to get at that list, modify it, and then push it back to the Settings class, and all should be well.
It's a basic OOP approach, but the mental hangup for me was the idea of instantiating an activity. I don't know if this is common or not, but it seems weird to me. Anyone have a more elegant solution?
Within the Settings class:
public ArrayList<Categories> getCategoriesList() {
return categoriesList;
}
public void setCategoriesList(ArrayList<Categories> list) {
categoriesList = list;
}
Within the adapter:
Settings settings = new Settings();
ArrayList<Categories> tempCategoriesList = new ArrayList<Categories>();
tempCategoriesList = settings.getCategoriesList();
//make changes to the list
settings.setCategoriesList(tempCategoriesList);
Upvotes: 1
Reputation: 73385
You should set the checkbox checked state inside your ExpandListAdapter
instead of accessing the view inside the ExpandableListView
directly.
Upvotes: 0