I have a SherlockListFragment in which I have a custom list item (checkbox, icon and some text) - works fine. The list is populated from a custom CursorAdapter and it is in here that I trap the Checkbox events (so I can preserve the data through orientation changes as well - also works). I want to be able to raise an event in the ListFragment when the user checks one or more checkboxes to display an actionbar item.
I've implemented an interface in the fragment to accept the event but just cannot work out the syntax for raising the fragment event in the cursoradapter's checkbox onClick listener - the event triggers ok but a can't call the listener, can't suss the object to use.
Fragment:
public class ContactListFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks<Cursor>
{
public interface ListItemCheckedListener
{
public void OnListItemChecked(int count);
}
private SimpleCursorAdapter mAdapter;
public ListItemCheckedListener checkListener;
public void setListItemListener(ListItemCheckedListener listener)
{
checkListener = listener;
}
<snip>
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
<snip>
setListItemListener(new ListItemCheckedListener()
{
@Override
public void OnListItemChecked(int count)
{
// Turn on/off Action Bar Item
//if (count > 0)...
}
});
CursorAdapter:
public class ContactCursorAdapter extends SimpleCursorAdapter
{
<snip>
@Override
public void bindView(View view, Context context, Cursor cursor)
{
<snip>
CheckBox cBox = (CheckBox)view.findViewById(R.id.checkBox);
final int pos = cursor.getPosition();
cBox.setOnClickListener(new OnClickListener()
{
public void onClick(View view)
{
CheckBox cb = (CheckBox)view.findViewById(R.id.checkBox);
if (cb.isChecked())
{
itemChecked.set(pos, true);
countChecked++;
<something>.checkListener(countChecked); // <----- Umm?
}
else if (!cb.isChecked())
{
itemChecked.set(pos, false);
countChecked--;
<something>.checkListener(countChecked); // <----- ditto
}
}
});
cBox.setChecked(itemChecked.get(pos));
}
I've tried all manner of objects for "something" but can't seem to quite get the right one.
Help... please...!
Well, after quite a few dead ends I finally came up with a solution. Don't know if it is a good solution or if it is the "correct" way to do it but it works...
I simply added a public ListItemCheckedListener variable to the CursorAdapter class, assigned my ListFragment ListItemCheckedListener instance to it straight after setting up the adapter and then could call the local instance of the listener within the CursorAdapter and the event would propagate out to the ListFragment.
ListFragment:
public class ContactListFragment extends SherlockListFragment implements
LoaderManager.LoaderCallbacks<Cursor>// , OnQueryTextListener
{
private ContactCursorAdapter _adapter; // <----- HERE
<snip>
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setListItemListener(new ContactListItemCheckedListener()
{
@Override
public void OnListItemChecked(int count)
{
if (count > 0)
{
if (_mode == null)
{
_loaderManager = getLoaderManager();
ContactsActivity activity = (ContactsActivity)getActivity();
_mode = activity.startActionMode(_actionModeCallback);
}
if (_contactInvite != null)
_contactInvite.setRecipCount(count);
}
else
{
if (_mode != null)
{
_mode.finish();
_mode = null;
}
}
}
});
_fragment = this;
setListProcessedListener(new ContactListProcessedListener()
{
public void OnListProcessed(int contactMethod)
{
for (int i = 0; i < _adapter.itemChecked.size(); i++)
_adapter.itemChecked.set(i, false);
_fragment.setListAdapter(_adapter);
_loaderManager.initLoader(0, null, _fragment);
}
});
setEmptyText(this.getString(R.string.contacts_none));
setHasOptionsMenu(true);
_adapter = new ContactCursorAdapter(getActivity(),
R.layout.contact_list_item,
null,
new String[] { Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS },
CONTACT_SUMMARY_FIELDS,
0);
_adapter.checkListener = checkListener;
boolean[] iChecked = null;
if (savedInstanceState != null)
{
if (savedInstanceState.containsKey("itemChecked"))
{
iChecked = savedInstanceState.getBooleanArray("itemChecked");
for (int i = 0; i < iChecked.length; i++)
_adapter.itemChecked.add(i, iChecked[i]);
_adapter.countChecked = savedInstanceState.getInt("countChecked");
checkListener.OnListItemChecked(_adapter.countChecked);
}
}
try
{
setListAdapter(_adapter);
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
((ContactsActivity)getActivity()).setSearchTextListener(new ContactsActivity.SearchTextListener()
{
@Override
public void OnSearchTextChange(String text)
{
onQueryTextChange(text);
}
});
}
catch (Exception e)
{
Singletons.Logger().error(TAG + ".onActivityCreated() failed",
e.getMessage());
}
}
CursorAdapter:
public class ContactCursorAdapter extends SimpleCursorAdapter
{
<snip>
public ListItemCheckedListener checkListener; // <----- HERE
<snip>
@Override
public void bindView(View view, Context context, Cursor cursor)
{
<snip>
cBox.setOnClickListener(new OnClickListener()
{
public void onClick(View view)
{
CheckBox cb = (CheckBox)view.findViewById(R.id.checkBox);
if (cb.isChecked())
{
itemChecked.set(pos, true);
countChecked++;
checkListener.OnListItemChecked(countChecked); // <----- HERE
}
else if (!cb.isChecked())
{
itemChecked.set(pos, false);
countChecked--;
checkListener.OnListItemChecked(countChecked); // <----- HERE
}
}
});
cBox.setChecked(itemChecked.get(pos));
}
Quite simple and fairly elegant, if I do say so myself. Well... at least it is in comparison to some of my earlier failed attempts(!). I still get the feeling that it isn't the best way to do it, somehow just feels wrong, but hey... it works.
If anyone has any better ways of doing this, I'd love to hear them. In the meanwhile, I hope this helps someone else.