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

 

In Java an inner class, like the OnClickListener has access to the local variables of its containing class but only if they are declared final. A variable that is declared final can only be assigned to once and in this case there is no reason not to declare the TextView as final - it isn't going to change the object it references.

Now all we have to do is set the onClick hander of the button to the OnClickListener object onclick that we have created:

b.setOnClickListener(onclick);

This is all we have to do. Notice however that it is slightly more subtle than you might think as all of the View objects are only created when the Activity calls the onCreateView method. 

The complete Fragment class is now:

 

public class myFragment extends Fragment {
@Override
public View onCreateView(
                 LayoutInflater inflater,
                 ViewGroup container,
                 Bundle savedInstanceState) {

 LinearLayout linLayout=
            new LinearLayout(getActivity());
 
 Button b = new Button(getActivity());
 b.setText("Hello Button");
 linLayout.addView(b);
 
 final TextView tv=new TextView(getActivity());
 tv.setText("Text Entry");
 linLayout.addView(tv);
 
 View.OnClickListener onclick=
                   new View.OnClickListener(){
  @Override
  public void onClick(View view){
   Button bt=(Button) view;
   tv.setText(bt.getText());
  }
 };
 b.setOnClickListener(onclick);
 return linLayout;}
}

 

If you now run the program you will find that when the button is clicked its text is transferred to the TextView. Notice that this would be true no matter what Activity used the Fragment. 

It is behavior inherent to the Fragment. 

You also need to be clear what is going on when the system destroys and recreates the Fragment. 

If you run the app and click the button you will see the TextView change. If you then rotate the device you will see that the TextView reverts to its original text. If you click the button you will again see that the text changes i.e. the event handler is still working. 

What happens is that the system calls the onCreateView when it recreates the Fragment - this sets up the UI and attaches the event handler. All previous state information is lost. 

If you want to keep the state information through a destroy-create cycle you have to use the savedInstanceState Bundle that is passed to the onCreateView method. This works in much the same way as for the Activity.

Saving State

Saving the state of a Fragment is very easy.

All you have to do is override the onSaveInstanceState event handler and use the savedInstanceState bundle to store any additional data you need to persist. For example to keep the TextView's current content all you have to do is:

@Override
public void onSaveInstanceState(
                    Bundle savedInstanceState) {
 super.onSaveInstanceState(savedInstanceState);
 TextView tv=
       (TextView) getView().findViewById(110);
 savedInstanceState.putCharSequence(
                       "myText",tv.getText());
}

 

Notice the use of getView to retrieve the View hierarchy created by the Fragment and the usual findViewById. To make this work we also need to add an Id to the TextView in the onCreateView:

tv.setId(110);

This saves the state information but we still have to manually restore it in the onCreateView:

if (savedInstanceState != null) {
 tv.setText(savedInstanceState.getCharSequence(
                                    "myText"));
};

You can also use the onCreate event handler to restore values to variables. It is called before the onCreateView event handler so don't try to use it to modify the View hierarchy.

The complete Fragment is:

 

public class myFragment extends Fragment {

 @Override
 public View onCreateView(
                  LayoutInflater inflater,
                  ViewGroup container,
                  Bundle savedInstanceState) {
  LinearLayout linLayout=
              new LinearLayout(getActivity());
  Button b = new Button(getActivity());
  b.setText("Hello Button");
  linLayout.addView(b);
  final TextView tv=new TextView(getActivity());
  tv.setText("Text Entry");
  tv.setId(110);
  linLayout.addView(tv);
  View.OnClickListener onclick=
                     new View.OnClickListener(){
   @Override
   public void onClick(View view){
    Button bt=(Button) view;
    tv.setText(bt.getText());
   }
  };
  b.setOnClickListener(onclick);
  if (savedInstanceState != null) {
   tv.setText(savedInstanceState.getCharSequence(
                                    "myText"));
  };
  return linLayout;
 }


 @Override
 public void onSaveInstanceState(
                   Bundle savedInstanceState) {
 super.onSaveInstanceState(savedInstanceState);
 TextView tv=
       (TextView) getView().findViewById(110);
 savedInstanceState.putCharSequence( 
                      "myText",tv.getText());
 }
}

What Does The Activity Do?

In a lot of cases the Fragment is the right place to handle events but what about the Activity - does it get to do anything useful?

The whole point of a UI, even a fragment of a UI, is to provide some data to or display some data from the application. At some point the Fragment has to accept data or give data to the application and the Activity in particular. 

To get data from the Fragment there is nothing new as the Activity can simply access the View objects created by the Fragment in the usual way using findViewById or similar. To put data to the Fragment the Activity can use the same technique. The Fragment can also offer the Activity public properties which it can set and get. There is nothing new here.

The only complication is that the Fragment can be destroyed and recreated by the system by calling its onCreateView. What this means is that any public properties it might have could be re-initialised at any time. 

The solution is, of course, to preserve the state of the Fragment using the Bundles provided for the job.

There is a tricky problem if the Activity tries to attach an event handler to a View provided by the Fragment. There is nothing wrong with creating a listener in the Activity and setting it as the event handler for a View object created by the Fragment. This works perfectly until the moment the Fragment is destroyed and recreated by the system calling the onCreateView. At this point the View object that the event handler was attached to is recreated and the connection to the event handler is lost.

The simplest solution is to set the event handler each time the Activity onCreate is called but this turns out to have lots of subtle difficulties - there are better but more elaborate solutions which will be explained in detail in the next chapter. 

You just need to be very aware that the fact that the Fragment can be destroyed and recreated at any time makes its use more complicated than you might first think.

When you have a single Fragment backed by an Activity there seems to be little point in splitting the code between the two. However if you keep in mind the idea that the Fragment should provide the UI and the Activity should provide the processing then you have a good division of concerns and code which should allow the Fragment or the Activity to be reused. If you know about the MVC - Model View Controller - design pattern then you can think of the Fragment as the View and the Activity as the Model. 

Things get much more interesting when you build an application with multiple Fragments and this is something we have to look at in a later installment. 



Last Updated ( Thursday, 20 November 2014 )
 
 

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