我有一项活动-MainActivity。在此活动中,我有两个片段,我在xml中声明性地创建了这两个片段。

我正在尝试将用户输入的文本String传递到Fragment A中的Fragment B的文本视图中。然而,事实证明这非常困难。有谁知道我将如何实现这一目标?

我知道片段可以使用getActivity()对其活动进行引用。所以我猜我会从那里开始吗?

#1 楼

看看Android开发人员页面:
http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface

基本上,您可以在自己的接口中定义一个接口片段A,让您的Activity实现该接口。现在,您可以在Fragment中调用接口方法,您的Activity将接收该事件。现在,在您的活动中,您可以调用第二个Fragment以接收的值更新textview。

您的Activity实现您的接口(请参见下面的FragmentA)

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


片段A定义了一个接口,并在需要时调用该方法

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


片段B有一个公共方法来处理文本

public class FragB extends Fragment{

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


评论


在这种情况下,片段a向片段b发送了一些字符串,片段b向片段a发送了一些结果,两个片段必须在活动中进行通信吗?

– Yohanes AI
2014-09-19 15:06



@Entreco当我在片段B中使用getactivity时,我得到了一个空指针异常。在FragB中,即使textview也为null。有什么建议?

–hemanth kumar
2014-09-22 4:16



在这种情况下,@ hemanthkumar您可能尚未将片段B添加到活动中。您可以通过将其包含到活动布局文件中,或者在活动中使用FragmentManager并将其添加到其中来完成此操作。祝好运

– Entreco
2014年9月22日在7:48

@Entreco,“为什么不直接实现宿主Activity的方法而不是实现接口”。因为在使用接口时,您的代码将更加通用(因此,如果将来有其他活动需要编组事件,则不得更新FragmentA的代码)

–深水corn
2015年9月6日7:06

@Entreco,您的答案+1,但您在此处泄漏了活动。不要忘记:onDetach(){mCallback = null; }

–深水corn
2015年9月6日于7:07

#2 楼

其他一些示例(甚至是撰写本文时的文档)都使用了过时的onAttach方法。这是完整的更新示例。



注意事项


您不希望片段直接彼此交谈或活动。将它们绑定到特定的Activity上,使重用变得困难。
解决方案是创建一个Activity将实现的回调侦听器接口。当Fragment想要向另一个Fragment或其父活动发送消息时,可以通过该接口发送消息。
Activity可以直接与其子片段公共方法进行通信。
活动充当控制器,将消息从一个片段传递到另一个。

代码

MainActivity.java

public class MainActivity extends AppCompatActivity implements GreenFragment.OnGreenFragmentListener {

    private static final String BLUE_TAG = "blue";
    private static final String GREEN_TAG = "green";
    BlueFragment mBlueFragment;
    GreenFragment mGreenFragment;

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

        // add fragments
        FragmentManager fragmentManager = getSupportFragmentManager();

        mBlueFragment = (BlueFragment) fragmentManager.findFragmentByTag(BLUE_TAG);
        if (mBlueFragment == null) {
            mBlueFragment = new BlueFragment();
            fragmentManager.beginTransaction().add(R.id.blue_fragment_container, mBlueFragment, BLUE_TAG).commit();
        }

        mGreenFragment = (GreenFragment) fragmentManager.findFragmentByTag(GREEN_TAG);
        if (mGreenFragment == null) {
            mGreenFragment = new GreenFragment();
            fragmentManager.beginTransaction().add(R.id.green_fragment_container, mGreenFragment, GREEN_TAG).commit();
        }
    }

    // The Activity handles receiving a message from one Fragment
    // and passing it on to the other Fragment
    @Override
    public void messageFromGreenFragment(String message) {
        mBlueFragment.youveGotMail(message);
    }
}


GreenFragment.java

public class GreenFragment extends Fragment {

    private OnGreenFragmentListener mCallback;

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

        Button button = v.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String message = "Hello, Blue! I'm Green.";
                mCallback.messageFromGreenFragment(message);
            }
        });

        return v;
    }

    // This is the interface that the Activity will implement
    // so that this Fragment can communicate with the Activity.
    public interface OnGreenFragmentListener {
        void messageFromGreenFragment(String text);
    }

    // This method insures that the Activity has actually implemented our
    // listener and that it isn't null.
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnGreenFragmentListener) {
            mCallback = (OnGreenFragmentListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnGreenFragmentListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallback = null;
    }
}


