web-dev-qa-db-fra.com

Comment ajouter un fragment dans un ViewPager à l'aide d'un fragment imbriqué (Android 4.2)

J'ai un ViewPager avec trois Fragments, chacun montrant un List (ou Grid).

Dans le nouveau Android API level 17 (Jelly Bean 4.2), l’une des caractéristiques est Fragments imbriqués . La nouvelle description de la fonctionnalité indique:

si vous utilisez ViewPager pour créer des fragments qui glissent vers la gauche et vers la droite et utilisent la majorité de l'écran, vous pouvez maintenant insérer des fragments dans chaque page de fragment.

Donc, si je comprends bien, je peux maintenant créer un ViewPager avec Fragments (avec un bouton à l'intérieur par exemple) à l'intérieur, et lorsque l'utilisateur appuiera sur le bouton affichera un autre Fragment sans perdre le ViewPager en utilisant cette nouvelle fonctionnalité.

J'ai passé la matinée à essayer de mettre en œuvre cela de différentes manières, mais je ne peux pas le faire fonctionner ... Quelqu'un peut-il ajouter un exemple simple de la façon de le mettre en œuvre?

PS: Cela ne m'intéresse que de faire ainsi, avec getChildFragmentManager pour savoir comment ça marche.

55
sabadow

En supposant que vous ayez créé les mises en page XML correctes. Il est maintenant très simple d'afficher des fragments dans un ViewPager hébergé par un autre fragment.

Voici un fragment parent qui affiche des fragments enfants:

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

Il est important d'utiliser Fragment.getChildFragmentManager () lors de l'instanciation de FragmentPagerAdapter. Notez également que vous ne pouvez pas utiliser Fragment.setRetainInstance () () sur les fragments enfants, sinon vous obtiendrez une exception. Les importations ont été omises pour des raisons de brièveté.

Le code source peut être trouvé à l'adresse suivante: https://github.com/marcoRS/nested-fragments

100
Marco RS

Edit: Si vous souhaitez remplacer tout le contenu d'une page dans un ViewPager, vous pouvez toujours utiliser des fragments imbriqués, mais certaines modifications sont nécessaires. Vérifiez l'exemple ci-dessous (le FragmentActivity, définissant le ViewPager et le PagerAdapter sont identiques à l'extrait de code précédent):

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

En un coup d’œil, cela fonctionne, mais des problèmes peuvent apparaître car je ne l’ai pas beaucoup testé.

Quelqu'un peut-il montrer un exemple simple de la façon de procéder?

Utiliser des fragments imbriqués semble assez facile, jusqu'à ce que Commonsware propose un exemple plus élaboré, vous pouvez essayer le code ci-dessous:

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

    }

}

J'étais paresseux et je créais tout dans le code, mais je suis sûr que cela peut fonctionner avec des mises en page XML gonflées.

12
Luksprog

J'ai créé un ViewPager avec 3 éléments et 2 sous-éléments pour les index 2 et 3 et voici ce que je voulais faire.

enter image description here

J'ai implémenté cela avec l'aide de questions précédentes et les réponses de StackOverFlow et voici le lien.

ViewPagerChildFragments

6
Rukmal Dias