Surely Android developers interviewed often have questions such as “How can I communicate between 2 Fragments in the same Activity?”. I often use 1 of 2 ways that I will detail in this article, that is using Callback and ViewModel.
I have a simple problem as follows: There is an Activity containing 2 Fragmen A and B simultaneously, Fragment A to send data including 1 EditText and 1 Button, Fragment B has 1 TextView, how to click on Button A will display the content in EditText on side B. Use the two methods above to solve this problem.
1. Use Callback
This is a fairly common and familiar way, we will use a callback to transfer data from A to B. Fragment A will have the callback object, when clicking the Send button will call the sendText () method in Callback. We will implement that callback in the activity containing both fragments. To make it easier to imagine, you refer to the following code:
Xml files:
activity_main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <FrameLayout android:id="@+id/send_fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="#E8F1F1" android:layout_marginBottom="16dp" /> <FrameLayout android:id="@+id/receive_fragment_container" android:layout_width="match_parent" android:background="#E8EBF5" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout> |
fragment_send
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp"> <EditText android:id="@+id/edit_send" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:hint="Type to send"/> <Button android:id="@+id/btn_send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="send"/> </LinearLayout> |
fragment_receive
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="32dp"> <TextView android:id="@+id/text_receive" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:layout_marginTop="50dp" /> </LinearLayout> |
SendFagment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class SendFragment extends Fragment { private static SendFragment instance; private SendTextListener sendTextListener; public static SendFragment getInstance() { if (instance == null) { instance = new SendFragment(); } return instance; } public void setSendTextListener(SendTextListener sendTextListener) { this.sendTextListener = sendTextListener; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_send, container, false); initView(view); return view; } public void initView(View view) { final EditText editText = view.findViewById(R.id.edit_send); Button button = view.findViewById(R.id.btn_send); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendTextListener.sendText(editText.getText().toString()); } }); } } |
ReceivedFragment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class ReceivedFragment extends Fragment { private static ReceivedFragment instance; private TextView textReceived; public static ReceivedFragment getInstance() { if (instance == null) { instance = new ReceivedFragment(); } return instance; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_receive, container, false); textReceived = view.findViewById(R.id.text_receive); return view; } public void getText(String text) { textReceived.setText(text); } } |
SendTextListenner
1 2 3 4 | public interface SendTextListener { void sendText(String text); } |
MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class MainActivity extends AppCompatActivity implements SendTextListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { getSupportFragmentManager().beginTransaction().add(R.id.send_fragment_container, SendFragment.getInstance()).commit(); getSupportFragmentManager().beginTransaction().add(R.id.receive_fragment_container, ReceivedFragment.getInstance()).commit(); SendFragment.getInstance().setSendTextListener(this); } @Override public void sendText(String text) { ReceivedFragment.getInstance().getText(text); } } |
The result will be as follows, when entering the text Edittext “This is a message” and click the Send button, will display on the TextView of Fragment B
2. Use the ViewModel
I introduced a little bit about ViewModel. The ViewModel is a class designed to store and manage data in a separate lifecycle, allowing data to be preserved even when the screen is rotated. In the way above, when you rotate the screen leading to Configuration change, your Activity will be reset, resulting in data loss, the TextView content in Fragment B will also be reset to empty. As for using ViewModel, because the data is stored in a separate lifecycle and does not depend on the lifecycle of Activity or Fragment (in the case of screen rotation), the data remains the same, so when you rotate the screen then the data will stay the same.
That said, the next way is to communicate between 2 Fragments via ViewModel. First I will create a class that inherits ViewModel, has a property of type MutableLiveData (you can learn more about LiveData, this article is long, so I will not introduce anymore).
SendTextViewModel
1 2 3 4 5 6 7 8 9 | public class SendTextViewModel extends ViewModel { public MutableLiveData<String> text = new MutableLiveData<>(); public MutableLiveData<String> getText() { return text; } } |
Fragment B will register to listen for changes to the value of the “text” attribute in ViewModel so that when changing the value of the “text” attribute in Fragment A, Fragment B will know the change of that attribute and update it. Updated interface. You can refer to the code below to make it easier to understand.
SendFragment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public class SendFragment extends Fragment { private static SendFragment instance; private SendTextViewModel viewmodel; public static SendFragment getInstance() { if (instance == null) { instance = new SendFragment(); } return instance; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_send, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initView(view); } public void initView(View view) { final EditText editText = view.findViewById(R.id.edit_send); viewmodel = ViewModelProviders.of(getActivity()).get(SendTextViewModel.class); Button button = view.findViewById(R.id.btn_send); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewmodel.getText().postValue(editText.getText().toString()); } }); } |
ReceivedFragment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class ReceivedFragment extends Fragment { private static ReceivedFragment instance; private TextView textReceived; private SendTextViewModel viewmodel; public static ReceivedFragment getInstance() { if (instance == null) { instance = new ReceivedFragment(); } return instance; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_receive, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewmodel = ViewModelProviders.of(getActivity()).get(SendTextViewModel.class); textReceived = view.findViewById(R.id.text_receive); viewmodel.getText().observe(this, new Observer<String>() { @Override public void onChanged(String s) { textReceived.setText(s); } }); } } |
And of course the result is the same as the way 1 but this method is quite concise and additional bonus case of preserving data when rotating the screen.
3. Summary
I have presented 2 ways I usually use to solve the question raised at the beginning of the article. You use other ways, the comment below for us to learn and apply offline.
Link github here .