IS 389 ANDROID FRAGMENT LAB EKEDAHL ABSTRACT In this lab, you will work with Android Fragments. The application that you create for this lab will not do much, if any processing. Rather, it shows you the event lifecycle of adding and replacing fragments and how fragments are displayed within their container. For this lab, I have posted the document electronically. I suggest that you load the Microsoft Word document. That way you can copy and paste my code into your lab. There is a significant amount of code in this program. For each code segment that you develop, there is a discussion of the code you have written. In some cases, you will modify the code on your own. HANDS-ON EXERCISE: Creating the Layouts For this application, you will need to create 5 layouts. Two of these layouts are used to render the activity while in portrait and landscape mode. The following list describes the activities. The first layout is named activity_main.xml and appears in the layout folder. This layout will be rendered while the device is in portrait mode. This activity contains two buttons and two fragments that will be rendered vertically The second layout is named activity_main.xml and appears in the layout-land folder. This layout will be rendered while the device is in landscape mode. The activity contains two buttons and two fragments that will be rendered horizontally. There are three fragments. Each works the same way and will be rendered inside one of the parent activities. The first fragment named fragment_one_layout.xml contains one TextView used to illustrate the activity and fragment events. The second fragment named fragment_two_layout.xml contains one TextView used to illustrate the activity and fragment events. The third fragment named fragment_three_layout.xml contains one TextView used to illustrate the activity and fragment events. The following excerpt from the Package Explorer shows the files and their folder location. 1. Create a new Android Application project as you have been doing. I suggest using version 19 of the API’s. Create the application with a blank activity. Use the default name for the main activity. So that all of the names match, I suggest that you name the application (package) fragmentdemo1. 2. Edit the file named activity_main.xml in the res/layout folder. Enter the following code: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/buttonOne" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/replace_fragment" /> <Button android:id="@+id/buttonTwo" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/add_fragment" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/fragment_one_holder" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="#6495ED" /> <FrameLayout android:id="@+id/fragment_two_holder" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="#40E0D0" /> </LinearLayout> </LinearLayout> Note the following about the preceding statements. The activity uses an outer <LinearLayout> organized vertically (android:orientation="vertical"). There are two nested <LinearLayout> elements. The first contains two buttons appearing horizontally. The second contains two <FrameLayout>s organized vertically. These <FrameLayout> elements will render one of the three fragments: Modify the strings.xml file appearing in the values folder adding the following lines (shown in bold). <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">FragmentDemo1</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="add_fragment">Add Fragment</string> <string name="replace_fragment">Replace Fragment</string> </resources> Next you will create a second activity. This activity will be displayed while the device is oriented in landscape mode. 1. Create a folder named layout-land in the res folder. Remember that this name is magical based on the default configuration. This activity will be rendered when the device is in landscape mode. Create a file named activity_main.xml in this folder and copy the following code into the file. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/buttonOne" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/replace_fragment" /> <Button android:id="@+id/buttonTwo" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/add_fragment" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <FrameLayout android:id="@+id/fragment_one_holder" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#6495ED" /> <FrameLayout android:id="@+id/fragment_two_holder" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#40E0D0" /> </LinearLayout> </LinearLayout> HANDS-ON ACTIVITY: Creating the Fragment Layouts Next you will create the layout for the three fragment. Create a fragment named fragment_one_layout.xml in the layout folder. Enter the following code. <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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.fragment.demo.LauncherActivity$PlaceholderFragment" android:background="@android:color/holo_red_light"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fragment One" /> </RelativeLayout> Create a fragment named fragment_two_layout.xml in the layout folder. Enter the following code. <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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.fragment.demo.LauncherActivity$PlaceholderFragment" android:background="@android:color/holo_green_light" > <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fragment Two" /> </RelativeLayout> Create a fragment named fragment_three_layout.xml in the layout folder. Enter the following code. <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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.fragment.demo.LauncherActivity$PlaceholderFragment" android:background="@android:color/holo_blue_light" > <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fragment Three" /> </RelativeLayout> HANDS-ON ACTIVITY: Creating the Activity Code Next, you will begin creating the code for the main activity The package com.example.fragmentdemo1; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import com.example.fragmentdemo1.R; import com.example.fragmentdemo1.FragmentOne; import com.example.fragmentdemo1.FragmentTwo; import com.example.fragmentdemo1.FragmentThree; public class MainActivity extends Activity { // Create references to the two buttons. private Button replaceFragmentButton, addFragmentButton; // We have three fragments that will be displayed in position 1 //or position 2 // private static int FRAGMENT_ONE_POSITION = 0; private static int FRAGMENT_TWO_POSITION = 0; // Override the onCreate method as usual. @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Get a reference to the two buttons. replaceFragmentButton = (Button)findViewById(R.id.buttonOne); addFragmentButton = (Button)findViewById(R.id.buttonTwo); // Create the listener for the replace button by overriding the // onClick handler. replaceFragmentButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Create the fragment transaction. FragmentTransaction fragmentOneTransaction = getFragmentManager().beginTransaction(); // Replace the current fragment with the new fragment // incrementing // the fragment each time or resetting the fragment // position to 0. switch (FRAGMENT_ONE_POSITION) { case 0: // Increment the fragment position. FRAGMENT_ONE_POSITION++; // Replace the fragment. fragmentOneTransaction.replace( R.id.fragment_one_holder, new FragmentOne(MainActivity.this)); break; case 1: FRAGMENT_ONE_POSITION++; fragmentOneTransaction.replace( R.id.fragment_one_holder, new FragmentTwo(MainActivity.this)); break; case 2: // Reset the fragment position to zero. FRAGMENT_ONE_POSITION = 0; fragmentOneTransaction.replace( R.id.fragment_one_holder, new FragmentThree(MainActivity.this)); break; default: break; } // Commit the fragment transaction. fragmentOneTransaction.commit(); } }); // Create the listener for the add button by overriding the // onClick handler. This code is nearly identical to the code for // the replace fragment button. addFragmentButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { FragmentTransaction fragmentTwoTransaction = getFragmentManager().beginTransaction(); switch (FRAGMENT_TWO_POSITION) { case 0: FRAGMENT_TWO_POSITION++; fragmentTwoTransaction.add( R.id.fragment_two_holder, new FragmentOne(MainActivity.this)); break; case 1: FRAGMENT_TWO_POSITION++; fragmentTwoTransaction.add( R.id.fragment_two_holder, new FragmentTwo(MainActivity.this)); break; case 2: FRAGMENT_TWO_POSITION = 0; fragmentTwoTransaction.add( R.id.fragment_two_holder, new FragmentThree(MainActivity.this)); break; default: break; } /** * Adding current fragment to back stack which can * be popped back when we need. * We can add name to this back stack * and we can access this fragment by name later. */ fragmentTwoTransaction.addToBackStack(null); fragmentTwoTransaction.commit(); } }); } /************************************** * Overriding onBackPressed button and pop backStack fragment. * **************************************/ @Override public void onBackPressed() { FragmentManager fm = getFragmentManager(); if (fm.getBackStackEntryCount() > 0) { fm.popBackStack(); } else { super.onBackPressed(); } } } The following list identifies significant points about the preceding code. The two variables named FRAGMENT_ONE_POSITION and FRAGMENT_TWO_POSITION indicate whether the first, second, or third fragment in one of the two frames. An onClick listener is set up for each of the two buttons. The first replaces fragments, the second adds them. The purpose of this is to demonstrate the operation of the back stack. You will see this behavior at the end of the exercise, when you run the application. First, the fragment transaction is created. The switch statement tests the current position, which is initially 0. Each of the case statements replace the current fragment with the new one. At the end of the procedure, the transaction is committed. HANDS-ON ACTIVITY: Creating the Fragment Code In this set of hands-on steps, you will create the code for the three fragments. The fragment code is nearly identical for each fragment. Enter the following code in the FragmentOne.java file. package com.example.fragmentdemo1; import import import import import import import import import android.app.Activity; android.app.Fragment; android.content.Context; android.os.Bundle; android.view.LayoutInflater; android.view.View; android.view.ViewGroup; android.widget.TextView; android.widget.Toast; import com.example.fragmentdemo1.R; import com.example.fragmentdemo1.MainActivity; public class FragmentOne extends Fragment{ StringBuilder sb = new StringBuilder(); Context _context; public FragmentOne() { } public FragmentOne(MainActivity launcherActivity) { _context = launcherActivity; } @Override public void onAttach(Activity activity) { sb.append("\n:::::::1:::::::"); sb.append("\n onAttach\n"); super.onAttach(activity); } @Override public void onCreate(Bundle savedInstanceState) { sb.append("onCreate\n"); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { sb.append("onCreateView\n"); View rootView = inflater.inflate(R.layout.fragment_one_layout, container, false); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { sb.append("onViewCreated\n"); TextView tv = (TextView)view.findViewById(R.id.textView); tv.setText(FragmentOne.class.getSimpleName()+sb); super.onViewCreated(view, savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { sb.append("onActivityCreated\n"); super.onActivityCreated(savedInstanceState); } @Override public void onStart() { sb.append("onStart\n"); super.onStart(); } @Override public void onResume() { sb.append("onResume\n"); super.onResume(); } @Override public void onPause() { sb.append("onPause\n"); super.onPause(); } @Override public void onStop() { sb.append("onStop\n"); super.onStop(); } @Override public void onDestroyView() { sb.append("onDestroyView\n"); super.onDestroyView(); } @Override public void onDestroy() { sb.append("onDestroy\n"); super.onDestroy(); } @Override public void onDetach() { if(null!= sb){ sb.append("onDetach\n"); if(_context != null){ Toast.makeText(_context, FragmentOne.class.getSimpleName()+" is Detach", Toast.LENGTH_LONG).show(); } } super.onDetach(); } } The following list describes the above code: Enter the following code in the FragmentTwo.java file. package com.example.fragmentdemo1; import import import import import import import import import android.app.Activity; android.app.Fragment; android.content.Context; android.os.Bundle; android.view.LayoutInflater; android.view.View; android.view.ViewGroup; android.widget.TextView; android.widget.Toast; import com.example.fragmentdemo1.R; import com.example.fragmentdemo1.MainActivity; public class FragmentTwo extends Fragment{ StringBuilder sb = new StringBuilder(); Context _context; public FragmentTwo() { } public FragmentTwo(MainActivity launcherActivity) { _context = launcherActivity; } @Override public void onAttach(Activity activity) { sb.append("\n:::::::2:::::::"); sb.append("\n onAttach\n"); super.onAttach(activity); } @Override public void onCreate(Bundle savedInstanceState) { sb.append("onCreate\n"); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { sb.append("onCreateView\n"); View rootView = inflater.inflate(R.layout.fragment_two_layout, container, false); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { sb.append("onViewCreated\n"); TextView tv = (TextView)view.findViewById(R.id.textView); tv.setText(FragmentTwo.class.getSimpleName()+sb); super.onViewCreated(view, savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { sb.append("onActivityCreated\n"); super.onActivityCreated(savedInstanceState); } @Override public void onStart() { sb.append("onStart\n"); super.onStart(); } @Override public void onResume() { sb.append("onResume\n"); super.onResume(); } @Override public void onPause() { sb.append("onPause\n"); super.onPause(); } @Override public void onStop() { sb.append("onStop\n"); super.onStop(); } @Override public void onDestroyView() { sb.append("onDestroyView\n"); super.onDestroyView(); } @Override public void onDestroy() { sb.append("onDestroy\n"); super.onDestroy(); } @Override public void onDetach() { if(null!= sb){ sb.append("onDetach\n"); if(_context != null){ Toast.makeText(_context, FragmentTwo.class.getSimpleName() + " is Detach", Toast.LENGTH_LONG).show(); } } super.onDetach(); } } Enter the following code in the FragmentThree.java file. package com.example.fragmentdemo1; import import import import import import import import import android.app.Activity; android.app.Fragment; android.content.Context; android.os.Bundle; android.view.LayoutInflater; android.view.View; android.view.ViewGroup; android.widget.TextView; android.widget.Toast; import com.example.fragmentdemo1.R; import com.example.fragmentdemo1.MainActivity; public class FragmentThree extends Fragment{ StringBuilder sb = new StringBuilder(); Context _context; public FragmentThree(MainActivity launcherActivity) { _context = launcherActivity; } @Override public void onAttach(Activity activity) { sb.append("\n:::::::3:::::::"); sb.append("\n onAttach\n"); super.onAttach(activity); } @Override public void onCreate(Bundle savedInstanceState) { sb.append("onCreate\n"); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { sb.append("onCreateView\n"); View rootView = inflater.inflate(R.layout.fragment_three_layout, container, false); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { sb.append("onViewCreated\n"); TextView tv = (TextView)view.findViewById(R.id.textView); tv.setText(FragmentThree.class.getSimpleName()+sb); super.onViewCreated(view, savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { sb.append("onActivityCreated\n"); super.onActivityCreated(savedInstanceState); } @Override public void onStart() { sb.append("onStart\n"); super.onStart(); } @Override public void onResume() { sb.append("onResume\n"); super.onResume(); } @Override public void onPause() { sb.append("onPause\n"); super.onPause(); } @Override public void onStop() { sb.append("onStop\n"); super.onStop(); } @Override public void onDestroyView() { sb.append("onDestroyView\n"); super.onDestroyView(); } @Override public void onDestroy() { sb.append("onDestroy\n"); super.onDestroy(); } @Override public void onDetach() { if(null!= sb){ sb.append("onDetach\n"); if(_context != null){ Toast.makeText(_context, FragmentThree.class.getSimpleName() +" is Detach", Toast.LENGTH_LONG).show(); } } super.onDetach(); } }