Android: Can You Nest Fragments

Android: Can you nest Fragments?

Finally! the android 4.2 update comes with default support for nested fragments, also compatible:
Official NestedFragments. What's more, the support of the nested Fragments in announced in the latest 11th revision of the v4 support library!

Best practice for nested fragments in Android 4.0, 4.1 ( 4.2) without using the support library

Limitations

So nesting fragments inside another fragment is not possible with xml regardless of which version of FragmentManager you use.

So you have to add fragments via code, this might seem like a problem, but in the long run makes your layouts superflexible.

So nesting without using getChildFragmentManger? The essence behind childFragmentManager is that it defers loading until the previous fragment transaction has finished. And of course it was only naturally supported in 4.2 or the support library.

Nesting without ChildManager - Solution

Solution, Sure! I have been doing this for a long time now, (since the ViewPager was announced).

See below; This is a Fragment that defers loading, so Fragments can be loaded inside of it.

Its pretty simple, the Handler is a really really handy class, effectively the handler waits for a space to execute on the main thread after the current fragment transaction has finished committing (as fragments interfere with the UI they run on the main thread).

// Remember this is an example, you will need to modify to work with your code
private final Handler handler = new Handler();
private Runnable runPager;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
return inflater.inflate(R.layout.frag_layout, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
runPager = new Runnable() {

@Override
public void run()
{
getFragmentManager().beginTransaction().addFragment(R.id.frag_container, MyFragment.newInstance()).commit();
}
};
handler.post(runPager);
}

/**
* @see android.support.v4.app.Fragment#onPause()
*/
@Override
public void onPause()
{
super.onPause();
handler.removeCallbacks(runPager);
}

I wouldn't consider it 'best practice', but I have live apps using this hack and I am yet to have any issues with it.

I also use this method for embedding view pagers - https://gist.github.com/chrisjenx/3405429

Is Nested Fragment Possible?

Yes, Nested fragment is possible.

You have to use getChildFragmentManager() instead of getFragmentManager() while you are working with nested fragments. For more details look into this link.

Hope this will help you....:)

Android Nested Fragments with a RecyclerView

After a lot of soul searching, I can confirm that it is not possible to generate IDs inside fragments. IF you do so, Android gets confused while rendering the UI and the app crashes.

I have tried this in another project where we generated ids for radio buttons and groups that were dynamically generated and placed within a fragment and same behavior. Two solutions:
- do not create programatically so you don't need to generate ids
- do not use fragments

Communication between nested fragments in Android

Following Rahul Sharma's advice in the comments, I used interface callbacks to communicate up from the Child Fragment to the Parent Fragment and to the Activity. I also submitted this answer to Code Review. I am taking the non-answer there (at the time of this writing) to be a sign that there are no major problems with this design pattern. It seems to me to be consistent with the general guidance given in the official fragment communication docs.

Example project

The following example project expands the example given in the question. It has buttons that initiate upward communication from the fragments to the activity and from the Child Fragment to the Parent Fragment.

I set up the project layout like this:

Sample Image

Main Activity

The Activity implements the listeners from both fragments so that it can get messages from them.

Optional TODO: If the Activity wanted to initiate communication with the fragments, it could just get a direct reference to them and then call one of their public methods.

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

The Parent Fragment implements the listener from the Child Fragment so that it can receive messages from it.

Optional TODO: If the Parent Fragment wanted to initiate communication with the Child Fragment, it could just get a direct reference to it and then call one of its public methods.

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

The Child Fragment defines listener interfaces for both the Activity and for the Parent Fragment. If the Child Fragment only needed to communicate with one of them, then the other interface could be removed.

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

}

Android Nested Fragment and Interface

From Android Developer website, it says:

Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.

To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.

Therefore, when you implement that interface in Fragment B, it throws a RuntimeException error.

Summary: you have to implement that interface in your fragment hosting activity.

You can learn more on how to pass data between fragments thru a hosting activity (usually we make use of Bundle) at http://www.vogella.com/tutorials/AndroidFragments/article.html



Related Topics



Leave a reply



Submit