Creating and managing fragments

We are going to learn how to create and manage fragments with a sample Android application. This application is going to list book names. When a book name is clicked, the author of the book will be displayed. This application will be designed for small and large screen devices, this way we will see how to use fragments for different screen sizes. The following is the screenshot of this application for small screens. As you can see in this screenshot, the left hand side of the screen has the list of books and when a book is clicked, the right hand side of the screen will be displayed which shows the author of the clicked book:

Creating and managing fragments

We will firstly implement these screens, and then we will design this application for large screens.

In this application, we have two activities, one for the first screen and one for the second screen. Each activity consists of one fragment. The following diagram shows the structure of this application:

Creating and managing fragments

The XML code for the layout of Fragment B is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/textViewAuthor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

As you can see in this code, it has a LinearLayout layout with a TextView component. TextView is for displaying the author of the book. We don't have a layout for Fragment A, because it is a ListFragment property which includes the ListView component.

Now we need two classes that extend the Fragment classes for each fragment. The following is the class for Fragment A:

package com.chapter5;

import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;

public class Chapter5_1FragmentA extends ListFragment implements  
OnItemClickListener {


  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
             super.onActivityCreated(savedInstanceState);
             //initialize the adapter and set on click events of items
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
        android.R.layout.simple_list_item_1, Book.BOOK_NAMES);
    this.setListAdapter(adapter);
    getListView().setOnItemClickListener(this);
  }
  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
  {
    //Start a new Activity in order to display author name
    String author = Book.AUTHOR_NAMES[position];
    Intent intent = new Intent(getActivity().getApplicationContext(),
    Chapter5_1Activity_B.class);
    intent.putExtra("author", author);
    startActivity(intent);
  }
}

As you can see, the Chapter5_1FragmentA class extends ListFragment, because we are listing the books in this screen. It is similar to ListActivity and this class has a ListView view. In the onActivityCreated method we set the ListAdapter property of the ListFragment. The source for the adapter is a class that contains the string arrays of book names and authors as shown in the following code block:

package com.chapter5;

public class Book {
  public static final String[] BOOK_NAMES = { "Book Name 1", "Book Name 2", "Book Name 3", "Book Name 4", "Book Name 5", "Book Name 6", "Book Name 7", "Book Name 8" };
  public static final String[] AUTHOR_NAMES = { "Author of Book 1", "Author of Book 2", "Author of Book 3", "Author of Book 4", "Author of Book 5", "Author of Book 6", "Author of Book 7", "Author of Book 8" };
}

After initializing ListAdapter, we set the OnItemClickListener event of the ListView view. This event is called when a ListView item is clicked. When an item is clicked, the onItemClick method is called. In this method, a new activity is started with the author of the book. As you can see in the code, we reach the owner activity of the fragment with the getActivity() method. We could receive the ApplicationContext with the getActivity() method. Remember that the OnCreateView method is called before OnActivityCreated, and because of that we initialized ListAdapter and ListView in the OnActivityCreated method, because we need the user interface components to be created before we initialize them and they are created in OnCreateView. We don't need to override the OnCreateView method of ListFragment, because it returns a ListView. You can override the OnCreateView method if you want to use a customized ListView.

The following is the class for Fragment B:

package com.chapter5;

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

public class Chapter5_1FragmentB extends Fragment {

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

    View view = inflater.inflate(R.layout.fragment_b, container, false);
    return view;
  }
}

As you can see from this code, if a fragment has a user interface, this method should be overridden and should return a view. In our sample application, we are returning a view inflated with the XML layout that we previously implemented.

Now we need two activity classes that host these fragments. The following is the Activity class of Activity A that hosts Fragment A:

package com.chapter5;

import android.app.Activity;
import android.os.Bundle;

public class Chapter5_1Activity_A extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
    }
}

It is a simple Activity class that just sets the content view with a layout. The XML layout code of the Activity A class is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <fragment
        android:id="@+id/fragment_a"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.chapter5.Chapter5_1FragmentA" />

</LinearLayout>

As you can see from this code, we specified Fragment A with the class property com.chapter5.Chapter5_1FragmentA. Furthermore, we specified the id property. Fragments should have either an id or a tag property as an identifier because Android needs that in restoring the fragment when the activity is restarted.

The Activity class for Activity B that hosts Fragment B is as follows:

package com.chapter5;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class Chapter5_1Activity_B extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
        
        Bundle extras = getIntent().getExtras();
    if (extras != null) {
      String s = extras.getString("author");
      TextView view = (TextView) findViewById(R.id.textViewAuthor);
      view.setText(s);
    }
    }
}

It is a simple Activity class that just sets the content view with a layout. The XML layout code of Activity B is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
     
    <fragment
        android:id="@+id/fragment_b"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.chapter5.Chapter5_1FragmentB" />
    
</LinearLayout>

As you can see from this code, we specified Fragment B with the class property com.chapter5.Chapter5_1FragmentB.

Programmatically adding a fragment

In our previous sample application, we added a fragment to an activity layout in XML layout code. You can also add a fragment to an activity programmatically. The following is the programmatically added fragment version of our previous sample application and XML layout code of the activity:

package com.chapter5;

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;

public class Chapter5_1Activity_A extends Activity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_a);
    addFragment();
  }

  public void addFragment() {
    FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    Chapter5_1FragmentA fragment = new Chapter5_1FragmentA();
    fragmentTransaction.add(R.id.layout_activity_a, fragment);
    fragmentTransaction.commit();
  }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_activity_a"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

</LinearLayout>

