Introducing Android Fragments
Written by Mike James   
Thursday, 13 February 2014
Article Index
Introducing Android Fragments
Using a Fragment
The Activity
Summary and Conclusion

Using a Fragment

Now that we have our customized fragment complete with a very simple UI it is time to display it in an Activity. 

This is perhaps the most complicated part of using a Fragment although once you get used to it its not so difficult. 

The first new feature is that every Activity has a FragmentManager which is used to control how Fragments are displayed. To display a Fragment you first have to obtain the FragmentManager using getFragmentManager. Then you have to get a FragmentTransaction from the FragmentManager.

You can't do anything with a Fragment unless you start a transaction. Within the transaction you can set up what you want to happen, usually add the Fragment to the current layout, but nothing happens until you use the commit method.  

The final complication is that the add method, used to add a Fragment to a specified container needs the container specified by id number. This fine for an XML layout but not so convenient for a code generated layout. 

That is if you have a FragmentTransaction ft then:

ft.add(id,frag1);

adds Fragment frag1 to the ViewGroup with the specified id. Notice this is not a Layout Id.

Lets see how to implement all of this.

Let's add the Fragment to the MainActivity using the usual onCreate method:

@Override
protected void onCreate(
             Bundle savedInstanceState){
 super.onCreate(savedInstanceState);

First we need to add a LinearLayout to act as a container for the rest of the UI and the Fragment in particular:

LinearLayout linLayout=new LinearLayout(this);

and this needs an id property so that we can store the Fragement's UI inside it.

You can simply assign an id using setId, it only has to be unique at the current level of the View hierarchy:

linLayout.setId(100);

Finally we can add the LinearLayout to the ContentView:

setContentView(linLayout);

Apart from the need to assign a ViewId to there is nothing new so far.

Now we need to add the Fragment.

To do this we create an instance of the Fragment, get the FragmentManager and use it to get a FragmentTransaction object:

myFragment frag1=new myFragment();
FragmentManager fm=getFragmentManager();
FragmentTransaction ft=fm.beginTransaction();

Now we can add the Fragment to the LinearLayout container using its ViewId to identify it and finally commit the transaction to make it all happen:

ft.add(100,frag1);
ft.commit();

As well as the add method, the FragmentTransaction also has a replace and remove method.

The subject of managing Fragments is discussed in a later installment.

 

Puttting all this together gives:

 

@Override
protected void onCreate(
                Bundle savedInstanceState){
 super.onCreate(savedInstanceState);
 LinearLayout linLayout=
                   new LinearLayout(this);
 linLayout.setId(100);
 setContentView(linLayout);

 
 myFragment frag1=new myFragment();
 FragmentManager fm=getFragmentManager();
 FragmentTransaction ft=fm.beginTransaction();
 ft.add(100,frag1);
 ft.commit();
}

 

If you run the program you will now see a Button appear.

The View hierarchy after the Fragment has been added consists of a single LinearLayout created by the Activity, containing a LinearLayout created by the Fragment, which contains a Button created by the Fragment.

 

firstprog

There are some subtle points to note that aren't important at the moment, but could come back to bite you when you try to do some more advanced things.

The first is that the Fragment's View hierarchy is not constructed at the end of the transaction, i.e. immediately after the commit. The onCreateView event is fired some time after the onCreate event handler returns. This means you cannot access the View objects added by the Fragment within the onCreate method.  

Secondly, at the moment we are creating the Fragment each time the onCreate method is fired by the Activity's Create event but the system automatically saves and restores Fragments when the Activity is suspended. In particular the entire Activity and its associated Fragments are destroyed if a configuration change such as a orientation change occurs. In this case the system recreates the Fragment and the Activity for you. 

You need to keep this in mind because this automatic destroy- recreate mechanism causes most of the complications you will encounter in using Fragments.

 

The current onCreate method works, but it is inefficient because it manually creates the Fragment after the system has restored the old instance. 

A better way to code the onCreate method is:

@Override
protected void onCreate(
              Bundle savedInstanceState){
 super.onCreate(savedInstanceState);
 LinearLayout linLayout=
             new LinearLayout(this);
 linLayout.setId(100);
 setContentView(linLayout);
 if (savedInstanceState == null) {
  myFragment frag1=new myFragment();
  FragmentManager fm=getFragmentManager();
  FragmentTransaction ft=
                   fm.beginTransaction();
  ft.add(100,frag1);
  ft.commit();
 }
}

By checking to see if there is any saved instance state we can avoid creating the Fragment when it isn't needed. 

Test that this works by running the app and then rotating the device. 

IMPORTANT NOTE:
If you find that the rotation test doesn't work you are probably testing using 4.4.x (API19) in which the emulator has a bug which means it doesn't respond to rotation commands. The solution is to downgrade to 4.3 (API18) until the problem is solved.

We need to look more at how Fragments can retain their state when the system destroys and recreates them.

By now you might well be thinking that this is a lot of work to go to when you could have just added the Button to the layout in the Activity. This is true enough and the reason that Fragments and their use have been left until now. Android Studio does have a way of making all of this seem much easier but at some point you do have to understand what it is doing to get any value from using Fragments.

It is time to move on and look at how to make Fragments do something.

Fragment Events

The next topic we have to consider is the issue of how much work a Fragment should do.

For example, when a button is clicked what should handle the event? 

The idea of a Fragment is that it is a wrapped up chunk of UI that can be used by any Activity that needs it.

On this basis you have to ask yourself if the event that has to be handled is the same for every Activity or unique to each Activity. If it is the same then the event handler is better written within the Fragment. 

Let's see how to do this. 

The principle of getting a Fragment to handle an event is easy enough to understand. The Fragment doesn't have a UI of its own - it is displayed by an Activity  that the Fragment is associated with. The events are generated by objects in the View hierarchy, which is owned by the Activity. If you try to use Android Studio to add an event handler, for example, it will add it to the Activity and not to the Fragment. 

You can, however, define the EventListener that you want to handle the event in the Fragment and then hook it up to the View object in the Activity in which you want to generate the event.

Creating the event handler follows the same two possible patterns that you used to create it in the Activity.

Starting in the Fragment you can either make it implement the necessary interface or you can use an anonymous class. The anonymous class approach is the more flexible so let's do it this way.

First let's add a TextView to be used in the event handler. The user can click the button and transfer the button's text to the TextView. Add the following code to the onCreateView method:

final TextView tv=new TextView(getActivity());
tv.setText("Text Entry");
linLayout.addView(tv);

The reason for the use of the final keyword will become apparent.

Next we have to add an instance of an OnClickListener object:

 

View.OnClickListener onclick=
                  new View.OnClickListener(){
  @Override
  public void onClick(View view){
   Button bt=(Button) view;
   tv.setText(bt.getText());
 }
};

If you recall Java uses objects to handle events. You create an object of the correct type using an anonymous class which avoids having to create a named class in a separate file. 

In this case the OnClickListener has just one method the onClick event handler that we have to override. It is assumed that the event handler will only be attached to a Button so we are safe in casting the view parameter to a Button object. Next we can use the button text to set the TextView's text. 

At this point you might be worried by the use of the local variable tv as it doesn't belong to the OnClickListener object but the containing Fragment. How can this class access a variable that isn't part of it?



Last Updated ( Saturday, 16 August 2014 )
 
 

   
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.