Difference between revisions of "Using Fragments in Android Studio - An Example"

From Techotopia
Jump to: navigation, search
(New page: <br><br> As outlined in the previous chapter, fragments provide a convenient mechanism for creating reusable modules of application functionality consisting of both sections of a user inte...)
 
(= Creating the First Fragment Layout)
Line 26: Line 26:
 
Click the Finish button to begin the project creation process.
 
Click the Finish button to begin the project creation process.
  
== Creating the First Fragment Layout =
+
== Creating the First Fragment Layout ==
  
 
The next step is to create the user interface for the first fragment that will be used within our activity.  
 
The next step is to create the user interface for the first fragment that will be used within our activity.  

Revision as of 19:28, 7 May 2014



As outlined in the previous chapter, fragments provide a convenient mechanism for creating reusable modules of application functionality consisting of both sections of a user interface and the corresponding behavior. Once created, fragments can be embedded within activities.

Having explored the overall theory of fragments in the previous chapter, the objective of this chapter is to create an example Android application using Android Studio designed to demonstrate the actual steps involved in both creating and using fragments, and also implementing communication between one fragment and another within an activity.


Contents


About the Example Fragment Application

The application created in this chapter will consist of a single activity and two fragments. The user interface for the first fragment will contain a toolbar of sorts consisting of an EditText view, a SeekBar and a Button, all contained within a RelativeLayout view. The second fragment will consist solely of a TextView object, also contained within a RelativeLayout view.

The two fragments will be embedded within the main activity of the application and communication implemented such that when the button in the first fragment is pressed, the text entered into the EditText view will appear on the TextView of the second fragment using a font size dictated by the position of the SeekBar in the first fragment.

Since this application is intended to work on earlier versions of Android, it will also be necessary to make use of the appropriate Android support library.

Creating the Example Project

Launch Android Studio and create a new project with both the Application and Module names set to FragmentExample. For the avoidance of confusion later in the chapter, be sure to use the following as the application package name:

com.ebookfrenzy.fragmentexample.fragmentexample

Select API 8: Android 2.2 (Froyo) as the minimum required SDK and the latest available SDK for the compilation and target SDK settings and verify that the Create activity option is selected.

Support for fragments was not introduced until Android 3.0. This means that for applications that make use of fragments to be compatible with older versions of Android, use must be made of version 4 of the Android Support Library. The purpose of the Android Support Library is to make features that were introduced in later versions of Android available to applications that need to be compatible with earlier Android versions. For this reason, enable the Fragments option in the Support Modes section before clicking on the Next button to proceed. On the subsequent screen, select the Blank Activity option and, on the final screen, name the activity FragmentExampleActivity with a corresponding layout named activity_fragment_example.

Click the Finish button to begin the project creation process.


Creating the First Fragment Layout

The next step is to create the user interface for the first fragment that will be used within our activity. This user interface will, of course, reside in an XML layout file so begin by navigating to the layout folder located under FragmentExample -> src -> main -> res in the Project tool window. Once located, right-click on the layout entry and select the New -> Layout resource file menu option as illustrated in Figure 23-1:


Adding a layout resource file to an Android Studio project

Figure 23-1


In the resulting dialog, name the layout toolbar_fragment and change the root element from LinearLayout to RelativeLayout before clicking on OK to create the new resource file.

The new resource file will appear within the Designer tool ready to be designed. Switch the Designer to Text mode and modify the XML so that it reads as outlined in the following listing to add three new view elements to the layout:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/seekBar1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="17dp"
        android:text="Change Text" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:inputType="text" >
        <requestFocus />
    </EditText>

    <SeekBar
        android:id="@+id/seekBar1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/editText1"
        android:layout_marginTop="14dp" />

</RelativeLayout>

Once the changes have been made, switch the Designer tool back to Design mode. Select the button view and click on the light bulb icon followed by the I18N message to display the Extract Resource dialog. Name the resource button_text and click on OK to create a string resource for the button.

Upon completion of these steps, the user interface layout should resemble that of Figure 23-2:


The user interfaced layout for the toolbar fragment

Figure 23-2


With the layout for the first fragment implemented, the next step is to create a class to go with it.

Creating the First Fragment Class

In addition to a user interface layout, a fragment also needs to have a class associated with it to do the actual work behind the scenes. Add a class for this purpose to the project by unfolding the src -> main -> java folder under the FragmentExample project in the Project tool window and right-clicking on the package name given to the project when it was created (in this instance com.ebookfrenzy.fragmentexample.fragmentexample). From the resulting menu, select the New -> Java Class option. In the resulting Create New Class dialog, name the class ToolbarFragment and click on OK to create the new class.

Once the class has been created, it should, by default, appear in the editing panel where it will read as follows:

package com.ebookfrenzy.fragmentexample.fragmentexample;

/**
 * Created by <name> on <date>.
 */
public class ToolbarFragment {
}

For the time being, the only changes to this class are to add some import directives and to override the onCreateView() method to make sure the layout file is inflated and displayed when the fragment is used within an activity. The class declaration also needs to indicate that the class extends the Android Fragment class:

