I have 2 views of essentially the same data:
- List of items in a android.support.v4.app.ListFragment
- Markers on a map in a com.google.android.gms.maps.SupportMapFragment
Both of the above are using loader pattern to obtain the same data (extending LoaderCallbacks, querying ContentProvider, and so on)
Both are hosted within a single activity inside a ViewPager.
What will be the best strategy to synchronize currently selected list item / marker for both of these fragments? (Think of "My Places" edit UI, or "Directions" of the Google Maps with their left-hand pane and a map in the center).
Scenarios i'm thinking of so far:
- Make every fragment manually notify it's counterpart about selection change via callback interface (this will probably involve underlying activity to coordinate inter-fragment communications, as it is suggested by Android docs).
- Somehow make both fragments use the same Cursor, or even ListAdapter (whatever it means for a map, because now it's populated directly from the cursor).
- (Something else?)
Maybe someone has already dealt with this exact case? (I'll sure find some solution, just wanted to avoid "reinventing the wheel". Sorry for a too conceptual question.)
EDIT (Solution)
I think Maciej has answered my exact question ("best strategy", and so on..), so the answers are both 1 and 2 ;-)
Going into more details, my implementation went like this:
At first I frightened by enormous overhead of dealing with publisher/subscriber pattern in Java (involving interfaces, finding proper places for callbacks, and what's not). Fortunately, Otto bus implementation caught my eyes, which made communication between fragments a trivial thing. Not only it is possible to notify all subscribers about selection change, but also the whole Loader Patter fit nicely:
Borrow BusProvider class from Otto's sample code.
Create few message contracts to carry notification data:
public class LocationSelectedEvent {
public long id;
}
public class LocationsLoadedEvent {
public Cursor cursor;
}
Annotate "receiver" methods in fragments with @Subscribe (example below is for loader case, for selection change it's no more complex):
@Subscribe
public void onLoadFinished(LocationsLoadedEvent event) {
final CursorAdapter a = (CursorAdapter) getListAdapter();
a.swapCursor(event.cursor);
}
Make fragments "listening" to notifications:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
BusProvider.getInstance().register(this);
}
Make fragments to stop listening when they're not "alive" (specially true for fragments API, learned it the hard way):
@Override
public void onDestroy() {
super.onDestroy();
BusProvider.getInstance().unregister(this);
}
Finally, trigger notifications where desired (example below deomnstrates how to notify from LocationList activity when cursor has been loaded):
@Override
public void onResume() {
if(null == getLoaderManager().getLoader(0)) {
getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int paramInt, Bundle paramBundle) {
return new CursorLoader(LocationsList.this, Locations.CONTENT_URI, null, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> paramLoader, Cursor cursor) {
BusProvider.getInstance().post(new LocationsLoadedEvent(cursor));
}
@Override
public void onLoaderReset(Loader<Cursor> paramLoader) {
BusProvider.getInstance().post(new LocationsLoadedEvent(null));
}
});
}
super.onResume();
}
Bonus: notifications flow visualization