I recently asked this question on Stack Overflow, but now that I have a working example, I would like to have it checked.
I set up the layout like this:
I am going off the idea that parents can talk directly to their children, but children should use an interface to talk to parents (docs). The docs where not clear, though, about communication between nested fragments. That is why I made this project.
MainActivity.java
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);
}
}
ParentFragment.java
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;
}
}
ChildFragment.java
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(context.toString()
+ " 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;
}
}
What I am concerned about
- Is my design principle for (1) Child Fragment to Parent Fragment communication and (2) Child Fragment to Activity communication valid? Is it a "best practice"?
Is this an appropriate way for the Child Fragment to get a reference to the Parent Fragment?
// check if parent Fragment implements listener if (getParentFragment() instanceof OnChildFragmentInteractionListener) { mParentListener = (OnChildFragmentInteractionListener) getParentFragment(); } else { throw new RuntimeException(context.toString() + " must implement OnChildFragmentInteractionListener"); }