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