两个片段之间的基本通信

我有一个活动 MainActivity。在这个活动中,我有两个片段,它们都是在 xml 中以声明方式创建的。

我试图传递的 String的文本输入由用户到 Fragment A的文本视图在 Fragment B。然而,这被证明是非常困难的。有人知道我是怎么做到的吗?

我知道片段可以使用 getActivity()获得对其活动的引用。我猜我会从那里开始?

127047 次浏览

更新

忽略这个答案。不是说它不管用。但是有更好的方法。此外,Android 强调不鼓励片段之间的直接通信。参见 官方文件。感谢用户@Wahib Ul Haq 的提示。

原始答案

你可以在片段 B 中创建一个私有变量和 setter 然后从片段 A 本身设置值,

片段 B.java

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


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

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 开发者页面: Http://developer.android.com/training/basics/fragments/communicating.html#defineinterface

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

您的活动实现您的接口(参见下面的片段 A)

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

考虑我的2个片段 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");
}


}


}

碎片 B 看起来像

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


}

我们来看看这个 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/

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

这里有一个例子。单击 DialogFragment上的 TextView触发对 onTextClicked中的 MainActivity的回调,然后获取与之交互的 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());
}
}

}

其他一些示例(甚至在撰写本文时的 文件)使用过时的 onAttach方法。下面是一个完整的更新示例。

enter image description here

笔记

  • 您不希望片段直接相互对话或与活动对话。这将它们与特定的活动联系起来,并使得重用变得困难。
  • 解决方案是创建一个 Activity 将实现的回调侦听器接口。当片段想要向另一个片段或其父活动发送消息时,它可以通过接口来完成。
  • 活动可以直接与其子片段公共方法进行通信。
  • 因此 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);
}
}

绿色碎片

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>

片段 _ 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>

片段 _ 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>

最好和推荐的方法是使用共享的 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 : 两个片段从不直接交流

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

通信中涉及的片段需要使用与活动生命周期相关联的相同视图模型对象。视图模型对象包含 LiveData 对象,数据通过一个片段传递给这个对象,第二个片段监听 LiveData 上的更改,并接收从片段1发送的数据。

完整的例子见 http://www.zoftino.com/passing-data-between-android-fragments-using-viewmodel

学习“ setTargetFragment ()”

在“ startActivityForResult ()”建立2个活动之间关系的地方,“ setTargetFragment ()”定义2个片段之间的调用方/被调用方关系。

我为我的活动提供了一个接口,所有的片段都可以使用它。如果在同一个活动中有许多片段,这将节省大量的代码重写工作,并且比为每个片段制作具有相似功能的单独接口更清晰、更模块化。我也喜欢它的模块化。缺点是,有些片段可以访问它们不需要的函数。

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

片段之间有多种通信方式。

  • 传统的交流方式是通过 interface 例子
  • 通过 ViewModel,如果您遵循 MVVM 模式 例子
  • 广播接收机: 通过 LocalBraodcastManager 例子EventBus 例子等。

您可以使用2方法在2个 fragments之间进行通信:

1)

您可以使用 LiveData来观察一个 fragment在另一个 fragment中的数据更改

创建共享的 ViewModel

public class SharedViewModel extends ViewModel {


private MutableLiveData<String> name;


public void setNameData(String nameData) {
name.setValue(nameData);
}


public MutableLiveData<String> getNameData() {
if (name == null) {
name = new MutableLiveData<>();
}


return name;
}
}

碎片一

 private SharedViewModel sharedViewModel;


public FragmentOne() {


}


@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
submitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {


sharedViewModel.setNameData(submitText.getText().toString());
}
});


}

碎片二

 private SharedViewModel sharedViewModel;


public FragmentTwo() {


}


@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);


sharedViewModel.getNameData().observe(this, nameObserver);
}


Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(String name) {
receivedText.setText(name);
}
};

有关视图模型的详细信息,请参阅: Mvvm-viewmodel-livedata交流片段

2)

您可以使用 大事件巴士来实现相同的目标

implementation 'org.greenrobot:eventbus:3.2'

定义事件

public static class MessageEvent { /* Additional fields if needed */ }

注册/取消注册用户

 @Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}


@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}

倾听事件

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};

邮递活动

 EventBus.getDefault().post(new MessageEvent());

自从片段1.3.0以来,我们有了一种新的方式来在片段之间进行通信。

从片段1.3.0开始,每个片段管理器都实现了片段结果所有者。 这意味着一个片段管理器可以充当片段结果的中央存储器。此更改允许组件通过设置块结果并监听这些结果来相互通信,而不需要这些组件彼此之间有直接引用。

片段听众:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResultListener("requestKey") { requestKey, bundle ->
// We use a String here, but any type that can be put in a Bundle is supported
val result = bundle.getString("bundleKey")
// Do something with the result
}
}

碎片发射器:

button.setOnClickListener {
val result = "result"
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}

基本上,两个部分之间的交流方式如下:

i) ViewModel
ii) Fragment Result API
iii) Interface