BlueFragment.java

public class BlueFragment extends Fragment {

    private TextView mTextView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_blue, container, false);
        mTextView = v.findViewById(R.id.textview);
        return v;
    }

    // This is a public method that the Activity can use to communicate
    // directly with this Fragment
    public void youveGotMail(String message) {
        mTextView.setText(message);
    }
}


XML

activity_main.xml

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

    <!-- Green Fragment container -->
    <FrameLayout
        android:id="@+id/green_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginBottom="16dp" />

    <!-- Blue Fragment container -->
    <FrameLayout
        android:id="@+id/blue_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>


fragment_green.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:background="#98e8ba"
              android:padding="8dp"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:text="send message to blue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>


fragment_blue.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:background="#30c9fb"
              android:padding="16dp"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:id="@+id/textview"
        android:text="TextView"
        android:textSize="24sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>


评论


如果需要通过两种方式在Activity和fragment之间进行通信,则可以使用Create和Java接口文件来代替在Activity或fragment中声明。

– nobjta_9x_tq
18-10-30在8:46

#3 楼

最好的推荐方法是使用共享的ViewModel。

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

摘自Google文档:

public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

public void select(Item item) {
    selected.setValue(item);
}

public LiveData<Item> getSelected() {
    return selected;
}
}


public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
    itemSelector.setOnClickListener(item -> {
        model.select(item);
    });
}
}


public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
    model.getSelected().observe(this, { item ->
       // Update the UI.
    });
}
}


ps:两个片段永远不会直接通信

评论


我尝试了此操作,但是按照您的示例,DetailFragment无法观察到更改。 model.select(项目);方法被称为

– Kishan Solanki
5月28日15:40

#4 楼

考虑我的两个片段A和B,并假设我需要将数据从B传递到A。

然后在B中创建一个接口,并将数据传递到Main Activity。在那里创建另一个接口,并将数据传递给片段A。

分享一个小示例:

片段A看起来像

public class FragmentA extends Fragment implements InterfaceDataCommunicatorFromActivity {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
String data;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void updateData(String data) {
    // TODO Auto-generated method stub
    this.data = data;
    //data is updated here which is from fragment B
}

@Override
public void onAttach(Activity activity) {
    // TODO Auto-generated method stub
    super.onAttach(activity);
    try {
        interfaceDataCommunicatorFromActivity = (InterfaceDataCommunicatorFromActivity) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement TextClicked");
    }

}

}


FragmentB看起来像

class FragmentB extends Fragment {
public InterfaceDataCommunicator interfaceDataCommunicator;

@Override
public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);

    // call this inorder to send Data to interface
    interfaceDataCommunicator.updateData("data");
}

public interface InterfaceDataCommunicator {
    public void updateData(String data);
}

@Override
public void onAttach(Activity activity) {
    // TODO Auto-generated method stub
    super.onAttach(activity);
    try {
        interfaceDataCommunicator = (InterfaceDataCommunicator) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement TextClicked");
    }

}

}


主要活动是

public class MainActivity extends Activity implements InterfaceDataCommunicator {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;

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

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public void updateData(String data) {
    // TODO Auto-generated method stub
    interfaceDataCommunicatorFromActivity.updateData(data);

}

public interface InterfaceDataCommunicatorFromActivity {
    public void updateData(String data);
}

}


评论


与FragmentA和MainActivity相比,为什么FragmentB的变量有所不同? FragmentB是InterfaceDataCommunicator,但对于FragmentA和MainActivity是InterfaceDataCommunicatorFromActivity。

–阿里顿·奥利维拉(Aliton Oliveira)
19年1月30日,0:21

#5 楼

看看https://github.com/greenrobot/EventBus
或http://square.github.io/otto/

甚至... http:// nerds .weddingpartyapp.com / tech / 2014/12/24 / implementing-an-event-bus-with-rxjava-rxbus /

评论


对于这种非常简单的情况,Roboguice是不必要的额外开销。

