5
\$\begingroup\$

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:

enter image description here

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");
    } 
    
asked Sep 15, 2016 at 4:50
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I have a few suggestions about your code.

First, the easy one, is that you can follow this "standard" for the onAttach method:

 @Override
 public void onAttach(Activity activity) {
 super.onAttach(activity);
 try {
 mMinInfoToApplyCallback = (MinInfoToApplyCallback) activity;
 } catch (ClassCastException e) {
 throw new ClassCastException(activity.toString() + " must implement MinInfoToApplyCallback");
 }
 }

Why I say this is the standard? Principally because when you make in Android Studio New --> Fragment --> Fragment (Blank) and select the option to communicate with the Activity, this is code generated, with the catch directly instead of checking the instance of.

The second part could have more debate. Before start, remember that a Fragment should be agnostic of the activity is contained, that is the reason we use activity cast in the onAttach, to ensure we can use that fragment in whatever Activity. Saying that, we have to know if the child fragment is agnostic of the parent fragment or is always related to it and we can ensure ChildFragment can't live without the partent.

If the ChildFragment is agnostic to the parent the correct solution should have in the ChildFragment only a listener with the Activity and the responsibility of communicate with the ParentFragment is of the Activity.

If the ChildFragment is always related with the ParentFragment, childFragment should only communicate with it and the Parent pass that "messages" to the activity.

With this approach, that I know is a little more boring, you can assure that every class only has dependencies with the classes that should know and maintain SOLID

answered Dec 29, 2016 at 10:22
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.