Basic Communication Between Two Fragments

Basic communication between two fragments

Have a look at the Android developers page:
http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface

Basically, you define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview with the received value

Your Activity implements your interface (See FragmentA below)

public class YourActivity implements FragmentA.TextClicked{
@Override
public void sendText(String text){
// Get Fragment B
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}

Fragment A defines an Interface, and calls the method when needed

public class FragA extends Fragment{

TextClicked mCallback;

public interface TextClicked{
public void sendText(String text);
}

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);

// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}

public void someMethod(){
mCallback.sendText("YOUR TEXT");
}

@Override
public void onDetach() {
mCallback = null; // => avoid leaking, thanks @Deepscorn
super.onDetach();
}
}

Fragment B has a public method to do something with the text

public class FragB extends Fragment{

public void updateText(String text){
// Here you have it
}
}

Communication between two fragments with interface

I would recommend putting the code for replacing the fragment in your Activity and put the values which should be passed to SFragment() in its arguments directly:

SFragment newFragment = new SFragment();
Bundle args = new Bundle();
args.putString("place1", data1);
args.putString("place2", data1);
newFragment.setArguments(args);

You could get them in SFragment#onCreateView()

String place1 = getArguments().getString("place1");

Also have a look here: https://developer.android.com/training/basics/fragments/communicating.html

How to communicate between fragments?

When communicating from Fragment to Fragment you use an interface to pass data to the Activity which in turn updates the fragment you want to change.

For Example:

In Fragment 1:

public class FragmentOne extends Fragment{

public Callback mCallback;

public interface Callback{
void onUpdateFragmentTwo(String message);
}


@Override
public void onAttach(Activity activity){
super.onAttach(activity);
mCallback = (Callback) activity;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.fragone, container,false);
Button btn = (Button) v.findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallback.onUpdateFragmentTwo("clicked");
}
});
return v;
}
}

then in main Activity implement the interface:

public class MainActivity extends AppCompatActivity implements Callback{

FragmentTwo fragmentTwo;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// ... Load views or perform logic

// ... Load Fragment Two into your container
if(savedInstanceState == null){
fragmentTwo = FragmentTwo.newInstance(new Bundle()); // use real bundle here
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_holder, fragmentTwo, "Frag2").commit();
}
}


// Interface method
@Override
public void onUpdateFragmentTwo(String message){
// Call activity method with the argument
if(fragmentTwo != null){
fragmentTwo.updateFragmentTwo(message);
}
}

}

Update

In your second fragment I typically use a static newInstance(Bundle args) method to initialize and then would use a public method to communicate from the Activity to the Fragment for example:

 public class FragmentTwo extends Fragment{

public static FragmentTwo newInstance(Bundle args){
FragmentTwo fragment = new FragmentTwo();
fragment.setArguments(args);
return fragment;
}

//... Class overrides here onCreateView etc..

// declare this method
public void updateFragmentTwo(String updateText){
// .. do something with update text
}

}

Thats it, happy coding!

Fragment-Fragment communication in Android

It should be done thought listener, so Fragments are still not depend on each other and can be used in one or two pane mode. Activity should handle listeners of both fragments.

Here is an example of activity with two Fragments:

package com.example;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;

import com.example.fragment.FragmentA;
import com.example.fragment.FragmentA.TextChangeListener;
import com.example.fragment.FragmentB;

public class ActivityAB extends FragmentActivity {

FragmentA fragmentA;
FragmentB fragmentB;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ab);

FragmentManager manager = getSupportFragmentManager();
fragmentA = (FragmentA) manager.findFragmentById(R.id.fragmentA);
fragmentB = (FragmentB) manager.findFragmentById(R.id.fragmentB);

fragmentA.setTextChangeListener(new TextChangeListener() {

@Override
public void onTextChange(CharSequence newText) {
fragmentB.updateTextValue(newText);
}
});
}

}

Here is Fragment A, that has custom listener for text change event.

package com.example.fragment;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import com.example.R;

