How to add a Fragment inside a ViewPager using Nested Fragment (Android 4.2)

AndroidAndroid FragmentsAndroid ViewpagerAndroid Support-LibraryAndroid Nested-Fragment

Android Problem Overview


I have a ViewPager with three Fragments, each one shows a List (or Grid).

In the new Android API level 17 (Jelly Bean 4.2), one of the features is Nested Fragments. The new functionality description says:

> if you use ViewPager to create fragments that swipe left and right and > consume a majority of the screen space, you can now insert fragments > into each fragment page.

So, if I understand right, now I can create a ViewPager with Fragments (with a button inside for example) inside, and when user press the button show another Fragment without loose the ViewPager using this new feature.

I've expended my morning trying to implement this several different ways, but I can´t get it working... Can somebody add a simple example of how to implement this?

PS: I'm only interested in doing at this way, with getChildFragmentManager to learn how works.

Android Solutions


Solution 1 - Android

Assuming you have created the correct xml layouts. It is now very simple to display fragments in a ViewPager that is hosted by another Fragment.

The following is a parent fragment that displays child fragments:

class ParentViewPagerFragment : Fragment() {

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val root = inflater.inflate(R.layout.fragment_parent_viewpager, container, false)

    val viewPager = root.findViewById(R.id.viewPager) as ViewPager
    // Important: Must use the child FragmentManager or you will see side effects.
    viewPager.adapter = MyAdapter(childFragmentManager)

    val tabStrip = root.findViewById<TabLayout>(R.id.pagerTabStrip)
    tabStrip.setupWithViewPager(viewPager)

    return root
  }

  class MyAdapter internal constructor(fm: FragmentManager) : FragmentPagerAdapter(fm) {

    override fun getCount(): Int = 4

    override fun getItem(position: Int): Fragment {
      val args = Bundle().apply { putInt(ChildFragment.POSITION_KEY, position) }
      return ChildFragment.newInstance(args)
    }

    override fun getPageTitle(position: Int): CharSequence = "Tab $position"
  }

  companion object {
    val TAG: String = ParentViewPagerFragment::class.java.name
  }
}

It is important to use Fragment.getChildFragmentManager() when instantiating the FragmentPagerAdapter. Also note that you cannot use Fragment.setRetainInstance() on the children fragments or you'll get an exception. The imports were omitted for brevity.

Source code can be found at: https://github.com/marcoRS/nested-fragments

Solution 2 - Android

Edit: If you want to replace all the content of a page in a ViewPager you could still use nested fragments, but some changes are needed. Check the sample below(the FragmentActivity, setting the ViewPager and the PagerAdapter are the same as the previous snippet of code):

// this will act as a fragment container, representing one page in the ViewPager
public static class WrapperFragment extends Fragment implements
		ReplaceListener {

	public static WrapperFragment newInstance(int position) {
		WrapperFragment wp = new WrapperFragment();
		Bundle args = new Bundle();
		args.putInt("position", position);
		wp.setArguments(args);
		return wp;
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		FrameLayout fl = new FrameLayout(getActivity());
		fl.setId(10000);
		if (getChildFragmentManager().findFragmentByTag("initialTag") == null) {
			InitialInnerFragment iif = new InitialInnerFragment();
			Bundle args = new Bundle();
			args.putInt("position", getArguments().getInt("position"));
			iif.setArguments(args);
			getChildFragmentManager().beginTransaction()
					.add(10000, iif, "initialTag").commit();
		}
		return fl;
	}

    // required because it seems the getChildFragmentManager only "sees"
    // containers in the View of the parent Fragment.   
	@Override
	public void onReplace(Bundle args) {
		if (getChildFragmentManager().findFragmentByTag("afterTag") == null) {
			InnerFragment iif = new InnerFragment();
			iif.setArguments(args);
			getChildFragmentManager().beginTransaction()
					.replace(10000, iif, "afterTag").addToBackStack(null)
					.commit();
		}
	}

}

// the fragment that would initially be in the wrapper fragment
public static class InitialInnerFragment extends Fragment {

