Tag

fragments

Browsing

In this post, we’ll learn Fragment communication using RxJava. There are several ways of communicating two fragments. This approach is the best way of doing that and easy to implement. In my opinion, this is the best approach for communicating two fragments in Android.

This way is pretty good and easy to implement. Personally, I feel this is the best way to communicating two fragments in Android. In this technique, We use PublishSubject to create our own RxBus. This PublishSubject emits all the subsequent items of the source Observable.

1. Set up a new project

In this android app tutorial, we’ll create a ViewPager with TabLayout. Android send the data from first fragment to second fragment. For doing that we’ll use PublishSubject in RxJava. Let’s create a new project with androidx and add dependencies in app/build.gradle.

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.appcompat:appcompat:1.0.2'
  implementation 'com.google.android.material:material:1.0.0'
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

  // RxJava2 Dependencies
  implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
  implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'


  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'androidx.test:runner:1.2.0'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

2. Prepare ViewPager with TabLayout

Open the activity_main.xml layout add below line of code. In this layout file I’m adding TabLayout and ViewPager.

<?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"
      />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

3. Create own RxBus

Generally, no default way of communicating two fragments. In this android example, we’ll use PublishSubject to create our own RxBus. First, we have to understand what is PublishSubject?

PublishSubject emits all the subsequent items of the source Observable at the time of subscription. In simple words, if a student entered late into the classroom, he just wants to listen from that point of time when he entered the classroom. So, Publish will be the best for this use-case.

For doing that create singleton java class and add below code

package com.fragmentcommunicationexample.ui.main;

import io.reactivex.Observable;
import io.reactivex.subjects.PublishSubject;

public class RxBus {

  private static RxBus mInstance;

  public static RxBus getInstance() {
    if (mInstance == null) {
      mInstance = new RxBus();
    }
    return mInstance;
  }

  private RxBus() {
  }

  private PublishSubject<String> publisher = PublishSubject.create();

  void publish(String event) {
    publisher.onNext(event);
  }

  // Listen should return an Observable
  Observable<String> listen() {
    return publisher;
  }
}

As you saw above code snippet, here I have defined two methods.

  • publish(String event) -> this method this publish event
  • Observable listen() -> It return an Observable

4. Create a new Fragment named FirstFragment

I making few changes in FirstFragment layout file

<?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="72dp"
      android:layout_height="72dp"
      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/textInputTextData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter Input"
        />
  </com.google.android.material.textfield.TextInputLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

5. Now open the Java file and bind the layout with source

package com.fragmentcommunicationexample.ui.main;

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 {

  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 inputDataText = view.findViewById(R.id.textInputTextData);
    inputDataText.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) {
        RxBus.getInstance().publish(charSequence.toString());
      }

      @Override
      public void afterTextChanged(Editable editable) {
      }
    });
  }
}

Now see the highlight line code RxBus.getInstance().publish(charSequence.toString()); here we are just published the event on RxBus instance. This line will publish the event to all subscribers.

6. Create another fragment named is SecondFragment

In this fragment, we’ll listen to a published event and display the value on TextView. I’m adding a TextView in layout file

<?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="72dp"
      android:layout_height="72dp"
      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="Display received input data"
      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"
      />

</androidx.constraintlayout.widget.ConstraintLayout>

7. Let’s move to SecondFragment java file

package com.fragmentcommunicationexample.ui.main;

import android.os.Bundle;
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;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;

public class SecondFragment extends Fragment {

  private TextView txtName;

  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);

    //  listen RxJava event here
    RxBus.getInstance().listen().subscribe(getInputObserver());
  }

  // Get input observer instance
  private Observer<String> getInputObserver() {
    return new Observer<String>() {
      @Override public void onSubscribe(Disposable d) {

      }

      @Override public void onNext(String s) {
        txtName.setText(s);
      }

      @Override public void onError(Throwable e) {

      }

      @Override public void onComplete() {

      }
    };
  }
}

8. Create Pager Adapter for ViewPager

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;
  }
}

9. Open MainActivity and add below code

package com.fragmentcommunicationexample;

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

public class MainActivity extends AppCompatActivity {

  @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);
  }
}

Conclusion

In this blog, we learned how to communicate between Fragments using RxJava in our application. I Hope you enjoyed this article. Help me by sharing this post with all your friends who learning android app development.

Deep drive on fragment commucation Four ways to communicate between fragments in android

In this android tutorial, We’ll learn Fragment communication using ViewModel. Using shared ViewModel is recommended way by Google for communicating between two fragments. In this technique, We’ll create a shared ViewModel instance and owner of this ViewModel will be Activity. Both fragments will access this ViewModel.

What is ViewModel?

Android Architecture Components provide a ViewModel helper class. That is used to store and manage UI-related changes in a lifecycle conscious way. We usually create one view model for one activity. One activity can have many fragments, means two or more fragment can share one ViewModel.

Data sharing between fragments

Data sharing between fragments is a very common task. While you developing an android application, So many scenarios come where you need to communicate between two fragments. There are several ways to do that, Read our article – Four ways to communicate between fragments.

Fragment communication using ViewModel (Demo App)

Fragment Communication Sample Application

In this tutorial, we’ll create a sample application, that contains one shared ViewModel, ViewPager with TabLayout. Here we’ll have two fragments in ViewPager and we will try to send data from first fragment to second fragment. Both fragments will be used common ViewModel.  So, let’s get started.

