按照 Rahul Sharma 在 cmets 中的建议,我使用接口回调从子片段到父片段和 Activity 进行通信。我也submitted this answer to Code Review。我认为那里没有答案(在撰写本文时)表明这种设计模式没有重大问题。在我看来,这与官方fragment communication docs 中给出的一般指导一致。
示例项目
以下示例项目扩展了问题中给出的示例。它具有启动从 Fragment 到 Activity 以及从 Child Fragment 到 Parent Fragment 的向上通信的按钮。
我这样设置项目布局:
主要活动
Activity 实现了两个片段的侦听器,以便可以从它们获取消息。
可选 TODO:如果 Activity 想要启动与 Fragment 的通信,它可以直接获取对它们的引用,然后调用它们的公共方法之一。
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:如果父 Fragment 想要启动与子 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;
}
}