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:
onAttach
method of the ListFragment
do with mCallbacks = (Callbacks) activity
?onItemSelected
method is called in onListItemClick
method of ListFragment
, the one which needs implementation or onItemSelected
in FragmentActivity
?onItemSelected
methods take an id
of type String
(because DummyContent
id
is a String
). If I changed DummyContent
id
to a long
, which onItemSelected
methods would I need to change? I tried changing the one in FragmentActivity
but this has @Override
so 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);
}
}
}
What does the onAttach method of the ListFragment do with mCallbacks = (Callbacks) activity?
In the onAttach
callback the Activity
/FragmentActivity
(to which the Fragment
will be tied/used) is passed to that Fragment
instance(through the activity parameter). That line simply casts that Activity
to the Callbacks
interface to later be used in the Fragment
(see below). Your Activity
must implement that interface otherwise the code will fail due to the previous if
condition. Basically, the code in the onAttach
method says: "The Activity where this fragment will be used must implement the Callbacks interface otherwise the code will fail with an IllegalStateException".
Which onItemSelected method is called in onListItemClick method of ListFragment, the one which needs implementation or onItemSelected in FragmentActivity?
In a very raw explanation: The RecordingListFragment
keeps a reference(the mCallbacks field) to a Callbacks
type object(meaning a class which implements the Callbacks
interface). Initially, to this Callabacks
reference the code will assign a default/empty Callbacks
reference(sDummyCallbacks) which doesn't do anything(so no, you don't have to provide some implementation for the sDummyCallbacks
) as its onItemSelected is empty, this is to avoid a possible NullPointerException
in some cases(for example, if you don't assign something to the mCallbacks
field and you call onItemSelected
on it). When the onAttach
method is run the reference to the Activity
where this Fragment
will exist will be cast to a Callbaks
and put in the mCallbacks
field. After this happens, when you call mCallbacks.onItemSelected()
the FragmentActivity
's onItemSelected
method will be called and the code from the method will be run. If at a later point the onDetach
is called, the mCallbacks
will once again point to the sDummyCallbacks
, after this happens, calling mCallbacks.onItemSelected()
will do nothing.
The interface system above is important because it will make your RecordingListFragment
a much more reusable component in your code as it will not be tied to a specific Activity
implementation. When the user clicks an item in your fragment's list you'll call the onItemSelected
on the mCallbacks
reference to run the onItemSelected
method 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 a RecordingListFragment
fragment. How you would need to change the RecordingListFragment
class to make it work with the three activities where it will be used?
All these onItemSelected methods take an id of type String (because DummyContent id is a String). If I changed DummyContent id to a long, which onItemSelected methods would I need to change? I tried changing the one in FragmentActivity but this has @Override so I wasn't allowed to :(
Modify the interface:
public interface Callbacks {
public void onItemSelected(long id);
}
If you save the java file Eclipse will complain(that you must override a superclass method) where this interface was implemented. For the sDummyCallbacks
:
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
// NEEDS IMPLEMENTATION - i guess???? <- it doesn't need no implementation
// it's purpose is to do nothing
public void onItemSelected(long id) {
}
};
Also the FragmentActivity
implements the Callbacks
interface so you need to change that too:
public class RecordingListActivity extends FragmentActivity
implements RecordingListFragment.Callbacks {
@Override
public void onItemSelected(long id) {
if (mTwoPane) {
// ...
finally you would use the mCallbacks
in your fragment:
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
mCallbacks.onItemSelected(id);
}
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.