I have an app with a fairly standard fragment layout. An expandable listview fragment on the left and a panel on the right that is used for different things depending on what the user chooses to do with the list on the left (displaying data, adding new data, etc).
I'm using the LoaderManager (first time using loaders) with CommonWare's loaderex
library as I have no need or desire to create a Content Provider for my database just so I can use a standard CursorLoader. This setup works great for displaying my list.
The issue I am having is when I use the second fragment to add data to the database. I cannot figure out how to trigger a re-load of the list in the first fragment. For the life of me I cannot figure out how to grab the loader from the first fragment in the second so that it will be aware that the data needs to be pulled again, nor can I seem to figure how to manually trigger a re-load.
As this is my first attempt at using Loaders, if I'm doing something improperly I'd be happy to be (gently) re-directed down a better path.
Fragment 1
public class StudentListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
private TAOpenHelper mDbHelper = null;
private MyExpandableListAdapter mAdapter = null;
private ExpandableListView lv = null;
private Button addStudentButton;
public static long mRowId = 0;
public SQLiteCursorLoader studentLoader=null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.leftlistfragment_entry, container,
false);
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
addStudentButton = (Button) getActivity().findViewById(R.id.AddButton);
addStudentButton.setText(getResources().getString(
R.string.button_add_student));
addStudentButton.setOnClickListener(addStudentButtonHandler);
lv = (ExpandableListView) getListView();
mDbHelper = TAOpenHelper.getInstance(getActivity());
fillData();
getLoaderManager().getLoader(-1);
if (studentLoader != null && !studentLoader.isReset()) {
getLoaderManager().restartLoader(-1, null, this);
} else {
getLoaderManager().initLoader(-1, null, this);
}
}
private void fillData() {
mAdapter = new MyExpandableListAdapter(getActivity(), this,
R.layout.listlayout_exp_double_group,
R.layout.listlayout_exp_double_child,
new String[] { TeacherAidDB.STUDENT_FIRST,
TeacherAidDB.STUDENT_LAST }, new int[] {
R.id.ListItem1, R.id.ListItem2 }, new String[] {
TeacherAidDB.CLASS_NAME, TeacherAidDB.CLASS_LEVEL },
new int[] { R.id.ListItem1, R.id.ListItem2 });
lv.setAdapter(mAdapter);
}
public class MyExpandableListAdapter extends SimpleCursorTreeAdapter {
protected final SparseIntArray mGroupMap;
private StudentListFragment mFragment;
public MyExpandableListAdapter(Context context,
StudentListFragment clf, int groupLayout, int childLayout,
String[] groupFrom, int[] groupTo, String[] childrenFrom,
int[] childrenTo) {
super(context, null, groupLayout, groupFrom, groupTo, childLayout,
childrenFrom, childrenTo);
mFragment = clf;
mGroupMap = new SparseIntArray();
}
@Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
int groupPos = groupCursor.getPosition();
int groupId = groupCursor.getInt(groupCursor
.getColumnIndex(TeacherAidDB.CLASS_ROWID));
mGroupMap.put(groupId, groupPos);
Loader<Cursor> loader = getActivity().getLoaderManager().getLoader(
groupId);
if (loader != null && !loader.isReset()) {
getActivity().getLoaderManager().restartLoader(groupId, null,
mFragment);
} else {
getActivity().getLoaderManager().initLoader(groupId, null,
mFragment);
}
return null;
}
public SparseIntArray getGroupMap() {
return mGroupMap;
}
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id != -1) { // Child Cursor
studentLoader = new SQLiteCursorLoader(getActivity(), mDbHelper,
TeacherAidDB.STUDENT_LIST_CLASS_QUERY + id, null);
} else { // Group Cursor
studentLoader = new SQLiteCursorLoader(getActivity(), mDbHelper,
TeacherAidDB.STUDENT_LIST_QUERY, null);
}
return studentLoader;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
int id = loader.getId();
if (id != -1) { // Child cursor
if (!data.isClosed()) {
SparseIntArray groupMap = mAdapter.getGroupMap();
int groupPos = groupMap.get(id);
mAdapter.setChildrenCursor(groupPos, data);
}
} else { // Groups cursor
mAdapter.setGroupCursor(data);
}
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
mAdapter.changeCursor(null);
}
View.OnClickListener addStudentButtonHandler = new View.OnClickListener() {
public void onClick(View v) {
AddPerson personadd = AddPerson.newInstance(AddPerson.STUDENT, AddPerson.CREATE, mRowId);
getFragmentManager().beginTransaction()
.replace(R.id.rightpane, personadd).commit();
}
};
}
Fragment 2
public class AddPerson extends Fragment {
public static int STUDENT = 0;
public static int TEACHER = 1;
public static int CREATE = 0;
public static int EDIT = 1;
private int mRowId;
private TAOpenHelper mDbHelper;
private Cursor personedit;
private Button commit;
private Button cancel;
int who;
int what;
long rowId;
static AddPerson newInstance(int type, int action, long rowid) {
AddPerson f = new AddPerson();
Bundle args = new Bundle();
args.putInt("type", type);
args.putInt("action", action);
args.putLong("rowid", rowid);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
who = getArguments().getInt("type");
what = getArguments().getInt("action");
rowId = getArguments().getInt("rowid");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_person_add, container, false);
mDbHelper = TAOpenHelper.getInstance(getActivity());
if (what == EDIT) {
if (who == STUDENT) {
// Student Edit stuff here
} else {
// Teacher Edit stuff here
}
} else {
if (who == STUDENT) {
// Student Create stuff here
} else {
// Teacher Create stuff here
}
}
// Code to gather data from user goes here
commit = (Button) v.findViewById(R.id.commitbutton);
commit.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
first = firstTxt.getText().toString();
last = lastTxt.getText().toString();
street = streetTxt.getText().toString();
city = cityTxt.getText().toString();
zip = zipTxt.getText().toString();
phone = phoneTxt.getText().toString();
email = emailTxt.getText().toString();
if (what == CREATE) {
processAdd(who);
} else {
processUpdate(who);
}
}
});
cancel = (Button) v.findViewById(R.id.cancelbutton);
cancel.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Fragment check = getFragmentManager().findFragmentById(
R.id.rightpane);
getFragmentManager().beginTransaction().remove(check).commit();
}
});
return v;
}
private void processAdd(int who) {
ContentValues initialValues = new ContentValues();
if (who == STUDENT) {
initialValues.put(TeacherAidDB.STUDENT_FIRST, first);
initialValues.put(TeacherAidDB.STUDENT_LAST, last);
initialValues.put(TeacherAidDB.STUDENT_STREET, street);
initialValues.put(TeacherAidDB.STUDENT_CITY, city);
initialValues.put(TeacherAidDB.STUDENT_STATE, state);
initialValues.put(TeacherAidDB.STUDENT_ZIP, zip);
initialValues.put(TeacherAidDB.STUDENT_PHONE, phone);
initialValues.put(TeacherAidDB.STUDENT_EMAIL, email);
initialValues.put(TeacherAidDB.STUDENT_BDAY, birthday);
// How to get studentLoader from fragment 1?
//studentLoader.insert(TeacherAidDB.STUDENT_TABLE, null, initialValues);
}
}
}
With a regular
CursorLoader
, this would happen automagically via theContentObserver
framework, which eventually boils down to a bunch of static data members.With
SQLiteCursorLoader
,ContentObserver
is not available, with the closest simulacrum being to route your CRUD operations through theLoader
so it knows to reload theCursor
. And that is really only designed for use within a single activity.So, as Luksprog suggested, your best option is to delegate CRUD work to the containing activity.
If these fragments might be hosted by disparate activities (e.g., for small/normal vs. large/xlarge screen sizes), define a common interface for handling this work, and have the fragments delegate to the interface.