Core Android

Fragment Communication using Interface

In this post, we’ll learn Fragment Communication using Interface in Android. For doing that we’ll create a sample android application that contains ViewPager, TabLayout and Fragments. In this app, We’ll implement functionality that passing data between fragments.

Normally, Intents are used for sending data to activity level. To pass data between fragment have used setArguments(Bundle) when you are creating a new instance of Fragment. So once you have created new instance of the fragment you can’t update that bundle. Let’s think for movement how to communicate between fragments in android.

UseCase

Let’s take a proper use case, you are using ViewPager with tab layout and you want to update the second fragment on a certain event of first fragments.

There are several ways to doing that, In this post, we will learn only using Interface.

1. Let’s move to Android Studio and create a new project

In this project, we are using androidx with material design. let’s ensure below dependencies.

  implementation 'androidx.appcompat:appcompat:1.0.2'
  implementation 'com.google.android.material:material:1.0.0'
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

2. Defined in the styles.xml file as shown below.

The styles for the TabLayout and ToolBar below

<resources>

  <!-- Base application theme. -->
  <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
  </style>

  <style name="AppTheme.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
  </style>

  <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>

  <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>

</resources>

3. Prepare MainActivity XML layout

In this android application, we are using ViewPager, TabLayout with Fragments. Let’s open the activity_main.xml layout file and paste below code.

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    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"
    >

  <com.google.android.material.appbar.AppBarLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:theme="@style/AppTheme.AppBarOverlay"
      >

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minHeight="?actionBarSize"
        android:padding="@dimen/appbar_padding"
        android:text="@string/app_name"
        android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
        />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        />
  </com.google.android.material.appbar.AppBarLayout>

  <androidx.viewpager.widget.ViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layout_behavior="@string/appbar_scrolling_view_behavior"
      />

  <com.google.android.material.floatingactionbutton.FloatingActionButton
      android:id="@+id/fab"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="bottom|end"
      android:layout_margin="@dimen/fab_margin"
      app:srcCompat="@android:drawable/ic_dialog_email"
      />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

4. Now create a fragment named FirstFragment

4.1 In a root package, create a new Fragment and update that xml as below.

In this layout, I have added two TextInputLayout. So we send data first fragment to the second fragment onTextChange() event.

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
  <ImageView
      android:id="@+id/imageView"
      android:layout_width="96dp"
      android:layout_height="96dp"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="24dp"
      android:src="@drawable/user_avatar"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />
  <com.google.android.material.textfield.TextInputLayout
      android:id="@+id/textInputLayout"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginLeft="16dp"
      android:layout_marginRight="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="24dp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/imageView"
      style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
      >

    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/textInputTextName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter Name"
        />
  </com.google.android.material.textfield.TextInputLayout>
  <com.google.android.material.textfield.TextInputLayout
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginLeft="16dp"
      android:layout_marginRight="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="16dp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textInputLayout"
      style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
      >
    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/textInputTextEmail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter Email"
        />
  </com.google.android.material.textfield.TextInputLayout>


</androidx.constraintlayout.widget.ConstraintLayout>
4.2 Now open fragment java file and paste below code
package com.fragmentcommunicationexample.ui.main;

import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.fragmentcommunicationexample.R;
import com.google.android.material.textfield.TextInputEditText;

public class FirstFragment extends Fragment {

  private OnFragmentCommunicationListener mListener;

  public FirstFragment() {
    // Required empty public constructor
  }

  /**
   * Create a new instance of this fragment
   *
   * @return A new instance of fragment FirstFragment.
   */

  public static FirstFragment newInstance() {
    return new FirstFragment();
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_first, container, false);
  }

  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    TextInputEditText nameEditText = view.findViewById(R.id.textInputTextName);
    TextInputEditText emailEditText = view.findViewById(R.id.textInputTextEmail);
    nameEditText.addTextChangedListener(new TextWatcher() {
      @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

      }

      @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        mListener.onNameChange(charSequence.toString());
      }

      @Override public void afterTextChanged(Editable editable) {

      }
    });

    emailEditText.addTextChangedListener(new TextWatcher() {
      @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

      }

      @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        mListener.onEmailChange(charSequence.toString());
      }

      @Override public void afterTextChanged(Editable editable) {

      }
    });
  }

  @Override
  public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentCommunicationListener) {
      mListener = (OnFragmentCommunicationListener) context;
    } else {
      throw new RuntimeException(context.toString()
          + " must implement OnFragmentCommunicationListener");
    }
  }

  @Override
  public void onDetach() {
    super.onDetach();
    mListener = null;
  }

  /**
   * This interface must be implemented by activities that contain this
   * fragment to allow an interaction in this fragment to be communicated
   * to the activity
   */
  public interface OnFragmentCommunicationListener {
    void onNameChange(String name);

    void onEmailChange(String email);
  }
}

