Thursday, July 10, 2014

Get a reference to the current Fragment in a Page Tab


Fragment Reference

I have been experimenting with design patterns suggested by an article I recently read.
What I want to to is create an application with a navigation drawer, each selection in the navigation can display screens with tabs, within one of these Tabs I will display a master detail record.
In English
The user can start the application, select the orders option, this will display a tabbed page in the first tab we have a master detail with a list of clients on the left and the application will display client details for the client selected.

As I mentioned previously, these patterns rely heavily on fragments.




Because all the embedded fragments are created dynamically we will not be ables to use an id to get a reference to the fragment.

Summary :

We modify the OrderTabsPagerAdapter which sets/manages  the tabs to store the Fragment pointer of the current tab displayed, we modify the Tabs parent activity to pass a reference of the Pager adapter to the main activity. In the main activity we use the reference to the current page fragment to call a method in this page to display client details.

The Main activity will set up the navigation drawers.

The main activity needs to implement  ClientListFragment.Callbacks this method will be called when the user selects a client , we need to get the customer id of the selected client.


When the user selects the orders drawer we will display and Order activity  which extends Fragment and implements ActionBar.TabListener 

The order activity will use a FragmentPagerAdapter to set up the tabs in the Order activity, we need to modify the FragmentPagerAdapter to store the current fragment being displayed and get a reference to it.

We need to add the following two methods to the adapter.

The setPrimary item is called by the framework and we will use it to store the current fragment reference  displayed.
@Override
   public void setPrimaryItem(ViewGroup container, int position, Object object) {
       if (getCurrentFragment() != object) {
           mCurrentFragment = ((Fragment) object);
       }
       super.setPrimaryItem(container, position, object);
   }
We create a method to recover the current fragment.
   public Fragment getCurrentFragment() {
       return mCurrentFragment;

   }


In the order activity we will update the Main activity with a reference to the pager adapter.

  MainActivity ma = (MainActivity) getActivity();    

  ma.opa = mAdapter;

The First Tab in the Order Activity will be a Client fragment

This fragment will dynamically create a customer list fragment , we will also need to call this fragment to display customer details when a customer has been selected from a list.


The customer list fragment will make sure the main activity implements implements the call back interface.
When the user selects an item (customer) , the list fragment will call the call back method in the main activity.

The call back method in the main activity ;

Uses the reference to the pager adapter passed to the main activity  by the orders activity above to get a reference to the current page tab fragment displayed.
With this fragment reference we can call a method in the customer fragment that will display the details for the client selected.



/**
* Callback method from {@link ClientListFragment.Callbacks} indicating that
* the item with the given ID was selected.
*/
@Override
public void onItemSelected(String id) {
    CustomerFragment tf = (CustomerFragment) opa.getCurrentFragment();
    tf.showdetails();

}



Displaying Google Map Inside Android Fragment

Display Google Fragment Maps in a Tab Fragment


Problem



I had problems finding information on how to display the Google map fragment within a tab pager.

This is a real problem because fragments are used extensively in master detail, tabs and navigation drawer patterns.

The solution was simple so I decided to post it, hope it helps some one.


I have an application where a user can select a contact, and then display details for the contact in tabs, for example all photos we have taken for this contact (from my EasySnap application), incoming and outgoing calls , address etc…




The user can move from one set of details to the next by selecting a Tab or swiping. The content for each tab is in a Fragment. I was unable to add the Google Map Fragment which would display the contacts location to a Tab fragment.
The problem was that until recently you could not add a Fragment to a Fragment, even now you can not create static fragments within a fragment.

Solution

You can now create fragments in Fragments if you create them dynamically in the fragment code.

1. Create a container for the Map Fragment in the layout for the tab fragment which will contain the map.


<FrameLayout

          android:layout_width="match_parent"

          android:layout_height="0dp"
          android:layout_weight="1.03"
          android:name="com.google.android.gms.maps.SupportMapFragment"
          android:id="@+id/mapwhere" />

2. We create the Map fragment in the code of the tab which displays the "Map" fragment. One important thing to note is we use getChildFragmentManager() to access the Fragment manager because we are in a fragment.


public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getChildFragmentManager();
fragment = (SupportMapFragment) fm.findFragmentById(R.id.mapwhere);
if (fragment == null) {
fragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.mapwhere, fragment).commit();
}

}
3. Gotcha To add locations to the map we need a pointer to the map created in the code above using map = fragment.getMap(); , it is tempting to add this code in the on ActivityCreated above, But the fragment references is not available until the onResume method is called.

With a reference to the map we can start adding locations as follows


map.addMarker(new MarkerOptions().position(new LatLng(latitude, longitude)).title("My Home").snippet("Home Address"));