package com.ebookfrenzy.fragmentexample.fragmentexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class ToolbarFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container, Bundle 
				savedInstanceState) {

        // Inflate the layout for this fragment
        View view =  inflater.inflate(R.layout.toolbar_fragment,
                container, false);
        return view;
    }
}

Later in this chapter, more functionality will be added to this class. Before that, however, we need to create the second fragment.

Creating the Second Fragment Layout

Add a second new Android XML layout resource file to the project, once again selecting a RelativeLayout as the root element. Name the layout text_fragment and click OK. When the layout loads into the Designer tool, change to Text mode and modify the XML to add a TextView to the fragment layout as follows:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Fragment Two"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

Once the XML changes have been made, switch back to Design mode, select the TextView component and click on the light bulb icon followed by the I18N message to display the Extract Resource dialog. Name the resource text_label and click on OK to create a string resource for the button. Upon completion of these steps, the user interface layout for this second fragment should resemble that of Figure 23-3:


The second Fragment UI layout in Android Studio Designer

Figure 23-3


As with the first fragment, this one will also need to have a class associated with it. Unfold the src category under the FragmentExample project (if it is not already unfolded) in the Project tool window and right-click on the package name. From the resulting menu, select the New -> Java Class option. Name the fragment TextFragment and click OK to create the class.

Edit the new TextFragment.java class file and modify it to implement the onCreateView() method and designate the class as extending the Android Fragment class:

package com.ebookfrenzy.fragmentexample.fragmentexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class TextFragment extends Fragment {
    
    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.text_fragment,
                container, false);

        return view;
    }
}

Now that the basic structure of the two fragments has been implemented, they are ready to be embedded in the application’s main activity.

Adding the Fragments to the Activity

The main activity for the application has associated with it an XML layout file named activity_fragment_example.xml. For the purposes of this example, the fragments will be added to the activity using the <fragment> element within this file. Using the Project tool window, navigate to the src -> main -> res -> layout section of the FragmentExample project and double click on the activity_fragment_example.xml file to load it into the Android Studio Designer tool. Switch from Design mode to Text mode by clicking on the text tab at the bottom of the Designer panel. Modify the XML to remove the default TextView object, any padding properties and to embed the two fragments as follows:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ebookfrenzy.fragmentexample.fragmentexample.FragmentExampleActivity">

    <fragment
        android:id="@+id/toolbar_fragment"
        android:name="com.ebookfrenzy.fragmentexample.fragmentexample.ToolbarFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        tools:layout="@layout/toolbar_fragment" />

    <fragment
        android:id="@+id/text_fragment"  
        android:name="com.ebookfrenzy.fragmentexample.fragmentexample.TextFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        tools:layout="@layout/text_fragment" />

</RelativeLayout>

Click on the Design tab to return to the graphical view and note that the fragments are now visible in the layout as demonstrated in Figure 23-4:


The user interface layout for an Android Studio fragment tutorial

Figure 23-4


Making the Toolbar Fragment Talk to the Activity

When the user touches the button in the toolbar fragment, the fragment class is going to need to get the text from the EditText view and the current value of the SeekBar and send them to the text fragment. As outlined in An Introduction to Using Fragments in Android Studio, fragments should not communicate with each other directly, instead using the activity in which they are embedded as an intermediary. The first step in this process is to make sure that the toolbar fragment responds to the button being clicked. We also need to implement some code to keep track of the value of the SeekBar view. For the purposes of this example, we will implement these listeners within the ToolbarFragment class. Select the ToolbarFragment.java file and modify it so that it reads as shown in the following listing:

package com.ebookfrenzy.fragmentexample.fragmentexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.app.Activity;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;

public class ToolbarFragment extends Fragment implements OnSeekBarChangeListener {

    private static int seekvalue = 10;
    private static EditText edittext;

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container, Bundle 
				savedInstanceState) {

        // Inflate the layout for this fragment
        View view =  inflater.inflate(R.layout.toolbar_fragment,
                container, false);

        edittext = (EditText) view.findViewById(R.id.editText1);
        final SeekBar seekbar =
                (SeekBar) view.findViewById(R.id.seekBar1);

        seekbar.setOnSeekBarChangeListener(this);

        final Button button =
                (Button) view.findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                buttonClicked(v);
            }
        });

        return view;
    }

    public void buttonClicked (View view) {

    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
                                  boolean fromUser) {
        seekvalue = progress;
    }

    @Override
    public void onStartTrackingTouch(SeekBar arg0) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar arg0) {

    }
}

Before moving on, we need to take some time to explain the above code changes. First, the class is declared as implementing the OnSeekBarChangeListener interface. This is because the user interface contains a SeekBar instance and the fragment needs to receive notifications when the user slides the bar to change the font size. Implementation of the OnSeekBarChangeListener interface requires that the onProgressChanged(), onStartTrackingTouch() and onStopTrackingTouch() methods be implemented. These methods have been implemented but only the onProgressChanged() method is actually required to perform a task, in this case storing the new value in a variable named seekvalue which has been declared at the start of the class. Also declared is a variable in which to store a reference to the EditText object.

