Tuesday, July 24, 2012

Fragments

,

Fragments Overview

Fragment components allow you to organize your application code so that it is easier to support different sized devices.
Fragments are components with their own lifecycle and their own user interface. They can be defined via layout files or via coding.
Fragments always run in the context of an Activity. If an Activity is stopped its Fragments will also be stopped; if an Activity is destroyed its Fragments will also get destroyed.
If a Fragment component is defined in an XML layout file, the android:name attribute points to the Fragments class.
The base class for Fragments is android.app.Fragment. For special purposes you can also use more special classes, like ListFragment or DialogFragment.
The onCreateView() method is called by Android once the Fragment should create its user interface. Here you can inflate an layout. The onStart() method is called once the Fragment gets visible.
Fragments can be dynamically added and removed from an Activity via Fragment transactions. This will add the action to the history stack of the Activity, i.e. this will allow to revert the Fragment changes in the Activity via the back button.

When to use Fragments

Fragments make it easy to re-use components in different layouts, e.g. you can build single-pane layouts for handsets (phones) and multi-pane layouts for tablets.
This is not limited to tablets; for example you can use Fragments also to support different layout for landscape and portrait orientation. But as tablets offer significantly more space you typically include more views into the layout and Fragments makes that easier.
The typical example is a list of items in an activity. On a tablet you see the details immediately on the same screen on the right hand side if you click on item. On a handset you jump to a new detail screen. The following discussion will assume that you have two Fragments (main and detail) but you can also have more. We will also have one main activity and one detailed activity. On a tablet the main activity contains both Fragments in its layout, on a handheld it only contains the main fragment.
To check for an fragment you can use the FragmentManager.

DetailFragment fragment = (DetailFragment) getFragmentManager().
  findFragmentById(R.id.detail_frag);
if (fragment==null || ! fragment.isInLayout()) {
 // start new Activity
 }
else {
 fragment.update(...);
} 

To create different layouts with Fragments you can:
  • Use one activity, which displays two Fragments for tablets and only one on handsets devices. In this case you would switch the Fragments in the activity whenever necessary. This requires that the fragment is not declared in the layout file as such Fragments cannot be removed during runtime. It also requires an update of the action bar if the action bar status depends on the fragment.
  • Use separate activities to host each fragment on a handset. For example, when the tablet UI uses two Fragments in an activity, use the same activity for handsets, but supply an alternative layout that includes just one fragment. When you need to switch Fragments, start another activity that hosts the other fragment.

The second approach is the most flexible and in general preferable way of using Fragments. In this case the main activity checks if the detail fragment is available in the layout. If the detailed fragment is there, the main activity tells the fragment that is should update itself. If the detail fragment is not available the main activity starts the detailed activity.
It is good practice that Fragments do not manipulate each other. For this purpose a Fragment typically implements an interface to get new data from its host Activity.

# Fragments Tutorial

Overview

The following tutorial demonstrates how to use Fragments. The entry Activity (called MainActivity of our application) will use different layouts for portrait and for landscape mode.
In portrait mode MainActivity will show one Fragment with a list of names. If the user touches an item in the list, a second Activity called DetailActivity will start and show the selected text.
In landscape mode MainActivity will show two Fragments. The first is again the Fragments which shows the list of names. The second Fragment shows the text of the current selected item. This is similar to the portrait mode, but the whole information will be shown on one screen.

Create project

Create a new project de.vogella.android.fragments with an Activity called MainActivity.

Create layouts for portrait mode

Create or change the following layout files in the "res/layout/" folder.
First create the following file called "details.xml". This layout will be used by the DetailFragment.

<?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" >

    <TextView
        android:id="@+id/detailsText"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal|center_vertical"
        android:layout_marginTop="20dip"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="30dip" />

</LinearLayout> 

Change the existing main.xml file. This layout will be used by MainActivity in landscape mode and shows two Fragments.

<?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="horizontal" >

    <fragment
        android:id="@+id/listFragment"
        android:layout_width="150dip"
        android:layout_height="match_parent"
        android:layout_marginTop="?android:attr/actionBarSize"
        class="de.vogella.android.fragments.ListFragment" ></fragment>

    <fragment
        android:id="@+id/detailFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="de.vogella.android.fragments.DetailFragment" >
        <!-- Preview: layout=@layout/details -->
    </fragment>

</LinearLayout> 

Create Fragment classes

Create now the Fragment classes. Create the ListFragment class.

package de.vogella.android.fragments;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class ListFragment extends android.app.ListFragment {
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

 }

 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);
  String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
    "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
    "Linux", "OS/2" };
  ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
    android.R.layout.simple_list_item_1, values);
  setListAdapter(adapter);
 }

 @Override
 public void onListItemClick(ListView l, View v, int position, long id) {
  String item = (String) getListAdapter().getItem(position);
  DetailFragment fragment = (DetailFragment) getFragmentManager()
    .findFragmentById(R.id.detailFragment);
  if (fragment != null && fragment.isInLayout()) {
   fragment.setText(item);
  } else {
   Intent intent = new Intent(getActivity().getApplicationContext(),
     DetailActivity.class);
   intent.putExtra("value", item);
   startActivity(intent);

  }

 }
} 

Create the DetailFragment class.

package de.vogella.android.fragments;

import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class DetailFragment extends Fragment {
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.e("Test", "hello");
 }

 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);

 }

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

 public void setText(String item) {
  TextView view = (TextView) getView().findViewById(R.id.detailsText);
  view.setText(item);
 }
} 

Create layouts for landscape mode

We want that Android uses a different main.xml file in portrait model then in landscape mode.
For this reason create the "res/layout-port" folder.
In portrait mode Android will check the "layout-port" folder for fitting layout files. Only if we would not have a main.xml file in "layout-port", Android would check the "layout" folder.
Therefore create the following main.xml layout file in "res/layout-port".

<?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="horizontal" >

    <fragment
        android:id="@+id/listFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="?android:attr/actionBarSize"
        class="de.vogella.android.fragments.ListFragment" />
</LinearLayout> 

Also create the "details_activity_layout.xml" layout file. This layout will be used in the DetailActivity which is only used in portrait mode. Please note that we could have create this file also in the "layout" folder, but as it is only used in portrait mode it is best practise to place it into this folder.

<?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/detailFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="de.vogella.android.fragments.DetailFragment" />

</LinearLayout> 

Activities

Create a new Activity called DetailActivity with the following class.

package de.vogella.android.fragments;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.widget.TextView;

public class DetailActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Need to check if Activity has been switched to landscape mode
  // If yes, finished and go back to the start Activity
  if (getResources().getConfiguration().orientation == 
    Configuration.ORIENTATION_LANDSCAPE) {
   finish();
   return;
  }

  setContentView(R.layout.details_activity_layout);
  Bundle extras = getIntent().getExtras();
  if (extras != null) {
   String s = extras.getString("value");
   TextView view = (TextView) findViewById(R.id.detailsText);
   view.setText(s);
  }
 }
} 

MainActivity will remain unmodified.

package de.vogella.android.fragments;

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

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

Run

Run your example. If you run the application in portrait mode you should see only one Fragment. Use Ctrl+F11 to switch the orientation. In horizontal mode you should see two Fragments. If you select an item in portrait mode a new Activity should get started with the selected item. In horizontal mode your second Fragment should display the select item.


ActionBar navigation with Fragments

Fragments can also be used in combination with the ActionBar for navigation. For this your main Activity needs to implement a TabListener which is responsible for moving between the tabs.
The ActionBar allows to add tabs to it via the newTab() method. The following code shows such an Activity. It uses two Fragments, called DetailFragment and ImageFragment. At this point you should be able to create these two Fragments yourself.

package de.vogella.android.fragment;

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.ActionBar.TabListener;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;

public class MainActivity extends Activity {
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  // setup action bar for tabs
  ActionBar actionBar = getActionBar();
  actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
  actionBar.setDisplayShowTitleEnabled(false);

  Tab tab = actionBar
    .newTab()
    .setText("First tab")
    .setTabListener(new MyTabListener<DetailFragment>(this, "artist",
        DetailFragment.class));
  actionBar.addTab(tab);

  tab = actionBar
    .newTab()
    .setText("Second Tab")
    .setTabListener(new MyTabListener<ImageFragment>(this, "album",
        ImageFragment.class));
  actionBar.addTab(tab);

 }

 public static class MyTabListener<T extends Fragment> implements
   TabListener {
  private Fragment mFragment;
  private final Activity mActivity;
  private final String mTag;
  private final Class<T> mClass;

  
/** * Constructor used each time a new tab is created. * * @param activity * The host Activity, used to instantiate the fragment * @param tag * The identifier tag for the fragment * @param clz * The fragment's Class, used to instantiate the fragment */
public MyTabListener(Activity activity, String tag, Class<T> clz) { mActivity = activity; mTag = tag; mClass = clz; } /* The following are each of the ActionBar.TabListener callbacks */ public void onTabSelected(Tab tab, FragmentTransaction ft) { // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity mFragment = Fragment.instantiate(mActivity, mClass.getName()); ft.add(android.R.id.content, mFragment, mTag); } else { // If it exists, simply attach it in order to show it ft.setCustomAnimations(android.R.animator.fade_in, R.animator.animationtest); ft.attach(mFragment); } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { ft.setCustomAnimations(android.R.animator.fade_in, R.animator.test); ft.detach(mFragment); } } public void onTabReselected(Tab tab, FragmentTransaction ft) { } } }

 (vogella.com)

1 comments:

  • July 24, 2014 at 1:08 AM
    Unknown says:

    cool

Post a Comment

 

Android Development Tutorials Copyright © 2011 -- Template created by O Pregador -- Powered by Blogger Templates