Home » Android » Fragment Transaction and Lifecycle

Fragment Transaction and Lifecycle

Welcome to the second module of this series. In this module we will learn about Fragment Transaction and Lifecycle, how to add, remove and replace fragment, how to attach and detach fragment and at last we will create an example to show the Fragment lifecycle and how it Fragment is associated to its activity.

Below are the topics that we will cover in this module:

  • What is Fragment Transaction?
  • Add, Remove and Replace
  • Attach and Detach
  • Example of android Fragment Lifecycle

 

What is Fragment Transaction?

Like any other transaction, fragment transaction is the term used to add, remove and replace fragments. Each change that we commit to the activity is called as transaction and FragmentManager class is responsible to handle these transactions. We can save each transaction to a backstack by calling addToBackStack() method before committing the change, it allows user to navigate backward.
The main advantage of Fragment transaction is, fragments can be managed dynamically which means fragments can be added, removed or replaced at runtime by using add(), remove() and replace() methods respectively. Then, to apply the transactions to the activity, we must call commit().

 

Add, Remove and Replace

In this example we will see how to Add, remove and replace fragments.
Let’s create an activity which will have three buttons: Add, Remove, Replace and one LinearLayout to display Fragment transaction.

fragment_trasaction_1.xml

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

<LinearLayout
android:id="@+id/id_addRemoveReplace"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" >

<Button
android:id="@+id/id_addFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ADD" />

<Button
android:id="@+id/id_RemoveFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="REMOVE" />

<Button
android:id="@+id/id_ReplaceFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="REPLACE" />
</LinearLayout>

</LinearLayout>

Now create FragmenTransactionActivity.java class, in which we will write a logic to add, remove or replace the fragment

FragmenTransactionActivity.java

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.codeureka.fragmentsexample.R;

public class FragmenTransactionActivity extends Activity implements OnClickListener {

  final String FRAGMENT_TAG="fragment_tag";

  Button btn_Add,btn_Remove,btn_Replace;

@Override
protected void onCreate(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onCreate(savedInstanceState);
   setContentView(R.layout.fragment_trasaction_1);
   btn_Add = (Button)findViewById(R.id.id_addFrag);
   btn_Remove = (Button)findViewById(R.id.id_RemoveFrag);
   btn_Replace = (Button)findViewById(R.id.id_ReplaceFrag);

   btn_Add.setOnClickListener(this);
   btn_Remove.setOnClickListener(this);
   btn_Replace.setOnClickListener(this);
}

@Override
public void onClick(View v) {
   switch (v.getId()) {
     case R.id.id_addFrag:
        AddFragment();
      break;
      case R.id.id_RemoveFrag:
        RemoveFragment();
      break;
      case R.id.id_ReplaceFrag:
        ReplaceFragment();
      break;

      default:
      break;
   }
  }

private void ReplaceFragment() {
   // TODO Auto-generated method stub
   Fragment frag1 = new Fragment_1();

   FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
   fragTransaction.replace(R.id.id_addRemoveReplace, frag1, FRAGMENT_TAG);
   fragTransaction.addToBackStack("ReplaceFragment");
   fragTransaction.commit();
}

private void RemoveFragment() {
   // TODO Auto-generated method stub
   Fragment oldFrag = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
   Fragment_2 frag2 = new Fragment_2();

   FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
   if(oldFrag != null)
      fragTransaction.remove(oldFrag);
      fragTransaction.add(R.id.id_addRemoveReplace, frag2, FRAGMENT_TAG);
      fragTransaction.addToBackStack("RemoveFragment");
      fragTransaction.commit();
}

private void AddFragment() {
   // TODO Auto-generated method stub
   Fragment frag1 = new Fragment_1();
   FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
   Fragment oldFrag = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
   if(oldFrag == null){
      fragTransaction.add(R.id.id_addRemoveReplace, frag1, FRAGMENT_TAG);
      fragTransaction.addToBackStack("AddFragment");
      fragTransaction.commit();
   }
  }
}

Now create two more classes: Fragment_1.java and Fragment_2.java, Both classes are similar.

Fragment_1.java

public class Fragment_1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   View view = inflater.inflate(R.layout.fragment_1, container,false);
   return view;
  }
}

fragment_1.xml

<?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/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:text="Large This is fragment 1"
android:textColor="#0f0"
android:textSize="25dp"
android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Above I have created a xml file with three buttons and one linear layout which will be the container for dynamically changing fragments.
Then I am passing the xml to setContentView() method of FragmenTransactionActivity.java. On each button we are calling different transaction method, you must have noticed that each method is almost same with little bit of variance in code.

Below are the steps to begin the transaction and add it to stack.

1. Create an instance of Fragment that we want to change dynamically.
     Fragment frag1 = new Fragment_1();

