Wednesday, May 16, 2012

21. Fragments Tutorial

21.1. 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.

21.2. Create project

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

21.3. 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="fill_parent"
    android:layout_height="fill_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>
   

21.4. 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);
 }
}

   

21.5. 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="fill_parent"
    android:layout_height="fill_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>
   

21.6. 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); } }

21.7. 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.

1 comment:

  1. It show exception in DetailFragment fragment = (DetailFragment) getFragmentManager()
    .findFragmentById(R.id.detailFragment);

    Cannot cast from Fragment to DetailFragment.

    Please Help Me

    ReplyDelete