Fragments Basic Knowledge

Fragment Lifecycle :

The Correlation between Activity and Fragment Lifecycle considering Fragment is created on creation of Activity

OnCreate - A

onAttach - F

onCreate - F

onCreateView - F

onActivityCreated - F

onStart - F

onStart - A

onResume - A

onResume - F

onPause - F

onPause - A

onStop - F

onStop - A

onDestroyView - F

onDestroy - F

onDetach - F

onDestroy - A


The Correlation between Activity and Fragment Lifecycle considering Activity is created 1st and then the fragment is added

OnCreate - A

onStart - A

onResume - A

onAttach - F

onCreate - F

onCreateView - F

onActivityCreated - F

onStart - F

onResume - F

onPause - F

onPause - A

onStop - F

onStop - A

onDestroyView - F

onDestroy - F

onDetach - F

onDestroy - A


The Correlation between Activity and Fragment Lifecycle considering Activity is created 1st and then the fragment is added to back stack and back pressed

OnCreate - A

onStart - A

onResume - A

onAttach - F

onCreate - F

onCreateView - F

onActivityCreated - F

onStart - F

onResume - F

onPause - F

onStop - F

onDestroyView - F

onDestroy - F

onDetach - F

onPause - A

onStop - A

onDestroy - A

Steps for Adding a fragment :

  1. Create a layout for fragment
  2. Create a fragment class and extend with Fragment class
  3. Set the layout file to the defined fragment
  4. Add a layout that will host the fragment
  5. Write code to add Fragment to Activity in Activity
Fragments should not be aware of each other
Fragments should not launch other fragments
Activity should be aware of all fragments
Actovity should perform Add/Remove/Replace actions

We can Add, Remove and Replace a fragment. This can be done using Fragment Transaction. To get hold of Fragment Transaction, We will be using a API called Fragment Manager.

1. Fragment Manager - Use method from  Activity to get Fragment Manager
2. Fragment Transaction - Provides 3 API's to Add, Replace or Remove the fragment. beginTransaction() will get the Fragment Transaction
We need to invoke Fragment Transaction on Fragment Manager Instance

Add Transaction : 
MainActivity.Java

public class MainActivity extends AppCompatActivity {

Button add;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

add = findViewById(R.id.btnAdd);

add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
addFragment();
}
});
}

private void addFragment() {
Fragment sampleFragment = new SampleFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragmentContainer, sampleFragment, "demofragment");
// This statement will remove the fragment from the activity when we press back button
// Doesn't affect activity lifecycle
fragmentTransaction.addToBackStack("fragmentStack1");
fragmentTransaction.commit();
}
}
Activity back stack is managed by ActivityManager managed by Android Platform itself
Fragment back stack is managed by FragmentManager and needs to be managed by Developer

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/pale_blue_200"
tools:context=".MainActivity">

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragmentContainer"
android:layout_margin="5dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

SampleFragment.Java
public class SampleFragment extends Fragment {

@Nullable
@Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_sample, container, false);
}
}
fragment_sample.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"
android:background="@color/pale_green_200">

</LinearLayout>
Playing with Back Stack : 
Adding Fragment and Removing them from Back Stack
MainActivity.xml
public class MainActivity extends AppCompatActivity {

private final String TAG = this.getClass().getSimpleName();
Button add;
TextView txtFragCount;
FragmentTransaction fragmentTransaction;
FragmentManager fragmentManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

add = findViewById(R.id.btnAdd);
txtFragCount = (TextView)findViewById(R.id.txtFragCount);
fragmentManager = getSupportFragmentManager();

txtFragCount.setText("Frag Count in BackStack " + fragmentManager.getBackStackEntryCount());

add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
addFragment();
}
});

fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
txtFragCount.setText("Frag Count in BackStack " + fragmentManager.getBackStackEntryCount());
}
});

Log.i(TAG, "Initial BackStackCount : "+fragmentManager.getBackStackEntryCount());
}

private void addFragment() {
Fragment fragment;
switch(fragmentManager.getBackStackEntryCount()) {
case 0 : fragment = new SampleFragment();
break;
case 1 : fragment = new FragmentTwo();
break;
case 2 : fragment = new FragmentThree();
break;
default : fragment = new SampleFragment();
break;
}

fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragmentContainer, fragment, "demofragment");
// This statement will remove the fragment from the activity when we press back button
// Doesn't affect activity lifecycle
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/pale_blue_200"
tools:context=".MainActivity">

<Button
android:id="@+id/btnAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Fragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.023" />

