Tuesday, April 23, 2013

Display UP icon on action bar and implement BACK navigation in Fragment Transaction

Follow the exercise "Allow navigate BACK through FragmentTransaction, by calling addToBackStack()", we are going to add a UP icon on non-home fragment, MyDetailFragment, to navigate BACK in the task history.

UP icon on action bar


  • To display/hide the UP icon, call getActionBar().setDisplayHomeAsUpEnabled(true/false) method.
  • Handle MenuItem of android.R.id.home in onOptionsItemSelected() to detect the UP icon clicked event.
  • To navigate back, call getFragmentManager().popBackStack().
  • Save and restore DisplayOptions of ActionBar in onSaveInstanceState() and onRestoreInstanceState(). Otherwise, the UP icon will disappear after orientation changed.

Modify MainActivity.java in "Allow navigate BACK through FragmentTransaction, by calling addToBackStack()".

package com.example.androiddualmode;

import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 // if run on phone, isSinglePane = true
 // if run on tablet, isSinglePane = false
 static boolean isSinglePane;
 
 static String[] month ={
   "January", "February", "March", "April",
   "May", "June", "July", "August",
   "September", "October", "November", "December"};

 public static class MyListFragment extends ListFragment {

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onActivityCreated(savedInstanceState);
   
   ListAdapter myArrayAdapter = 
     new ArrayAdapter<String>(
       getActivity(), android.R.layout.simple_list_item_1, month);
   setListAdapter(myArrayAdapter);
   
  }

  @Override
  public void onListItemClick(ListView l, View v, int position, long id) {
   
   String clickedDetail = (String)l.getItemAtPosition(position);
   
   if(isSinglePane == true){
    /*
     * The second fragment not yet loaded. 
     * Load MyDetailFragment by FragmentTransaction, and pass 
     * data from current fragment to second fragment via bundle.
     */
    MyDetailFragment myDetailFragment = new MyDetailFragment();
    Bundle bundle = new Bundle();
    bundle.putString("KEY_DETAIL", clickedDetail);
    myDetailFragment.setArguments(bundle);
    FragmentTransaction fragmentTransaction =
      getActivity().getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.phone_container, myDetailFragment);
    
    /*
     * Add this transaction to the back stack. 
     * This means that the transaction will be remembered after it is 
     * committed, and will reverse its operation when later popped off 
     * the stack.
     */
    fragmentTransaction.addToBackStack(null);
    
    fragmentTransaction.commit();
    
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
    
   }else{
    /*
     * Activity have two fragments. Pass data between fragments
     * via reference to fragment
     */
    
    //get reference to MyDetailFragment
    MyDetailFragment myDetailFragment =
      (MyDetailFragment)getFragmentManager().findFragmentById(R.id.detail_fragment);
    myDetailFragment.updateDetail(clickedDetail);
    
   }
  }

 }
 
 public static class MyDetailFragment extends Fragment {
  
  TextView textDetail;

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   View view = inflater.inflate(R.layout.layout_detailfragment, null);
   textDetail = (TextView)view.findViewById(R.id.text_detail);
   
   Bundle bundle = getArguments();
   if(bundle != null){
    String detail = bundle.getString("KEY_DETAIL", "no argument pass");
    textDetail.setText(detail);
   }
   
   return view;
  }

  public void updateDetail(String detail) {
   textDetail.setText(detail);
  }

 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  View v = findViewById(R.id.phone_container);
  if(v == null){
   //it's run on tablet
   isSinglePane = false;
   /*
    * MyListFragment and MyDetailFragment have been loaded in XML,
    * no need load.
    */
   
  }else{
   //it's run on phone
   //Load MyListFragment programmatically
   isSinglePane = true;
   
   if(savedInstanceState == null){
    //if's the first time created
    MyListFragment myListFragment = new MyListFragment();
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.add(R.id.phone_container, myListFragment);
    fragmentTransaction.commit();
   }
  }
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case android.R.id.home:
   getFragmentManager().popBackStack();
   getActionBar().setDisplayHomeAsUpEnabled(false);
            return true;    
  }
  
  return super.onOptionsItemSelected(item);
 }
 
 final static String KEY_DISPLAY_OPT = "KEY_Display_Option";

 @Override
 protected void onSaveInstanceState(Bundle outState) {
  // TODO Auto-generated method stub
  super.onSaveInstanceState(outState);
  outState.putInt(KEY_DISPLAY_OPT, getActionBar().getDisplayOptions());
 }
 
 
 @Override
 protected void onRestoreInstanceState(Bundle savedInstanceState) {
  // TODO Auto-generated method stub
  super.onRestoreInstanceState(savedInstanceState);
  int savedDisplayOpt = savedInstanceState.getInt(KEY_DISPLAY_OPT);
  if(savedDisplayOpt != 0){
   getActionBar().setDisplayOptions(savedDisplayOpt);
  }
 }

}


download filesDownload the files.

Next:
- implements OnBackStackChangedListener to display UP icon, automatically by getBackStackEntryCount()


No comments: