I have an app with master/detail layout (1 activity, 1 ListView fragment and 1 detail fragment). When the user clicks an item in the ListView, a fragment transaction instantiates a detail fragment on the right-pane that includes the information corresponding to that item. When the detail fragment is shown I hide the initial action bar buttons/items and show 3 new AB items (done/delete/cancel). The user can clean the right-pane and return to the initial UI state by either pressing the back button or by pressing one of the 3 AB items.
The issue I'm experiencing is that when the user selects the app's home icon (i.e. "up navigation") the activity gets re-loaded (i.e. the animation that indicates that the activity is starting can be seen as both the action bar and the UI is been redrawn). The issue only happens when the app home icon is pressed. If the user presses the back button or a cancel/done/delete action bar button, the fragment is simply remove from the right-pane and the UI returns to initial state without any "re-loading".
The XML layout for the activity is the following (inside LinearLayout; prettify is hiding that line):
<fragment class="*.*.*.ListFragment"
android:id="@+id/titles" android:layout_weight="1"
android:layout_width="0px"
android:layout_height="match_parent" />
<FrameLayout android:id="@+id/details" android:layout_weight="2"
android:layout_width="0px"
android:layout_height="match_parent" />
The DetailsFragement has the actionBar.setDisplayHomeAsUpEnabled statement in its onCreate method:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
ActionBar actionBar = getSherlockActivity().getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
For both the ListView fragment and the Detail fragments the onCreateOptionsMenu() and onOptionsItemSelected() method are implemented within the fragments. Below the code for the Details fragment:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.edit_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// some variable statements...
switch (item.getItemId()) {
case android.R.id.home:
//Toast.makeText(getSherlockActivity(), "Tapped home", Toast.LENGTH_SHORT).show();
onHomeSelectedListener.onHomeSelected();
return true;
case R.id.menu_edit_item_done:
editedTask.setTitle(editedTaskTitle);
onTaskEditedListener.onTaskEdited(editedTask, UPDATE_TASK, true);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
In the host activity I implement the onHomeSelectedListner to handle the app home icon press (i.e. "up navigation":
public void onHomeSelected(){
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
TaskFragment taskFragment = (TaskFragment)getSupportFragmentManager().findFragmentById(R.id.details);
ft.remove(taskFragment);
ft.commit();
manager.popBackStack();
}
The activity's listener in charged of handling all other action bar buttons (i.e. done/delete/cancel) is onTaskEditedListener and, aside of other code that processes some data, it has the same fragment transactions shown above.
Update(1/24) Based on tyczj and straya feedback I placed log statements inside onCreate(), onResume(), onPause() of the activity to determine the differences between onHomeSelected and onTaskEdited listeners. I'm able to confirm that during the "up navigation" event (i.e. onHomeSelected) onPause(), onCreate() and onResume() are called. Whereas during the onTaskEdited call (i.e. back button or done/delete/cancel press) none of those events are called.
Update (1/25) Based on a suggestion by Mark Murphy, I commented out the onHomeSelected method call in the "case android.R.id.home" statement just to see what would the Activity do. The thinking was that the app would do nothing since the are no statements. Turns out that is not the case. Even without a call to the listener method (i.e. that removes the fragment), the activity is restarted and the detail fragment is removed from the fragment container.
Update (2/28) I temporarily workaround the fact that my main activity was getting restarted by disabling the window animations (as highlighted in my own answer). However, through further testing I uncovered a bug. Thanks to Wolfram Rittmeyer's sample code I was able to figure out the real reason(s) why my activity was restarting (in master/detail single layout) during up navigation: 1) Although I was using this "onHomeSelectedListener" to properly remove the fragment from the backstack, I still had some remnant code in the ListView fragment's onOptionsItemSelected that was creating a new intent to start the hosting activity. That's why pressing the app's home icon was re-starting the activity. 2) In my final implementation (shown in my own answer), I got rid of the onHomeSelectedListener in the activity and replace the startActivity intent (i.e. offending code) inside the ListView's onOptionsItemSelected to use the fragment removal + popBackStack code originally in the onHomeSelectedListener.
I think you should handle the Up button only inside the activity. If youre in a phone, the up button will be handled by activity that acts as a wrapper of that fragment, in tablet (master/detail pattern) you dont want it anyways
After much research and poking around, turns out that only reason why my activity was restarting during "up navigation" for master/detail configuration was because I left some code in the ListView Fragment's onOptionsItemSelected that was creating an intent to start the main activity in addition to my full fragment transaction code elsewhere. Below is the final implementation with which I got "up navigation" to work properly on both phone (multiple activities) and tablet (single activity/multi-pane) configurations. Thanks to Wolfram Rittmeyer for a couple of hints in his code (link in the comment section) that help me pinpoint my problem!
Main Activity: Hosts the fragments and performs some other app-specific operations
ListView Fragment: Handles "up navigation in table configuration
Details Fragment: Handles up navigation in phone configuration
If you look at the Navigation Design Pattern you will see that you want to return to the starting activity when the home button is hit.
So say you have 2 Activities call them
A1
andA2
. Clicking on something in A1 takes you to A2. If the user hits the home button you should return them to A1 clearing the stack of everything up until that activity like thisthis is what the flag
Intent.FLAG_ACTIVITY_CLEAR_TOP
doesdon't: break and then return super.onOptionsItemSelected(item), rather just: return true;
UPDATE:
So you're saying the Activity is "restarted" based on what you see happen with Views, but can you confirm what may or may not happen to the Activity (and Fragments for that matter) by using logging in the various lifecycle methods? That way you can be sure of what the current (erroneous) behaviour is before moving forward with diagnosis.
UPDATE:
OK, good to be sure about behaviour :) Now regarding your question "What is the correct way to implement "up navigation" for a master/detail layout (1 activity/2fragments)? ": The typical way is that the 2 Fragments got added within a single FragmentTransaction and you simply popBackStack to remove them and go back to whatever previous state was. I think you're doubling up by manually removing a Fragment within a FragmentTransaction and then popping backstack. Try just popBackStack. Oh and just to be sure and consistent, since you're using ActionBarSherlock and support.v4 are you using a FragmentActivity (rather than an Activity) and SherlockFragment?