	private ReplaceListener mListener;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		mListener = (ReplaceListener) this.getParentFragment();
		LinearLayout ll = new LinearLayout(getActivity());
		Button b = new Button(getActivity());
		b.setGravity(Gravity.CENTER_HORIZONTAL);
		b.setText("Frame " + getArguments().getInt("position"));
		b.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Bundle args = new Bundle();
				args.putInt("positionInner",
						getArguments().getInt("position"));
				if (mListener != null) {
					mListener.onReplace(args);
				}
			}
		});
		ll.setOrientation(LinearLayout.VERTICAL);
		ll.addView(b, new LinearLayout.LayoutParams(250,
				LinearLayout.LayoutParams.WRAP_CONTENT));
		return ll;
	}

}

public static class InnerFragment extends Fragment {

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		TextView tv = new TextView(getActivity());
		tv.setText("InnerFragment in the outher Fragment with position "
				+ getArguments().getInt("positionInner"));
		return tv;
	}

}

public interface ReplaceListener {
	void onReplace(Bundle args);
}

At a quick look it works, but issues may appear as I haven't tested it to much.

> Can somebody show a simple example of how to do this?

Using nested fragments seems pretty easy, until Commonsware comes with a more elaborated sample you can try the code below:

public class NestedFragments extends FragmentActivity {

	@Override
	protected void onCreate(Bundle arg0) {
		super.onCreate(arg0);
		ViewPager vp = new ViewPager(this);
		vp.setId(5000);
		vp.setAdapter(new MyAdapter(getSupportFragmentManager()));
		setContentView(vp);
	}

	private static class MyAdapter extends FragmentPagerAdapter {

		public MyAdapter(FragmentManager fm) {
			super(fm);
		}

		@Override
		public Fragment getItem(int position) {
			return WrapperFragment.newInstance(position);
		}

		@Override
		public int getCount() {
			return 8;
		}
	}

	public static class WrapperFragment extends Fragment {

		public static WrapperFragment newInstance(int position) {
			WrapperFragment wp = new WrapperFragment();
			Bundle args = new Bundle();
			args.putInt("position", position);
			wp.setArguments(args);
			return wp;
		}

		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			LinearLayout ll = new LinearLayout(getActivity());
			FrameLayout innerFragContainer = new FrameLayout(getActivity());
			innerFragContainer.setId(1111);
			Button b = new Button(getActivity());
			b.setText("Frame " + getArguments().getInt("position"));
			b.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					InnerFragment innerFragment = new InnerFragment();
					Bundle args = new Bundle();
					args.putInt("positionInner",
							getArguments().getInt("position"));
					innerFragment.setArguments(args);
					FragmentTransaction transaction = getChildFragmentManager()
							.beginTransaction();
					transaction.add(1111, innerFragment).commit();
				}
			});
			ll.setOrientation(LinearLayout.VERTICAL);
			ll.addView(b, new LinearLayout.LayoutParams(
					LinearLayout.LayoutParams.MATCH_PARENT,
					LinearLayout.LayoutParams.WRAP_CONTENT));
			ll.addView(innerFragContainer, new LinearLayout.LayoutParams(
					LinearLayout.LayoutParams.MATCH_PARENT,
					LinearLayout.LayoutParams.MATCH_PARENT));
			return ll;
		}

	}

	public static class InnerFragment extends Fragment {

		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			TextView tv = new TextView(getActivity());
			tv.setText("InnerFragment in the outher Fragment with position "
					+ getArguments().getInt("positionInner"));
			return tv;
		}

	}

}

I was lazy and made everything in code but I'm sure it can work with inflated xml layouts.

Solution 3 - Android

I have created a ViewPager with 3 elements and 2 sub elements for index 2 and 3 and here what I wanted to do..

enter image description here

I have implemented this with the help from previous questions and answers from StackOverFlow and here is the link.

ViewPagerChildFragments

Solution 4 - Android

Easily use Navigation Component in your app! then in your listener, copy this code:

findNavController().navigate('Your action Id)
  • Your action id which IDE created automatically for you.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsabadowView Question on Stackoverflow
Solution 1 - AndroidMarco RSView Answer on Stackoverflow
Solution 2 - AndroiduserView Answer on Stackoverflow
Solution 3 - AndroidRukmal DiasView Answer on Stackoverflow
Solution 4 - AndroidParsamlmView Answer on Stackoverflow