As you can see from this XML code, we removed the Fragment tags because we are adding Fragment A programmatically. As you can see in the Chapter5_1Activity_A class, we added a method called addFragment(). We used the FragmentTransaction class in order to add Fragment A. The FragmentTransaction class is used for operations such as adding fragments, removing fragments, attaching fragments to the UI, and so on. As you can see in the addMethod() method, you can get an instance of FragmentTransaction with FragmentManager using the beginTransaction() method. Finally we have to call the commit() method for the changes to be applied.

FragmentManager is used for managing fragments. As you can see in the code, you can get an instance of FragmentManager by the getFragmentManager() method. FragmentManager allows you to begin a transaction by the beginTransaction() method, get a fragment in activity by the findFragmentById() or findFragmentbyTag() methods, and pop a fragment off the back stack by the popBackStack() method.

Event sharing with activity

In our example, we started an activity in the ListFragment class' onItemClick method . We can establish the same operation by creating a callback interface in ListFragment and make the Activity class implement that callback. By this way the Fragment class will notify the owner Activity class. When the owner Activity class is notified, it can share the notification by other fragments. This way, fragments can share an event and communicate. We can go about this operation using the following steps:

  1. We create the callback interface in the Chapter5_1FragmentA class:
    public interface OnBookSelectedListener 
      {
        public void onBookSelected(int bookIndex);
      }
  2. We create an instance of OnBookSelectedListener and assign the owner activity to that instance in the Chapter5_1FragmentA class:
    OnBookSelectedListener mListener;
    @Override
      public void onAttach(Activity activity) {
        super.onAttach(activity);
        mListener = (OnBookSelectedListener) activity;
      }

    As you can see from this code, the owner activity class of Chapter5_1FragmentA should implement the onBookSelectedListener instance or there will be a class cast exception.

  3. We make the Chapter5_1Activity_A class implement the onBookSelectedListener interface:
    public class Chapter5_1Activity_A extends Activity implements 
    OnBookSelectedListener {
    //some code here
            @Override
            public void onBookSelected(int bookIndex) {
    
              String author = Book.AUTHOR_NAMES[bookIndex];
              Intent intent = new Intent(this,Chapter5_1Activity_B.class);
              intent.putExtra("author", author);
              startActivity(intent);
           } 
    //some more code here
    }

    As you can see from this code, Chapter5_1Activity_A receives a selected book index in the event callback and starts the activity with author data.

  4. We call the onBookSelected method in the onItemClick method of the Chapter5_1FragmentA class:
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    
      mListener.onBookSelected(position);
    }

In this way, we made the activity and fragment share an event callback.

Using multiple fragments in an activity

Our sample book listing application is designed for small screens. When you execute this application on a larger screen, it will look bad. We have to use the space efficiently in larger screen sizes. In order to achieve this, we have to create a new layout for large screens. The new layout is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_small_a"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

    <fragment
        android:id="@+id/fragment_a"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        class="com.chapter5.Chapter5_1FragmentA" 
        android:layout_weight="1"/>
    <fragment
        android:id="@+id/fragment_b"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        class="com.chapter5.Chapter5_1FragmentB" 
        android:layout_weight="1"/>

</LinearLayout>

As you can see from this code, we put two fragments in a horizontal LinearLayout layout. In the previous sample application, there was one fragment in each activity, but in this activity there are two fragments in order to use the space efficiently. By setting the layout_weight property to 1, we make the fragments consume equal spaces on the screen.

We have to put this new layout XML file under a folder named layout-xlarge-land under the res folder. In this way, the Android uses this layout file when the device screen is large and in landscape mode. Android decides which layout file to use on runtime according to layout folder names. layout is the default folder name for Android. If Android can't find a suitable layout folder for a device screen size and mode, it uses the layout in the layout folder. Some of the common qualifiers for layout are as follows:

  • small for small screen sizes
  • normal for normal screen sizes
  • large for large screen sizes
  • xlarge for extra large screen sizes
  • land for landscape orientation
  • port for portrait orientation

However, this layout is not enough to make our sample function correctly on large screens. To make the new layout function correctly, we have to change how the fragments are managed. Update the onBookSelected property in Chapter5_1Activity_A as follows:

  @Override
  public void onBookSelected(int bookIndex) {

    FragmentManager fragmentManager = getFragmentManager();
    Fragment fragment_b = fragmentManager.findFragmentById(R.id.fragment_b);
    String author = Book.AUTHOR_NAMES[bookIndex];
    if(fragment_b == null)
    {
      Intent intent = new Intent(this,
          Chapter5_1Activity_B.class);
      intent.putExtra("author", author);
      startActivity(intent);
    }
    else
    {
      TextView textViewAuthor = (TextView)fragment_b.getView().findViewById(R.id.textViewAuthor);
      textViewAuthor.setText(author);
    }
  }

As you can see from this code, we get the Fragment B class by using FragmentManager. If the fragment_b is not null, we understand that this activity contains Fragment B, and the device has a large screen, because Fragment B is used in Activity A only when the screen is large and in landscape mode. Then using fragment_b, we get the textViewAuthor TextView component and update its text with the chosen book's author name. On the right of the screen we see the author name of the chosen book.

If fragment_b is null, we understand that the device has a small screen, and we start a new activity using Intent.

In the AndroidManifest.xml file, we have to set the minimum SDK version to API Level 14, because fragments have been available for small screens since API Level 14. The AndroidManifest.xml file should look like the following code block:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.chapter5"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="14" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".Chapter5_1Activity_A"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".Chapter5_1Activity_B"/>
    </application>

</manifest>

Our sample application will look like the following on a large screen:

Using multiple fragments in an activity
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.94.202.151