2. Request FragmentManager from an Activity and Create new transaction with FragmentManager
     FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();

3. Add/Remove/Replace Fragments within the transaction
     fragTransaction.replace(R.id.id_addRemoveReplace, frag1, FRAGMENT_TAG);

4. Commit the transaction
     fragTransaction.commit();

These are the 4 steps that are followed for any transaction.
In our example we have three methods. Let’s go through each of them in brief

AddFragment(): This method will check if the oldFragment is null, then only add frag1 or else do nothing.
RemoveFragment(): This method will check if the oldFragment is not null, then only remove the existing fragment and add frag2.
ReplaceFragment(): In this method I am using replace() which will remove the existing fragment and add frag1. So replace method perform both, add and remove.

Importance of addToBackStack():

When we add, remove and do other stuff on fragment, clicking on back button will navigate back to the activity.
For example:
If you navigate from Frag1 -> Frag2 -> Frag1, clicking on back will navigate user to their parent activity.
To make it work properly we should use addToBackStack(String name) before committing each change, then clicking on back will perform frag1 -> frag2 -> frag1.

onBackStackChanged() method is called whenever the content of backStack changes.

 

Attach and Detach

So far we have performed add, remove and replace action. In this section we will see how we can attach and detach the fragment.
Let’s create a class which will have 3 buttons; attach, detach, add and one layout to display fragment transaction.

fragment_transaction_2.xml

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

<LinearLayout
android:id="@+id/id_addRemoveReplace"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" >

<Button
android:id="@+id/id_AddFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ADD" />

<Button
android:id="@+id/id_AttachFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ATTACH" />

<Button
android:id="@+id/id_DetachFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="DETACH" />
</LinearLayout>

</LinearLayout>

Now create FragmentTransaction2.java class, in which we will write a logic to add, attach or detach the fragment

FragmentTransaction2.java

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.codeureka.fragmentsexample.R;

public class FragmentTransaction2 extends Activity implements OnClickListener{

  final String FRAGMENT_TAG="fragment_tag";
  Button btn_Attach,btn_Detach, btn_Add;

@Override
protected void onCreate(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onCreate(savedInstanceState);
   setContentView(R.layout.fragment_transaction_2);
   btn_Attach = (Button)findViewById(R.id.id_AttachFrag);
   btn_Detach = (Button)findViewById(R.id.id_DetachFrag);
   btn_Add = (Button)findViewById(R.id.id_AddFrag);

   btn_Attach.setOnClickListener(this);
   btn_Detach.setOnClickListener(this);
   btn_Add.setOnClickListener(this);
}

@Override
public void onClick(View v) {
   // TODO Auto-generated method stub
   int id = v.getId();
   switch (id) {
      case R.id.id_AddFrag:
        AddFragment();
      break;
      case R.id.id_AttachFrag:
        AttachFragment();
      break;
      case R.id.id_DetachFrag:
        DetachFragment();
      break;
      default:
      break;
   }
}

private void DetachFragment() {
   // TODO Auto-generated method stub
   Fragment fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
   if(fragment != null) {
      FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
      fragTransaction.detach(fragment);
      fragTransaction.addToBackStack("DetachFragment");
      fragTransaction.commit();
   }
}

private void AttachFragment() {
   // TODO Auto-generated method stub
   Fragment fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
   if(fragment != null) {
      FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
      fragTransaction.attach(fragment);
      fragTransaction.addToBackStack("AttachFragment");
      fragTransaction.commit();
   }
}

private void AddFragment() {
   // TODO Auto-generated method stub
   Fragment frag1 = new Fragment_1();
   FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
   Fragment oldFrag = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
   if(oldFrag == null)
      fragTransaction.add(R.id.id_addRemoveReplace, frag1, FRAGMENT_TAG);
      fragTransaction.addToBackStack("AddFragment");
      fragTransaction.commit();
   }
}

Before moving further let me ask you a simple question. Are you still thinking that why we have added Add button again?
To answer this question let me take one scenario:
In above example, we saw that we can add the Fragment, we can remove the fragment and we can even replace the fragment. But what if we just want to hide and display the fragment? What if we don’t want our Fragment to completely get removed and then add again? What if just want to make it invisible to the user but still attached to the activity. Well the answer is attach() and detach() method. Most of you must have thought that detach() is same as onDetach() method that i explained during fragment lifecycle, but that is not correct. onDetach() method gets called when fragment is detached from an activity, but here the detach() method will just hide the UI from the user. In same way, attach will not call onAttach() method, it will just display the UI again to the user.

Now let me change my question little bit. Can we hide and display the thing which doesn’t even exists?
Yes you are right, we cannot hide and display the thing which does not even exists. That is why we have add button, add button will add the fragment and detach, attach button will make that fragment hidden and visible to the user.