public class FragmentA extends Fragment {

TextChangeListener listener;

public interface TextChangeListener {
public void onTextChange(CharSequence newText);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container, false);

Button btn = (Button) view.findViewById(R.id.button1);
final TextView textView = (TextView) view.findViewById(R.id.textView1);

btn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if (null != listener) {
listener.onTextChange(textView.getText());
}

}
});
return view;
}

public void setTextChangeListener(TextChangeListener listener) {
this.listener = listener;
}
}

Here is Fragment B that has public method to update text field:

package com.example.fragment;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.example.R;

public class FragmentB extends Fragment {

TextView textView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_b, container, false);
textView = (TextView) view.findViewById(R.id.textView1);
return view;
}

public void updateTextValue(CharSequence newText) {
textView.setText(newText);
}
}

ActivityAB xml Layout:

<?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:baselineAligned="false"
android:orientation="horizontal" >

<fragment
android:id="@+id/fragmentA"
android:name="com.example.fragment.FragmentA"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />

<fragment
android:id="@+id/fragmentB"
android:name="com.example.fragment.FragmentB"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />

</LinearLayout>

Fragment A xml layout:

<?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" >

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is text"
android:textAppearance="?android:attr/textAppearanceLarge" />

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show" />

</LinearLayout>

Fragment B xml layout:

<?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" >

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="(here will be text)"
android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>

Communication between Fragments without Using Interface

Have a look at the Android deverlopers page: http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface

Basically, you define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview with the received value

  // You Activity implements your interface
public class YourActivity implements FragmentA.TextClicked{
@Override
public void sendText(String text){
// Get Fragment B
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}


// Fragment A defines an Interface, and calls the method when needed
public class FragA extends Fragment{

TextClicked mCallback;

public interface TextClicked{
public void sendText(String text);
}

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);

// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}

public void someMethod(){
mCallback.sendText("YOUR TEXT");
}

@Override
public void onDetach() {
mCallback = null; // => avoid leaking, thanks @Deepscorn
super.onDetach();
}
}

// Fragment B has a public method to do something with the text
public class FragB extends Fragment{

public void updateText(String text){
// Here you have it
}
}

Proper way of communication between fragments

According to android developer guide, communication between fragments is done through the associated Activity.

A fragment use his interface to communicate with the Activity. And the Activity deliver a message by capturing the Fragment instance with findFragmentById() or creating one if needed, then directly call the other fragment's public methods.

  • Fragment1 wants to pass some data: uses his interface method implemented by Activity.
  • Activity executes that method receiving the data, create&replace or find (depending on your layout) the Fragment2 and pass this data or execute some public method on fragment2 class (depending on your layout).
  • Fragment2 extract data from bundle or execute (depending on your layout) his public method to receive the data.

I think the problem in your code is you are misunderstanding interface purpose. You are using for start the same fragment who is calling the method. Fragment1 is calling startFragment1() in his onCreateView(), but it is already started.

If you needed, in here there is a good tutorial.

Kotlin: communication between different fragments

You can create a shared ViewModel to communicate between fragments. Create a ViewModel and access them using the hosting Activity's context inside each of the fragments.

Here is an example copied from the ViewModel documentation here: https://developer.android.com/topic/libraries/architecture/viewmodel

class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()

fun select(item: Item) {
selected.value = item
}
}

class MasterFragment : Fragment() {

private lateinit var itemSelector: Selector

private lateinit var model: SharedViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this).get(SharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")

itemSelector.setOnClickListener { item ->
model.select(item) // <-- This will notify the `DetailFragment`
}
}
}

class DetailFragment : Fragment() {

private lateinit var model: SharedViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this).get(SharedViewModel::class.java)
} ?: throw Exception("Invalid Activity")

model.selected.observe(this, Observer<Item> { item ->
// Update the UI
})
}
}

Here, the SharedViewModel is accessed in both MasterFragment and the DetailFragment. Both are accessing the same instance of the SharedViewModel because both of them access the ViewModel from the Activity's Context:

ViewModelProviders.of(*ACTIVITY*).get(SharedViewModel::class.java)

Now you can include some LiveData in the SharedViewModel and both fragments can listen / update them, which is eventually reflected on the other fragment as well.



Related Topics



Leave a reply



Submit