5. Create another fragment named SecondFragment

As usual, create a new fragment, In this fragment, we’ll receive the data and display on TextView

<?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"
    tools:context=".ui.main.SecondFragment"
    >

  <ImageView
      android:id="@+id/imageView2"
      android:layout_width="144dp"
      android:layout_height="144dp"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="24dp"
      android:src="@drawable/user_avatar"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />
  <TextView
      android:id="@+id/textViewName"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="32dp"
      android:gravity="center"
      android:hint="User Display Name"
      android:textColor="@color/colorPrimaryDark"
      android:textSize="22sp"
      android:textStyle="bold"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/imageView2"
      tools:text="Morris"
      />
  <TextView
      android:id="@+id/textViewEmail"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginLeft="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:gravity="center"
      android:hint="user@gmail.com"
      android:textColor="@color/colorPrimary"
      android:textSize="18sp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textViewName"
      />
</androidx.constraintlayout.widget.ConstraintLayout>
6. The layout is ready to use now, Now configure it with JAVA code.
package com.fragmentcommunicationexample.ui.main;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.fragmentcommunicationexample.R;

public class SecondFragment extends Fragment {

  private static final String TAG = "SecondFragment";

  private TextView txtName, txtEmail;

  public SecondFragment() {
    // Required empty public constructor
  }

  /**
   * Use this factory method to create a new instance of this fragment.
   *
   * @return A new instance of fragment SecondFragment.
   */
  public static SecondFragment newInstance() {
    return new SecondFragment();
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_second, container, false);
  }

  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    txtName = view.findViewById(R.id.textViewName);
    txtEmail = view.findViewById(R.id.textViewEmail);
  }

  public void onNameChange(String name) {
    txtName.setText(name);
    Log.d(TAG, "onNameChange() returned: " + name);
  }

  public void onEmailChange(String email) {
    txtEmail.setText(email);
    Log.d(TAG, "onEmailChange() returned: " + email);
  }
}

6. Create a ViewPager Adapter where the Fragments are initialized.

package com.fragmentcommunicationexample.ui.main;

import android.content.Context;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.fragmentcommunicationexample.R;

/**
 * A [FragmentPagerAdapter] that returns a fragment corresponding to
 * one of the sections/tabs/pages.
 */
public class SectionsPagerAdapter extends FragmentPagerAdapter {

  @StringRes
  private static final int[] TAB_TITLES = new int[] { R.string.tab_text_1, R.string.tab_text_2 };
  private final Context mContext;

  public SectionsPagerAdapter(Context context, FragmentManager fm) {
    super(fm);
    mContext = context;
  }

  @Override
  public Fragment getItem(int position) {
    // getItem is called to instantiate the fragment for the given page.
    if (position == 0) {
      return FirstFragment.newInstance();
    } else {
      return SecondFragment.newInstance();
    }
  }

  @Nullable
  @Override
  public CharSequence getPageTitle(int position) {
    return mContext.getResources().getString(TAB_TITLES[position]);
  }

  @Override
  public int getCount() {
    // Show 2 total pages.
    return 2;
  }
}

7. Finally, Open the MainActivity Java class and paste below code

package com.fragmentcommunicationexample;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import com.fragmentcommunicationexample.ui.main.FirstFragment;
import com.fragmentcommunicationexample.ui.main.SecondFragment;
import com.fragmentcommunicationexample.ui.main.SectionsPagerAdapter;
import com.google.android.material.tabs.TabLayout;

public class MainActivity extends AppCompatActivity
    implements FirstFragment.OnFragmentCommunicationListener {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    SectionsPagerAdapter sectionsPagerAdapter =
        new SectionsPagerAdapter(this, getSupportFragmentManager());
    ViewPager viewPager = findViewById(R.id.view_pager);
    viewPager.setAdapter(sectionsPagerAdapter);
    TabLayout tabs = findViewById(R.id.tabs);
    tabs.setupWithViewPager(viewPager);
  }

  @Override public void onNameChange(String name) {
    String tag = "android:switcher:" + R.id.view_pager + ":" + 1;
    SecondFragment fragment = (SecondFragment) getSupportFragmentManager().findFragmentByTag(tag);
    if (fragment != null) {
      fragment.onNameChan