Tag

fragments communication

Browsing

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 how to pass data between fragments in Android. I will also explain, what is possible ways of communicating between two fragments in Android. There’s always need for communication between fragment.

How to communicate between Fragments

You guys well know Fragment is a reuse UI component, It should be built self-contained, modular that defines its own layout and behavior. Once you have written these reusable Fragments, you can associate them with an Activity.

Normally, two Fragments should never communicate directly. There’s always need for communication between Fragments, and this is a very common requirement in the real project, for example, to change the content based on a user event. etc.

Four ways to communication between Fragments

Android has several ways to do that. In this tutorial, I will tell all the possible ways. If you have any feedback or improvement suggestions are more than welcome.

  • Interface
  • ViewModel
  • RxJava
  • Event Bus

You can add here If I have missed. These are the best ways for communicating between one fragment to another fragment. In this article, I will explain only using Interface. For other ways, I will write a separate article.

1. Interface

The interface is the simplest way to communicating between two fragments in android. In this approach, we can define an interface in the Fragment class and implement it in Activity. whoever conforms to that interface, can be informed by the Fragment of events. Basically, Fragment capture the interface implementation onAttach() lifecycle method. Somethings like that

  /**
   * 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);
  }
Attached with Activity
@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;
  }

I have written a separate article on Fragment communication using Interface that gives more insight into this.

2. ViewModel

ViewModel is a recommended way by Google to communicate between fragments. In this approach, we create a shared ViewModel object. Both fragments can access the ViewModel instance and this instance(ViewModel) is associate fragments containing Activity.

I have written a separate article on this approach the below post gives more insight into this.

Read this approach Fragment communication using ViewModel in Android

3. RxJava

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.

Read more about Fragment communication using RxJava in Android

4. Event Bus

EventBus is a publish or subscribe event bus for Android and Java. It simplifies communication between components. Personally, I don’t like this technique because It very loosely coupled and every broadcast can listen to events from a singleton instance. When the project very scalable then makes it very hard to maintain.

Deep drive on Fragment communication using EventBus in Android