–阿里·阿扎尔(Ali Azhar)
16年1月23日在19:34

#6 楼

有一种使用架构组件在活动片段之间实现通信的简单方法。可以使用ViewModel和LiveData在活动的各个片段之间传递数据。

通信中涉及的片段需要使用与活动生命周期相关的相同视图模型对象。视图模型对象包含livedata对象,一个片段将数据传递给该对象,第二个片段侦听LiveData上的更改并接收从片段1发送的数据。完整示例请参见http:// www .zoftino.com / passing-data-between-android-fragments-using-viewmodel

#7 楼

了解“ setTargetFragment()”

其中“ startActivityForResult()”建立2个活动之间的关系,“ setTargetFragment()”定义2个片段之间的调用者/被叫关系。

#8 楼

我给我的活动一个接口,然后所有片段都可以使用。如果您在同一活动中有很多片段,那么与为具有相似功能的每个片段创建单独的接口相比,这可以节省大量代码重写,并且是一种更简洁的解决方案/更模块化。我也喜欢它是模块化的。不利的一面是,某些片段将可以访问它们不需要的功能。

    public class MyActivity extends AppCompatActivity
    implements MyActivityInterface {

        private List<String> mData; 

        @Override
        public List<String> getData(){return mData;}

        @Override
        public void setData(List<String> data){mData = data;}
    }


    public interface MyActivityInterface {

        List<String> getData(); 
        void setData(List<String> data);
    }

    public class MyFragment extends Fragment {

         private MyActivityInterface mActivity; 
         private List<String> activityData; 

         public void onButtonPress(){
              activityData = mActivity.getData()
         }

        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            if (context instanceof MyActivityInterface) {
                mActivity = (MyActivityInterface) context;
            } else {
                throw new RuntimeException(context.toString()
                        + " must implement MyActivityInterface");
            }
        }

        @Override
        public void onDetach() {
            super.onDetach();
            mActivity = null;
        }
    } 


#9 楼

更新

忽略此答案。不是说它不起作用。但是有更好的方法可用。此外,Android着重阻止片段之间的直接通信。见官方文件。感谢用户@Wahib Ul Haq的提示。

原始答案

好吧,您可以在Fragment B中创建一个私有变量和设置器,并从Fragment A本身设置值,

FragmentB .java

private String inputString;
....
....

public void setInputString(String string){
   inputString = string;
}


FragmentA.java

//go to fragment B

FragmentB frag  = new FragmentB();
frag.setInputString(YOUR_STRING);
//create your fragment transaction object, set animation etc
fragTrans.replace(ITS_ARGUMENTS)


也可以按照所建议的方式使用Activity。 。

评论


现在,您应该这样做,因为它违反了模块化的基本概念。 Android文档说:>两个片段永远不要直接通信。碎片之间应该彼此意识,独立且独立工作。

–Wahib Ul Haq
17年4月12日在15:25

#10 楼

我最近创建了一个库,该库使用注释为您生成这些类型转换样板代码。
https://github.com/zeroarst/callbackfragment

这里是一个示例。单击TextView上的DialogFragment会触发对MainActivity中的onTextClicked的回调,然后获取与MyFagment实例进行交互。

public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback, MyDialogFragment.DialogListener {

private static final String MY_FRAGM = "MY_FRAGMENT";
private static final String MY_DIALOG_FRAGM = "MY_DIALOG_FRAGMENT";

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

    getSupportFragmentManager().beginTransaction()
        .add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), MY_FRAGM)
        .commit();

    findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            MyDialogFragmentCallbackable.create().show(getSupportFragmentManager(), MY_DIALOG_FRAGM);
        }
    });
}

Toast mToast;

@Override
public void onClickButton(MyFragment fragment) {
    if (mToast != null)
        mToast.cancel();
    mToast = Toast.makeText(this, "Callback from " + fragment.getTag() + " to " + this.getClass().getSimpleName(), Toast.LENGTH_SHORT);
    mToast.show();
}

@Override
public void onTextClicked(MyDialogFragment fragment) {
    MyFragment myFragm = (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGM);
    if (myFragm != null) {
        myFragm.updateText("Callback from " + fragment.getTag() + " to " + myFragm.getTag());
    }
}


}