<TextView
android:id="@+id/txtFragCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnAdd"
app:layout_constraintVertical_bias="0.041" />

<FrameLayout
app:layout_constraintTop_toBottomOf="@+id/txtFragCount"
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_marginTop="428dp"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="16dp">

</FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
fragment_sample/two/three.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"
android:gravity="center"
android:background="@color/pale_green_200">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="100sp"
android:text="1" />

</LinearLayout>

Playing with Fragments without Back Stack :
Add, Remove and Replace Fragments
public class MainActivity extends AppCompatActivity {

private final String TAG = this.getClass().getSimpleName();
Button add;
TextView txtFragCount;
FragmentTransaction fragmentTransaction;
FragmentManager fragmentManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

add = findViewById(R.id.btnAdd);
txtFragCount = (TextView)findViewById(R.id.txtFragCount);
fragmentManager = getSupportFragmentManager();

txtFragCount.setText("Frag Count in BackStack " + fragmentManager.getBackStackEntryCount());

add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
addFragment();
}
});

fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
txtFragCount.setText("Frag Count in BackStack " + fragmentManager.getBackStackEntryCount());
}
});

Log.i(TAG, "Initial BackStackCount : "+fragmentManager.getBackStackEntryCount());
}

private void addFragment() {
Fragment fragment = fragmentManager.findFragmentById(R.id.fragmentContainer);
if(fragment instanceof SampleFragment) {
fragment = new FragmentTwo();
} else if(fragment instanceof FragmentTwo) {
fragment = new FragmentThree();
} else if(fragment instanceof FragmentThree) {
fragment = new SampleFragment();
} else {
fragment = new SampleFragment();
}

fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragmentContainer, fragment, "demofragment");
// This statement will remove the fragment from the activity when we press back button
// Doesn't affect activity lifecycle
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}

@Override
public void onBackPressed() {
Fragment fragment = fragmentManager.findFragmentById(R.id.fragmentContainer);
if(fragment != null) {
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
} else {
super.onBackPressed();
}
}
}
fragmentTransaction.replace(R.id.fragmentContainer, fragment, "demofragment");
fragmentTransaction.add(R.id.fragmentContainer, fragment, "demofragment");
Add : Will add the fragments one over other and will pop in LIFO when back pressed.
Replace : Will replace the existing fragment with the new fragment. 
Only 1 fragment instance remains at any instance of time

Pop Back Stack are of three types :

Assuming we have 4 fragments added sequentially to backstack with positions mentioned as 
shown below
eg : 
FragFour - 3
FragThree - 2
FragTwo - 1
FragOne - 0
  1. fm.popBackStack() - The default backstack which pops individual fragments from the backstack
  2. fm.popBackStack(int id, int flags) - The 1st int variable refers to the position/id of the fragments to be removed. Eg: Suppose we pass (1,0) then Frag at position 2 and 3 are popped out and position 1 i.e FragTwo will be at the top of backstack Suppose we pass (1,POP_BACK_STACK_INCLUSIVE) then Frag at position 1, 2 and 3 are popped out and position 1 i.e FragOne will be at the top of backstack
  3. fm.popBackStack(String name, int flags) - the String name refers to the string that we has used to add to the Back Stack ie fragmentTransaction.addToBackStack("Add "+fragment.toString()) Eg: Suppose we pass fm.popBackStack("Add FragTwo", 0) then Frag at position 2 and 3 are popped out and position 1 i.e FragTwo will be at the top of backstack Special Cases : Suppose we have the following situation FragTwo - 5 FragTwo - 4 FragFour - 3 FragThree - 2 FragTwo - 1 FragOne - 0 Suppose we pass fm.popBackStack("Add FragTwo", POP_BACK_STACK_INCLUSIVE), now 5 and 4 will be removed
    & if we call fm.popBackStack("Add FragTwo", POP_BACK_STACK_INCLUSIVE) again then 3, 2, 1 will be removed