Now let’s go back to our code, so after this discussion i think everything is pretty straight forward now. AddFragment() will add the frag1, AttachFragment() will check if the fragment is not null then attach it (i.e. make it visible), DetachFragment() will check if the fragment is not null then detach it (i.e. make it invisible to the user).

Let’s discuss about attach and detach from code’s perspective. If you have run the above code, just follow these steps:
click on Add => Fragment will be added and visible to user
click on detach => Fragment will be hidden and become invisible
click on Add again => nothing will happen. Because in AddFragment() method we are checking if fragment is null, then only add the frag1, but clicking on detach button doesn’t detach the fragment from an activity. It just makes it invisible, which is why clicking on Add again will not add frag1 again.

 

Example of android Fragment Lifecycle

This example just show the flow of fragment and it associated activity’s methods that gets called.

Let’s create FragmentLifeCycleActivity.java class which will extend the Android activity class

FragmentLifeCycleActivity.java

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.widget.Toast;

public class FragmentLifeCycleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onCreate(savedInstanceState);
   Toast.makeText(this, "ACTIVITY: onCreate", Toast.LENGTH_SHORT).show();
   setContentView(R.layout.fragment_life_cycle_activity);
}

@Override
public void onAttachFragment(Fragment fragment) {
   // TODO Auto-generated method stub
   Toast.makeText(this, "ACTIVITY: onAttachFragment", Toast.LENGTH_SHORT).show();
   super.onAttachFragment(fragment);
}

@Override
protected void onStart() {
   // TODO Auto-generated method stub
   Toast.makeText(this, "ACTIVITY: onStart", Toast.LENGTH_SHORT).show();
   super.onStart();
}

@Override
protected void onResume() {
   // TODO Auto-generated method stub
   Toast.makeText(this, "ACTIVITY: onResume", Toast.LENGTH_SHORT).show();
   super.onResume();
}

@Override
protected void onPause() {
   // TODO Auto-generated method stub
   Toast.makeText(this, "ACTIVITY: onPause", Toast.LENGTH_SHORT).show();
   super.onPause();
}
@Override
protected void onStop() {
   // TODO Auto-generated method stub
   Toast.makeText(this, "ACTIVITY: onStop", Toast.LENGTH_SHORT).show();
   super.onStop();
}
@Override
protected void onDestroy() {
   // TODO Auto-generated method stub
   Toast.makeText(this, "ACTIVITY: onDestroy", Toast.LENGTH_SHORT).show();
   super.onDestroy();
}
}

fragment_life_cycle_activity.xml

<?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/fragLifeCycleActivity"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_width="match_parent"
class="com.codeureka.fragmentsexample.FragmentLifeCycleFrag" >
</fragment>

</LinearLayout>

Now let’s create FragmentLifeCycleFrag .java class which will extend android Fragment class

FragmentLifeCycleFrag.java

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

public class FragmentLifeCycleFrag extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onCreateView", Toast.LENGTH_SHORT).show();
   View view = inflater.inflate(R.layout.fragment_life_lycle_frag, container,false);
   return view;
}
@Override
public void onCreate(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onCreate", Toast.LENGTH_SHORT).show();
   super.onCreate(savedInstanceState);
}

@Override
public void onAttach(Activity activity) {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onAttach", Toast.LENGTH_SHORT).show();
   super.onAttach(activity);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onActivityCreated", Toast.LENGTH_SHORT).show();
   super.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onStart", Toast.LENGTH_SHORT).show();
   super.onStart();
}
@Override
public void onResume() {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onResume", Toast.LENGTH_SHORT).show();
   super.onResume();
}

@Override
public void onStop() {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onStop", Toast.LENGTH_SHORT).show();
   super.onStop();
}
@Override
public void onDestroyView() {
   // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onDestroyView", Toast.LENGTH_SHORT).show();
   super.onDestroyView();
}

@Override
public void onDetach() {
  // TODO Auto-generated method stub
   Toast.makeText(getActivity(), "FRAGMENT: onDetach", Toast.LENGTH_SHORT).show();
   super.onDetach();
  }
}

Above class doesn’t do much, it just display the Toast messages which will tell you the sequence of Fragment and its associated activity’s lifecycle. Below is the result:
When user starts the app:

ACTIVITY: onCreate
FRAGMENT: onAttach
ACTIVITY: onAttachFragment
FRAGMENT: onCreate
FRAGMENT: onCreateView
FRAGMENT: onActivityCreated
ACTIVITY: onStart
FRAGMENT: onStart
ACTIVITY: onResume
FRAGMENT: onResume

 

When user clicks back button:

ACTIVITY: onPause
FRAGMENT: onStop
ACTIVITY: onStop
FRAGMENT: onDestroyView
FRAGMENT: onDetach
ACTIVITY: onDestroy

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>