fragments in android studio

Fragments in Android Studio

Through the previous articles we learned what are activities in android studio, what are intents and also activities life cycle. In this article we will learn learn what are fragments. Fragments are another part of the android app that represents a reusable portion of your app UI. A Fragment can determines and maintains it’s own layout and life cycle. Moreover, it can manage it’s own life cycle. One important note is that fragments can not exist on their own. Moreover, we must host the fragments by an activity or another fragment.

Fragment Reusability and Modulatory

Fragments would be more useful for specifying and controlling the user interface of a single monitor or screen component. On the other contrary, Activities are a best environment to round up global items like a navigation drawer around the user interface of your app.

In order to make it easy to modify your activity at runtime you need to divide your app UI into fragments. Further more, You can use multiple instances of the same fragment class within the same activity, in multiple activities, or even as a child of another fragment. With this in mind, you should only provide a fragment with the logic necessary to manage its own UI. You should avoid depending on or manipulating one fragment from another.

Create A new Fragment

In order to create a new fragment you need to create a class the extends AndroidX Fragment class and override it’s methods to insert your app logic. Now we will work practically so, you need to create a new android studio project. Then create a new Java class I named it MinimalFragment. After that create a new resource layout file and name it “minimal_fragment.xml”. Next, copy the following code in the fragment java file.

public class MinimalFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.minimal_fragment,container,false);
    }
}

After that copy the following xml code in the resource layout file that you have created.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".MinimalFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="I am A Fragment"
        android:layout_gravity="center"
        android:textSize="35sp"
        android:gravity="center"/>
</FrameLayout>

Well done, now we need to edit the main activity xml file to add frame layout that will be replaced by our fragment. Then copy the following code in the activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".MainActivity">


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/frameLayout"
        android:background="@android:color/darker_gray"/>
</FrameLayout>

Now we want the fragment to be hosted in the main activity. For that we need to use support fragment manager. Don’t worry about about support fragment manager we will learn it in this article but now we need to try it. So, copy the following code in the main activity java file.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportFragmentManager().beginTransaction()
                .setReorderingAllowed(true)
                .add(R.id.frameLayout, new MinimalFragment(), null)
                .commit();
    }
}

Now, your code is ready run your project if success you will see the following image in the android emulator.

new fragment

Fragment Manager

The fragment manager is the class that responsible for actions like adding replacing or removing fragments in your app. In the next articles we will learn android jetpack Navigation library where we will not use the fragment manger but for now we need to understand how it works. Also, we need to know how to access the fragment manager, it’s roll in relation to your activities and fragments, managing the back stack with FragmentManager, and providing data and dependencies to your fragments.

How to access the Fragment Manager

Accessing in Activity: If your are using AppcompateActivity or FragmentActivity subclass, you need to access the fragment manager through getSupportFragmentManager()

Accessing in Fragment: The fragments also are able to host one or more child fragments. So, if you need to access the fragment manager inside a fragment’s children use getChildFragmentManager(). Further more, if you need to access its host FragmentManager, you can use getParentFragmentManager().

Child Fragments

Generally, your app should consists of single or small number of activities. Each activity represent a small number of related screens.  The activity may provide a point to place top-level navigation and a place to scope view models and other view-state between fragments. Each individual destination in your app should be represented by a fragment.

Cases where you may need to use child fragments:

  • Screen Slides using pager view.
  • Sub-navigation within a set of related screens.
  • jetpack Navigation.

Using Fragment Manager

The fragment manager can perform back stack operations like adding or removing the fragments upon user interactions with your app. Each set of changes are committed together as a single unit called a Fragment Transactions.

When the user clicks on the back button or when you call FragmentManager.popBackStack() method, the top-most fragment transaction is popped off of the stack, we can call that the transaction is reversed. If there are not fragments in the transaction stack or your are not use child fragments the back event bubbles up to the activity. By adding addToBackStack(), you can reverse the transaction.

Perform Transaction

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment.class, null)
    .setReorderingAllowed(true)
    .addToBackStack("name") // name can be null
    .commit();

You can use the fragment manager to display the fragment in container layout. To do that use the fragment transaction to add or replace the fragment as shown in the above example.

Use the Reordering allowing call to optimizes the state changes of the fragments involved in the transaction so that animations and transitions work correctly.

Calling addToBackStack() commits the transaction to the back stack. The user can later reverse the transaction and bring back the previous fragment by pressing the Back button. 

How to find an existing fragment

You can find the fragment either by findFragmentById() or findFragmentByTag(). See the following examples.

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment.class, null)
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .commit();


ExampleFragment fragment =
        (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment.class, null, "tag")
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .commit();


ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");

Provide dependencies to your fragments

Generally, you can add a fragment by creating a new instance of it and add it to the fragment transaction. But now, we did not know what is the dependency. Simply, the dependency is an object (variable of primitive data or another class object) that we can pass it to the constructor of the class and use it.

// Instantiate a new instance before adding
MyFragment myFragment = new MyFragment();
fragmentManager.beginTransaction()
    .add(R.id.fragment_view_container, myFragment)
    .setReorderingAllowed(true)
    .commit();

But What is the problem to add dependency to the fragment constructor

In order to the fragment manager instantiate a new instance of the fragment, it uses the Fragment factory that the android system provides. This factory invoke a no-argument constructor of your fragment. That means you can not use this default factory to provide dependency to your fragment. So that we need to create a custom Fragment Factory to provide our fragment with a dependency.

Suppose you have a fragment (MinimalFragment) and this fragment is dependent on the following repository class (MyRepository.java)

class MyRepository {

    private static MyRepository mInstance = null;

    MyRepository getInstance(){
        if (mInstance ==null){
             mInstance  = new MyRepository();
        }
        return mInstance;
    }


}

The fragment constructor should be as the following.

public class MinimalFragment extends Fragment {
    MyRepository myRepository;

    public MinimalFragment(MyRepository myRepository){
        super();
        this.myRepository = myRepository;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.minimal_fragment,container,false);
    }
}

Now we need to create a new class for custom fragment factory (MyFragmentFactory.java).

class MyFragmentFactory  extends FragmentFactory {
    private static final String TAG = "MyFragmentFactory";
    private final MyRepository myRepository;

    public MyFragmentFactory(MyRepository myRepository) {
        super();
        Log.d(TAG, "instantiate: "+MyFragmentFactory.this);

        this.myRepository = myRepository;
    }

    @NonNull
    @Override
    public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
        Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className);
        if (fragmentClass == MinimalFragment.class) {
            return new MinimalFragment(myRepository);
        } else {
            return super.instantiate(classLoader, className);
        }
    }
}

You can then designate MyFragmentFactory as the factory to use when constructing your app’s fragments by setting a property on the FragmentManager. You must set this property prior to your activity’s super.onCreate() to ensure that MyFragmentFactory is used when recreating your fragments.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyRepository repository = MyRepository.getInstance();
        getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository));
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
          getSupportFragmentManager().beginTransaction()
                .setReorderingAllowed(true)
                .replace(R.id.frameLayout, new MinimalFragment(repository), null)
                .commit();
    }
}

Read more about fragments andr fragments transaction

Thank You

Leave a Comment

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