The Eclipse generated master detail flow has some callback magic in the class which extends ListFragment – I say magic because that’s what it looks like when you are an Android and a Java noob, all at once 🙂
Given the code below can someone answer a few questions for me:
- What does the
onAttachmethod of theListFragmentdo withmCallbacks = (Callbacks) activity? - Which
onItemSelectedmethod is called inonListItemClickmethod ofListFragment, the one which needs implementation oronItemSelectedinFragmentActivity? - All these
onItemSelectedmethods take anidof typeString(becauseDummyContentidis aString). If I changedDummyContentidto along, whichonItemSelectedmethods would I need to change? I tried changing the one inFragmentActivitybut this has@Overrideso I wasn’t allowed to 🙁
Thank you
public class RecordingListFragment extends ListFragment {
private static final String STATE_ACTIVATED_POSITION = "activated_position";
private Callbacks mCallbacks = sDummyCallbacks;
private int mActivatedPosition = ListView.INVALID_POSITION;
public interface Callbacks {
public void onItemSelected(String id);
}
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
// NEEDS IMPLEMENTATION - i guess????
public void onItemSelected(String id) {
}
};
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
}
...
}
And one more file…
public class RecordingListActivity extends FragmentActivity
implements RecordingListFragment.Callbacks {
private boolean mTwoPane;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording_list);
getActionBar().setDisplayHomeAsUpEnabled(true);
if (findViewById(R.id.recording_detail_container) != null) {
mTwoPane = true;
((RecordingListFragment) getSupportFragmentManager()
.findFragmentById(R.id.recording_list))
.setActivateOnItemClick(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onItemSelected(String id) {
if (mTwoPane) {
Bundle arguments = new Bundle();
arguments.putString(RecordingDetailFragment.ARG_ITEM_ID, id);
RecordingDetailFragment fragment = new RecordingDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.recording_detail_container, fragment)
.commit();
} else {
Intent detailIntent = new Intent(this, RecordingDetailActivity.class);
detailIntent.putExtra(RecordingDetailFragment.ARG_ITEM_ID, id);
startActivity(detailIntent);
}
}
}
In the
onAttachcallback theActivity/FragmentActivity(to which theFragmentwill be tied/used) is passed to thatFragmentinstance(through the activity parameter). That line simply casts thatActivityto theCallbacksinterface to later be used in theFragment(see below). YourActivitymust implement that interface otherwise the code will fail due to the previousifcondition. Basically, the code in theonAttachmethod says: “The Activity where this fragment will be used must implement the Callbacks interface otherwise the code will fail with an IllegalStateException”.In a very raw explanation: The
RecordingListFragmentkeeps a reference(the mCallbacks field) to aCallbackstype object(meaning a class which implements theCallbacksinterface). Initially, to thisCallabacksreference the code will assign a default/emptyCallbacksreference(sDummyCallbacks) which doesn’t do anything(so no, you don’t have to provide some implementation for thesDummyCallbacks) as its onItemSelected is empty, this is to avoid a possibleNullPointerExceptionin some cases(for example, if you don’t assign something to themCallbacksfield and you callonItemSelectedon it). When theonAttachmethod is run the reference to theActivitywhere thisFragmentwill exist will be cast to aCallbaksand put in themCallbacksfield. After this happens, when you callmCallbacks.onItemSelected()theFragmentActivity‘sonItemSelectedmethod will be called and the code from the method will be run. If at a later point theonDetachis called, themCallbackswill once again point to thesDummyCallbacks, after this happens, callingmCallbacks.onItemSelected()will do nothing.The interface system above is important because it will make your
RecordingListFragmenta much more reusable component in your code as it will not be tied to a specificActivityimplementation. When the user clicks an item in your fragment’s list you’ll call theonItemSelectedon themCallbacksreference to run theonItemSelectedmethod from that object(the Activity in your case). Your fragment doesn’t know how implements the interface and it doesn’t even care. Think, for example that you have three activities and each one uses aRecordingListFragmentfragment. How you would need to change theRecordingListFragmentclass to make it work with the three activities where it will be used?Modify the interface:
If you save the java file Eclipse will complain(that you must override a superclass method) where this interface was implemented. For the
sDummyCallbacks:Also the
FragmentActivityimplements theCallbacksinterface so you need to change that too:finally you would use the
mCallbacksin your fragment:I would recommend that you take some time to study a bit about the java language, so you’ll work to understand the Android code and not the Android code + the java way.