1. Project Setup

Let’s move to Android Studio and create a new project with BasicActivity template. After creating the project verify below dependencies.

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

  implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
  implementation 'androidx.legacy:legacy-support-v4:1.0.0'
}

2. Prepare MainActivity UI

Before getting into logic part, create some UI for this demo application. Open the activity_main.xml layout file and add 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"
      />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

3. Create a subclass of ViewModel

In main package create subclass of ViewModel named is PageViewModel. In this class, we are taking one MutableLiveData<String> instance. that collect the input from FirstFragment and show the data other fragments. Final code looks like below

package com.fragmentcommunicationexample.ui.main;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class PageViewModel extends ViewModel {

  /**
   * Live Data Instance
   */
  private MutableLiveData<String> mName = new MutableLiveData<>();

  public void setName(String name) {
    mName.setValue(name);
  }

  public LiveData<String> getName() {
    return mName;
  }
}

5. Create a Fragment named is FirstFragment

5.1 Prepare Fragment UI

Once you have prepared a layout for MainActivity then create a new fragment is named FirstFragment. I ‘m adding in TextInputLayout in this layout file, code is below

<?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="72dp"
      android:layout_height="72dp"
      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="32dp"
      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>
</androidx.constraintlayout.widget.ConstraintLayout>
5.2 Bind the UI with Fragment and init ViewModel

So, we are done with the UI part. Let’s move on to the coding part of this fragment class. As you saw in code I have inflated the view and add addTextChangedListener() listener on TextInputEditText. So onTextChanged() we will send the data to second fragment.

Now let’s move on to ViewModel part. In fragment onCreate() method we initializing ViewModel this owner of it is Activity.

// init ViewModel
pageViewModel = ViewModelProviders.of(requireActivity()).get(PageViewModel.class);

After initializing ViewModel we’ll set the data like this

  pageViewModel.setName(charSequence.toString());
Finally, FirstFragment code looks like below
package com.fragmentcommunicationexample.ui.main;

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 androidx.lifecycle.ViewModelProviders;
import com.fragmentcommunicationexample.R;
import com.google.android.material.textfield.TextInputEditText;

public class FirstFragment extends Fragment {

  private PageViewModel pageViewModel;

  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 void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // init ViewModel
    pageViewModel = ViewModelProviders.of(requireActivity()).get(PageViewModel.class);
  }

  @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);

    // Add Text Watcher on name input text
    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) {
        pageViewModel.setName(charSequence.toString());
      }

      @Override public void afterTextChanged(Editable editable) {

      }
    });
  }
}

6. Create another Fragment named is SecondFragment

Same as previous fragment, I’m adding a TextView in layout file for showing input that taken from FirstFragment.

<?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="72dp"
      android:layout_height="72dp"
      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"
      />

</androidx.constraintlayout.widget.ConstraintLayout>
Inflate SecondFragment Layout

Nothings to do special here just bind Inflate the layout. Now our final task is to get the from ViewModel and display it on the TextView present in the MessageReceiver fragment:

Now, our final task is to get the message from the ViewModel and display it on the TextView present in the SecondFragment. Code is below

    pageViewModel.getName().observe(requireActivity(), new Observer<String>() {
      @Override
      public void onChanged(@Nullable String s) {
        txtName.setText(s);
      }
    });
Final code of SecondFragment is below
package com.fragmentcommunicationexample.ui.main;

import android.os.Bundle;
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 androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.fragmentcommunicationexample.R;

public class SecondFragment extends Fragment {

  private PageViewModel pageViewModel;
  private TextView txtName;

  public ≈() {
    // 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 void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // initialise ViewModel here
    pageViewModel = ViewModelProviders.of(requireActivity()).get(PageViewModel.class);
  }

  @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);

    pageViewModel.getName().observe(requireActivity(), new Observer<String>() {
      @Override
      public void onChanged(@Nullable String s) {
        txtName.setText(s);
      }
    });
  }
}

4. Create a Fragment Pager Adapter

Let’s full fill this demo needs. You know we are using ViewPager for showing two fragments. Let’s create a ViewPager adapter named is ViewPagerAdapter. So, the code for ViewPagerAdapter will be:

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 ViewPagerAdapter 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 ViewPagerAdapter(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;
  }
}

Open the MainActivity and paste below

This is not related to this topic but it part of this sample application. Just open the MainActivity file and add final code.

package com.fragmentcommunicationexample;

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

public class MainActivity extends AppCompatActivity {

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

Now run the application and put the input in TextInputLayout. Now swipe view you see all changes appear in the second fragment.

Conclusion

In this post, we learned how to communicate between Fragments using shared ViewModel in our application. I hope you enjoyed this article. Help me by sharing this post with all your friends who learning android app development.

Deep drive on fragment commucation Four ways to communicate between fragments in android

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.onNameChange(name);
    }
  }

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

Conclusion

In this android tutorial, I have learned the way of fragment communication using Interface in Android. We have also created a sample android application with ViewPager in android.  I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.

Keep in touch

If you want to keep in touch when I write new blog posts follow me on facebook or subscribe usIt only takes about 5 seconds to register. 

Deep drive on fragment commucation Four ways to communicate between fragments in android

Still, if you have any queries please put your comment below.