MainActivity.xml
public class MainActivity extends AppCompatActivity implements View.OnClickListener{

private static final String COMMON_TAG = "CombinedLifeCycle";
private static final String ACTIVITY_NAME = MainActivity.class.getSimpleName();
private static final String TAG = ACTIVITY_NAME;

FragmentManager fragmentManager;
FragmentTransaction fragmentTransaction;

private Button buttonAddFragmentOne, buttonpopFragment, buttonRemoveFragment;
private TextView textViewFragmentCount;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

buttonAddFragmentOne = (Button)findViewById(R.id.buttonAddFragmentOne);
buttonpopFragment = (Button)findViewById(R.id.buttonPopFragment);
buttonRemoveFragment = (Button)findViewById(R.id.buttonRemoveFragment);
textViewFragmentCount = (TextView)findViewById(R.id.textViewFragmentCount);

fragmentManager=getSupportFragmentManager();

textViewFragmentCount.setText("Fragment count in back stack: "+fragmentManager.getBackStackEntryCount());

fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
textViewFragmentCount.setText("Fragment count in back stack: "+fragmentManager.getBackStackEntryCount());

StringBuilder stringBuilder=new StringBuilder("Current status of transaction back stack: "+fragmentManager.getBackStackEntryCount()+"\n");
for(int i=(fragmentManager.getBackStackEntryCount()-1); i>=0;i--){
FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i);
stringBuilder.append(backStackEntry.getName()+"\n");
}
Log.i(TAG,stringBuilder.toString());
}
});

Log.i(COMMON_TAG,"Initial BackStackEntryCount: "+fragmentManager.getBackStackEntryCount());

buttonAddFragmentOne.setOnClickListener(this);
buttonpopFragment.setOnClickListener(this);
buttonRemoveFragment.setOnClickListener(this);
}

private void addFragment(){
Fragment fragment;
if(fragmentManager.getBackStackEntryCount()>0){
switch (fragmentManager.getBackStackEntryCount()){
case 0: loadFragmentOne(); break;
case 1: loadFragmentTwo();break;
case 2: loadFragmentThree(); break;
default: loadFragmentOne(); break;
}
}else {
fragment = fragmentManager.findFragmentById(R.id.fragmentContainer);
if(fragment instanceof SampleFragment){
loadFragmentTwo();
}else if(fragment instanceof FragmentTwo){
loadFragmentThree();
}else if(fragment instanceof FragmentThree){
loadFragmentOne();
}else{
loadFragmentOne();
}
}
}

private void loadFragmentOne(){
Fragment fragment;
fragmentTransaction = fragmentManager.beginTransaction();
fragment = fragmentManager.findFragmentByTag("demofragment");
/*if(fragment!=null){
fragmentTransaction.remove(fragment);
}*/
fragment = new SampleFragment();
fragmentTransaction.replace(R.id.fragmentContainer,fragment,"demofragment");
fragmentTransaction.addToBackStack("Add "+fragment.toString());
fragmentTransaction.commit();
}

private void loadFragmentTwo(){

Fragment fragment;
fragmentTransaction = fragmentManager.beginTransaction();
fragment = fragmentManager.findFragmentByTag("demofragment");
/*if(fragment!=null){
fragmentTransaction.remove(fragment);
}*/
fragment = new FragmentTwo();
fragmentTransaction.replace(R.id.fragmentContainer,fragment,"demofragment");
fragmentTransaction.addToBackStack("Add "+fragment.toString());
fragmentTransaction.commit();
}

private void loadFragmentThree(){
Fragment fragment;
fragmentTransaction = fragmentManager.beginTransaction();
fragment = fragmentManager.findFragmentByTag("demofragment");
/*if(fragment!=null){
fragmentTransaction.remove(fragment);
}*/
fragment = new FragmentThree();
fragmentTransaction.replace(R.id.fragmentContainer,fragment,"demofragment");
fragmentTransaction.addToBackStack("Add "+fragment.toString());
fragmentTransaction.commit();
}

@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.buttonAddFragmentOne: addFragment(); break;
case R.id.buttonPopFragment: fragmentManager.popBackStack("Add SampleFragment",FragmentManager.POP_BACK_STACK_INCLUSIVE); break;
case R.id.buttonRemoveFragment:
fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment=fragmentManager.findFragmentById(R.id.fragmentContainer);
if(fragment!=null){
fragmentTransaction.remove(fragment);
fragmentTransaction.addToBackStack("Remove "+fragment.toString());
fragmentTransaction.commit();
}else{
Toast.makeText(this,"No Fragment to remove",Toast.LENGTH_SHORT).show();
}
break;
default: break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/pale_blue_200"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3">

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

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

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

</LinearLayout>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="@color/pale_green_200"
android:id="@+id/textViewFragmentCount"
android:gravity="center"/>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragmentContainer"
android:layout_margin="5dp" />

</LinearLayout>

Comments

Popular posts from this blog

Android - Using KeyStore to encrypt and decrypt the data

Stack and Queue

Java Reflection API