我最近学会了how to make nested fragments in Android。但是,我不知道应该如何进行沟通。
从阅读fragment communication documentation我知道
所有片段到片段的通信都是通过关联的方式完成的 活动。两个碎片永远不应该直接沟通。
这对于活动中的兄弟片段是有意义的,但它对于父子片段通信没有多大意义。我是否需要一直到活动只是为了让Child Fragment与Parent Fragment交谈?如果答案是简单的"是"那我就能做到。如果它是" no",那么代码设计会是什么样子?
我在Nested Fragment documentation中看到可以使用getParentFragment()
来获取对父片段的引用。这是否意味着孩子应该直接与父母沟通?这似乎与正常片段与父活动沟通所鼓励的内容相反。
答案 0 :(得分:26)
根据Rahul Sharma在评论中的建议,我使用接口回调从Child Fragment到Parent Fragment和Activity进行通信。我也是submitted this answer to Code Review。我在那里(在撰写本文时)没有回答是这个设计模式没有重大问题的迹象。在我看来,这与官方fragment communication docs中给出的一般指导是一致的。
以下示例项目展开了问题中给出的示例。它具有按钮,用于启动从片段到活动以及从子片段到父片段的向上通信。
我设置了这样的项目布局:
Activity从两个片段实现侦听器,以便它可以从它们获取消息。
可选TODO:如果活动想要启动与片段的通信,它可以直接引用它们,然后调用它们的一个公共方法。
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnChildFragmentToActivityInteractionListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.parent_fragment_container, new ParentFragment());
ft.commit();
}
@Override
public void messageFromParentFragmentToActivity(String myString) {
Log.i("TAG", myString);
}
@Override
public void messageFromChildFragmentToActivity(String myString) {
Log.i("TAG", myString);
}
}
Parent Fragment从Child Fragment实现侦听器,以便它可以从中接收消息。
可选TODO:如果Parent Fragment想要启动与Child Fragment的通信,它可以直接引用它,然后调用它的一个公共方法。
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ParentFragment extends Fragment implements View.OnClickListener, ChildFragment.OnChildFragmentInteractionListener {
// **************** start interesting part ************************
private OnFragmentInteractionListener mListener;
@Override
public void onClick(View v) {
mListener.messageFromParentFragmentToActivity("I am the parent fragment.");
}
@Override
public void messageFromChildToParent(String myString) {
Log.i("TAG", myString);
}
public interface OnFragmentInteractionListener {
void messageFromParentFragmentToActivity(String myString);
}
// **************** end interesting part ************************
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_parent, container, false);
view.findViewById(R.id.parent_fragment_button).setOnClickListener(this);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
Fragment childFragment = new ChildFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.child_fragment_container, childFragment).commit();
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
}
Child Fragment为Activity和Parent Fragment定义了监听器接口。如果Child Fragment只需要与其中一个进行通信,那么可以删除另一个接口。
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ChildFragment extends Fragment implements View.OnClickListener {
// **************** start interesting part ************************
private OnChildFragmentToActivityInteractionListener mActivityListener;
private OnChildFragmentInteractionListener mParentListener;
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.child_fragment_contact_activity_button:
mActivityListener.messageFromChildFragmentToActivity("Hello, Activity. I am the child fragment.");
break;
case R.id.child_fragment_contact_parent_button:
mParentListener.messageFromChildToParent("Hello, parent. I am your child.");
break;
}
}
public interface OnChildFragmentToActivityInteractionListener {
void messageFromChildFragmentToActivity(String myString);
}
public interface OnChildFragmentInteractionListener {
void messageFromChildToParent(String myString);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// check if Activity implements listener
if (context instanceof OnChildFragmentToActivityInteractionListener) {
mActivityListener = (OnChildFragmentToActivityInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnChildFragmentToActivityInteractionListener");
}
// check if parent Fragment implements listener
if (getParentFragment() instanceof OnChildFragmentInteractionListener) {
mParentListener = (OnChildFragmentInteractionListener) getParentFragment();
} else {
throw new RuntimeException("The parent fragment must implement OnChildFragmentInteractionListener");
}
}
// **************** end interesting part ************************
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_child, container, false);
view.findViewById(R.id.child_fragment_contact_activity_button).setOnClickListener(this);
view.findViewById(R.id.child_fragment_contact_parent_button).setOnClickListener(this);
return view;
}
@Override
public void onDetach() {
super.onDetach();
mActivityListener = null;
mParentListener = null;
}
}
答案 1 :(得分:4)
虽然@Suragch的答案是正确的,但我想添加另一种方法来在Fragments
或Activity
之间传递数据。无论是Activity
还是Fragment
,您都可以通过3个步骤传递事件总线数据:
1-定义一个事件(消息):
public class OrderMessage {
private final long orderId;
/* Additional fields if needed */
public OrderMessage(long orderId) {
this.orderId = orderId;
}
public long getOrderId() {
return orderId;
}
}
2-注册和取消注册事件:为了能够接收事件,该类必须注册/取消注册事件总线。 Activities
和Fragments
的最佳位置是onStart()
和onStop()
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
为了能够接收您必须订阅该活动的活动。为此,请将@Subscribe
注释添加到您班级中的某个方法中。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(OrderMessage message){
/* Do something for example: */
getContractDetails(message.getOrderId());
}
3-发布活动
EventBus.getDefault().post(new OrderMessage(recievedDataFromWeb.getOrderId()));
答案 2 :(得分:1)
随着体系结构组件的发布,您可能应该看看viewmodel architecture component。
与实时数据结合,您将能够轻松地在任意嵌套的片段之间进行通信。
您可能还需要研究todoapp以及它们如何处理那里的事件。
答案 3 :(得分:1)
使用ViewModel在嵌套片段之间进行通信
现在已弃用旧的ViewModel和LiveData。我想提供有关此过程的信息。
在此示例中,我使用的是预定义的TabLayout,可以在创建项目时选择它。
在这里,我在父片段(选项卡)中有一个TextView,在子片段(自定义布局)中有一个EditText和一个Button。它将在子片段中输入的文本传递到父片段中的字段。
我希望这对某人有帮助。
ViewModel类
public class FragViewModel extends ViewModel {
private MutableLiveData<CharSequence> digit = new MutableLiveData<>();
public void insertDigit(CharSequence inDigit){
digit.setValue(inDigit);
}
public LiveData<CharSequence> getDigit(){
return digit;
}
}
在大多数情况下,这是来自Android开发人员ViewModel Overview的逐字记录,只是对变量名进行了少量更改。概述被用作以下父代和子代代码的指南。
父Fragement代码
public class Tab0Main extends Fragment {
private FragViewModel model0;
// This is the Child Fragment.
private ChildFrag childFrag;
private TextView textView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tab0_main, container, false);
// Setup the text field.
textView = view.findViewById(R.id.tab0TextView);
// Inserting the Child Fragment into the FrameLayout.
childFrag = new ChildFrag();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.tab0Frame, childFrag).commit();
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model0 = new ViewModelProvider(getActivity()).get(FragViewModel.class);
model0.getDigit().observe(getViewLifecycleOwner(), new Observer<CharSequence>() {
@Override
public void onChanged(CharSequence charSequence) {
textView.setText(charSequence);
}
});
}
儿童片段代码
public class ChildFrag extends Fragment {
private EditText fragEditText;
private Button fragButton;
// The ViewModel declaration
FragViewModel model;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.child_frag_layout, container, false);
// The ViewModel Instantiation
model = new ViewModelProvider(getActivity()).get(FragViewModel.class);
fragEditText = view.findViewById(R.id.fragEditText);
fragButton = view.findViewById(R.id.fragButton);
fragButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CharSequence in = fragEditText.getText();
// Inserting a digit into the ViewModel carrier.
model.insertDigit(in);
}
});
return view;
}
}