The onCreateView() method has been modified to obtain references to the EditText, SeekBar and Button views in the layout. Once a reference to the button has been obtained it is used to set up an onClickListener on the button which is configured to call a method named buttonClicked() when a click event is detected. This method is also then implemented, though at this point it does not do anything.

The next phase of this process is to set up the listener that will allow the fragment to call the activity when the button is clicked. This follows the mechanism outlined in the previous chapter:

public class ToolbarFragment extends Fragment implements OnSeekBarChangeListener {
	
	  private static int seekvalue = 10;
	  private static EditText edittext;
		
	  ToolbarListener activityCallback;
		
	  public interface ToolbarListener {
	        public void onButtonClick(int position, String text);
	  }
	  
	  @Override
	  public void onAttach(Activity activity) {
	        super.onAttach(activity);
	        try {
	            activityCallback = (ToolbarListener) activity;
	        } catch (ClassCastException e) {
	            throw new ClassCastException(activity.toString()
	                    + " must implement ToolbarListener");
	        }
	    }

          @Override
	    public View onCreateView(LayoutInflater inflater, 
              ViewGroup container, Bundle savedInstanceState) {
	        // Inflate the layout for this fragment
	    	
	        View view =  inflater.inflate(R.layout.toolbar_fragment,
                      container, false);
	        
	        edittext = (EditText) view.findViewById(R.id.editText1);
	        final SeekBar seekbar = 
                 (SeekBar) view.findViewById(R.id.seekBar1);
	        
	        seekbar.setOnSeekBarChangeListener(this);
	        
	        final Button button = 
                 (Button) view.findViewById(R.id.button1);
	        button.setOnClickListener(new View.OnClickListener() {
	            public void onClick(View v) {
	                buttonClicked(v);
	            }
	        });
	        
	        return view;
	   }

	   public void buttonClicked (View view) {
		   activityCallback.onButtonClick(seekvalue, 
                      edittext.getText().toString());
	   }
.
.
.
}

The above implementation will result in a method named onButtonClick() belonging to the activity class being called when the button is clicked by the user. All that remains, therefore, is to declare that the activity class implements the newly created ToolbarListener interface and to implement the onButtonClick() method.

Since the Android Support Library is being used for fragment support in earlier Android versions, the activity also needs to be changed to subclass from FragmentActivity instead of ActionBarActivity. Bringing these requirements together results in the following modified FragmentExampleActivity.java file:

package com.ebookfrenzy.fragmentexample.fragmentexample;

import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class FragmentExampleActivity extends FragmentActivity implements ToolbarFragment.ToolbarListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_example);
    }

    public void onButtonClick(int fontsize, String text) {

    }
.
.
.
}

With the code changes as they currently stand, the toolbar fragment will detect when the button is clicked by the user and call a method on the activity passing through the content of the EditText field and the current setting of the SeekBar view. It is now the job of the activity to communicate with the Text Fragment and to pass along these values so that the fragment can update the TextView object accordingly.

Making the Activity Talk to the Text Fragment =

As outlined in An Introduction to Using Fragments in Android Studio, an activity can communicate with a fragment by obtaining a reference to the fragment class instance and then calling public methods on the object. As such, within the TextFragment class we will now implement a public method named changeTextProperties() which takes as arguments an integer for the font size and a string for the new text to be displayed. The method will then use these values to modify the TextView object. Within the Android Studio editing panel, locate and modify the TextFragment.java file to add this new method and to add code to the onCreateView() method to obtain the ID of the TextView object:

package com.ebookfrenzy.fragmentexample.fragmentexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class TextFragment extends Fragment {

    private static TextView textview;

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.text_fragment,
                container, false);

        textview = (TextView) view.findViewById(R.id.textView1);

        return view;
    }

    public void changeTextProperties(int fontsize, String text)
    {
        textview.setTextSize(fontsize);
        textview.setText(text);
    }

}

When the TextFragment fragment was placed in the layout of the activity, it was given an ID of text_fragment. Using this ID, it is now possible for the activity to obtain a reference to the fragment instance and call the changeTextProperties() method on the object. Edit the FragmentExampleActivity.java file and modify the onButtonClick() method as follows:

public void onButtonClick(int fontsize, String text) {

   TextFragment textFragment =
    (TextFragment)
      getSupportFragmentManager().findFragmentById(R.id.text_fragment);

   textFragment.changeTextProperties(fontsize, text);
}

Testing the Application

With the coding for this project now complete, the last remaining task is to run the application. When the application is launched, the main activity will start and will, in turn, create and display the two fragments. When the user touches the button in the toolbar fragment, the onButtonClick() method of the activity will be called by the toolbar fragment and passed the text from the EditText view and the current value of the SeekBar. The activity will then call the changeTextProperties() method of the second fragment, which will modify the TextView to reflect the new text and font size:


An Android Studio fragment example app running

Figure 23-5


Summary

The goal of this chapter has been to work through the creation of an example project intended specifically to demonstrate the steps involved in using fragments within an Android application. Topics covered included the use of the Android Support Library for compatibility with Android versions predating the introduction of fragments, the inclusion of fragments within an activity layout and the implementation of inter-fragment communication.