Fragment à l'intérieur du fragment

137

J'ai besoin d'aide pour travailler sur un fragment à l'intérieur d'un fragment, en fait, je suis confronté à un problème en appuyant sur le bouton Retour. L'écran principal de l'application comporte des boutons et en appuyant sur chaque bouton, le remplacement par un nouveau fragment (et ce fragment contient un autre fragment), l'ajout / le remplacement dynamique du fragment fonctionne correctement, en appuyant sur le bouton1 fragment remplacé, la même chose se produit lorsque vous appuyez sur le bouton, mais si j'appuie sur le bouton à nouveau, a obtenu une exception:

"Duplicate id 0x7f05000a, tag null, or parent id 0x7f050009 with
another fragment for com........ fragmentname"

signifie que des fragments ou des fragments internes sont déjà ajoutés et que j'essaye de les ajouter à nouveau, tout le monde a une idée de la façon de travailler avec un fragment à l'intérieur d'un fragment et de se déplacer sans problème, merci pour le soutien.

MainActivity, où les fragments sont ajoutés et remplacés dynamiquement.

public class FragmentInsideFragmentTestActivity extends Activity {

    private Button button1;
    private Button button2;
    private Button button3;
    private Button button4;


    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        button1 =(Button) this.findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
           public void onClick(View view) {
               onButtonClick(view);
            }
        });

        button2 =(Button) this.findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                onButtonClick(view);
            }
        });

        button3 =(Button) this.findViewById(R.id.button3);
        button3.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
               onButtonClick(view);
            }
        });

        button4 =(Button) this.findViewById(R.id.button4);
        button4.setOnClickListener(new View.OnClickListener() {
           public void onClick(View view) {
               onButtonClick(view);
           }
        });
    }

    public void onButtonClick(View v) {
        Fragment fg;

        switch (v.getId()) {
           case R.id.button1:
                   fg=FirstFragment.newInstance();
                   replaceFragment(fg);
                   break;
           case R.id.button2:
                   fg=SecondFragment.newInstance();
                   replaceFragment(fg);
                   break;
           case R.id.button3:
                   fg=FirstFragment.newInstance();
                   replaceFragment(fg);
                   break;
           case R.id.button4:
                   fg=SecondFragment.newInstance();
                   replaceFragment(fg);
                   break;
        }
    }

    private void replaceFragment(Fragment newFragment) {
       FragmentTransaction trasection = getFragmentManager().beginTransaction();

        if(!newFragment.isAdded()) {
            try {
                //FragmentTransaction trasection =
                getFragmentManager().beginTransaction();
                trasection.replace(R.id.linearLayout2, newFragment);
                trasection.addToBackStack(null);
                trasection.commit();
            } catch (Exception e) {
                // TODO: handle exception
                // AppConstants.printLog(e.getMessage());
            } else {
                trasection.show(newFragment);
            }
        }
    }

Voici la mise en page: main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button1" />

        <Button
            android:id="@+id/button2"
            android:text="Button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/button3"
            android:text="Button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/button4"
            android:text="Button4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" />
</LinearLayout>

J'espère que j'ai essayé de résoudre mon problème.

Ijaz Ahmed
la source
1
le code ci-dessus fonctionne parfaitement bien pour moi avec Android 3.1
Vivek
3
duplication possible de fragments dans des fragments
Vladimir
pour plus de détails, consultez stackoverflow.com/a/11020531/1219456
Raneez Ahmed

Réponses:

284

AFAIK, les fragments ne peuvent pas contenir d'autres fragments.


METTRE À JOUR

Avec les versions actuelles du package de support Android - ou des fragments natifs de niveau API 17 et supérieur - vous pouvez imbriquer des fragments au moyen de getChildFragmentManager(). Notez que cela signifie que vous devez utiliser la version du package Android Support des fragments sur les niveaux d'API 11 à 16, car même s'il existe une version native des fragments sur ces appareils, cette version n'en a pas getChildFragmentManager().

CommonsWare
la source
30
La plate-forme est vraiment nul ici. J'ai passé trois heures à déboguer cela parce que le fragment interne rendrait bien la première fois, mais disparaîtrait après un changement d'orientation de l'écran. Aucune exception, informations de journal, rien. Le basculement getChildFragmentManager()et la suppression setRetainInstance(true)du fragment interne (dommage) l'ont corrigé. Merci d'avoir encore gardé mon bacon, @CommonsWare.
Felix
82

entrez la description de l'image ici

J'avais besoin d'un peu plus de contexte, alors j'ai fait un exemple pour montrer comment cela est fait. La chose la plus utile que j'ai lue lors de la préparation était la suivante:

Activité

activity_main.xml

Ajoutez un FrameLayoutà votre activité pour contenir le fragment parent.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Activity"/>

    <FrameLayout
        android:id="@+id/parent_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>

 </LinearLayout>

MainActivity.java

Chargez le fragment parent et implémentez les écouteurs de fragment. (Voir la communication fragmentée .)

import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnFragmentInteractionListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Begin the transaction
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.parent_fragment_container, new ParentFragment());
        ft.commit();
    }

    @Override
    public void messageFromParentFragment(Uri uri) {
        Log.i("TAG", "received communication from parent fragment");
    }

    @Override
    public void messageFromChildFragment(Uri uri) {
        Log.i("TAG", "received communication from child fragment");
    }
}

Fragment parent

fragment_parent.xml

Ajoutez un autre FrameLayoutconteneur pour le fragment enfant.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_margin="20dp"
              android:background="#91d0c2">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Parent fragment"/>

    <FrameLayout
        android:id="@+id/child_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </FrameLayout>

</LinearLayout>

ParentFragment.java

Utilisez getChildFragmentManagerdans onViewCreatedpour configurer le fragment enfant.

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;    

public class ParentFragment extends Fragment {

    private OnFragmentInteractionListener mListener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_parent, container, false);
    }

    @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 onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void messageFromParentFragment(Uri uri);
    }
}

Fragment d'enfant

fragment_child.xml

Il n'y a rien de spécial ici.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_margin="20dp"
              android:background="#f1ff91">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Child fragment"/>
</LinearLayout>

ChildFragment.java

Il n'y a rien de trop spécial ici non plus.

import android.support.v4.app.Fragment;

public class ChildFragment extends Fragment {

    private OnFragmentInteractionListener mListener;


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

    @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 void onDetach() {
        super.onDetach();
        mListener = null;
    }


    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void messageFromChildFragment(Uri uri);
    }
}

Remarques

  • La bibliothèque de support est utilisée afin que les fragments imbriqués puissent être utilisés avant Android 4.2.
Suragch
la source
67

Depuis que les fragments imbriqués Android 4.2 (API 17) sont disponibles http://developer.android.com/about/versions/android-4.2.html#NestedFragments

Pour placer un fragment dans un autre fragment, utilisez getChildFragmentManager ()

Il est également disponible dans la bibliothèque de support!

Nik
la source
Veuillez mettre à jour votre réponse pour mentionner que la bibliothèque de support prend déjà en charge les fragments imbriqués d'Android 1.6+
FindOutIslamNow
13

Des fragments peuvent être ajoutés à l'intérieur d'autres fragments, mais vous devrez ensuite le supprimer du fragment parent à chaque fois que la onDestroyView()méthode du fragment parent est appelée. Et ajoutez-le à nouveau dans la onCreateView()méthode de Parent Fragment .

Faites comme ceci:

@Override
    public void onDestroyView()
    {
                FragmentManager mFragmentMgr= getFragmentManager();
        FragmentTransaction mTransaction = mFragmentMgr.beginTransaction();
                Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment")
        mTransaction.remove(childFragment);
        mTransaction.commit();
        super.onDestroyView();
    }
Napoléon
la source
4
Cela n'a pas fonctionné pour moi. J'avais un fragment qui gonflait une vue contenant deux vues enfants. Dans onActivityCreated()il a ajouté deux fragments, un dans chacune des vues, en utilisant getFragmentManager(). Cela a fonctionné la première fois, mais lors de la rotation et de la reprise, un seul des fragments était visible. Le comportement était correct avec à la getChildFragmentManager()place. L'approche suggérée ici a jeté une exception puisque l'activité onSaveInstanceState()avait déjà été appelée. La validation de la transaction en utilisant a commitAllowingStateLoss()évité l'exception mais n'a pas résolu le problème d'origine.
user1978019
toute idée sur ce stackoverflow.com/questions/63052712/…
Sunil Chaudhary
8

J'ai résolu ce problème. Vous pouvez utiliser la bibliothèque de support et ViewPager. Si vous n'avez pas besoin de faire glisser votre doigt par geste, vous pouvez désactiver le balayage. Voici donc du code pour améliorer ma solution:

public class TestFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.frag, container, false);
    final ArrayList<Fragment> list = new ArrayList<Fragment>();

    list.add(new TrFrag());
    list.add(new TrFrag());
    list.add(new TrFrag());

    ViewPager pager = (ViewPager) v.findViewById(R.id.pager);
    pager.setAdapter(new FragmentPagerAdapter(getChildFragmentManager()) {
        @Override
        public Fragment getItem(int i) {
            return list.get(i);
        }

        @Override
        public int getCount() {
            return list.size();
        }
    });
    return v;
}
}

PSIt est un code moche pour le test, mais il améliore que c'est possible.

Le fragment PPS Inside ChildFragmentManagerdoit être transmis àViewPagerAdapter

Vetalll
la source
8

vous pouvez utiliser getChildFragmentManager() fonction.

exemple:

Fragment parent:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    rootView = inflater.inflate(R.layout.parent_fragment, container,
            false);


    }

    //child fragment 
    FragmentManager childFragMan = getChildFragmentManager();
    FragmentTransaction childFragTrans = childFragMan.beginTransaction();
    ChildFragment fragB = new ChildFragment ();
    childFragTrans.add(R.id.FRAGMENT_PLACEHOLDER, fragB);
    childFragTrans.addToBackStack("B");
    childFragTrans.commit();        


    return rootView;
}

Disposition parent ( parent_fragment.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">



    <FrameLayout
        android:id="@+id/FRAGMENT_PLACEHOLDER"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>




</LinearLayout>

Fragment d'enfant:

public class ChildFragment extends Fragment implements View.OnClickListener{

    View v ;
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View rootView = inflater.inflate(R.layout.child_fragment, container, false);


        v = rootView;


        return rootView;
    }



    @Override
    public void onClick(View view) {


    }


} 
S.M_Emamian
la source
4

Ce n'est rien de compliqué. Nous ne pouvons pas utiliser getFragmentManager()ici. Pour utiliser des fragments dans un fragment , nous utilisons getChildFragmentManager(). Le repos sera le même.

Wijay Sharma
la source
3

Vous pouvez ajouter FrameLayout au fragment et le remplacer par un autre fragment lors de son initialisation.

De cette façon, vous pouvez considérer que l'autre fragment se trouve à l'intérieur du premier fragment.

AmourBigPizza
la source
2

Actuellement dans le fragment imbriqué, le ou les imbriqués ne sont pris en charge que s'ils sont générés par programme! Donc, pour le moment, aucune disposition de fragment imbriqué n'est prise en charge dans le schéma de disposition xml!

Andrea Bellitto
la source
1

Cela peut aider ceux qui travaillent sur Kotlin, vous pouvez utiliser la fonction d'extension, alors créez un fichier kotlin, disons "util.kt" et ajoutez ce morceau de code

fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {

    val transaction = childFragmentManager.beginTransaction()
    transaction.replace(frameId, fragment).commit()
}

Disons que c'est la classe de l' enfant

class InputFieldPresentation: Fragment()
{
    var views: View? = null
    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        views = inflater!!.inflate(R.layout.input_field_frag, container, false)
        return views
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        ...
    }
    ...
}

Vous pouvez maintenant ajouter les enfants au fragment père comme ceci

 FatherPresentation:Fragment()
{
  ...

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val fieldFragment= InputFieldPresentation()
        addChildFragment(fieldFragment,R.id.fragmet_field)
    }
...
}

R.id.fragmet_field est l'id du layout qui contiendra le fragment. Ce lyout est à l'intérieur du fragment père bien sûr. Voici un exemple

Father_fragment.xml :

<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android"
    >

    ...

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:id="@+id/fragmet_field"
            android:orientation="vertical"
            >
        </LinearLayout>
    ...

    </LinearLayout>
DINA TAKLIT
la source
1

Il n'y a pas de support pour MapFragment, l'équipe Android dit qu'il y travaille depuis Android 3.0. Voici plus d'informations sur le problème Mais ce que vous pouvez faire en créant un fragment qui renvoie un fichier MapActivity. Voici un exemple de code. Merci à inazaruk:

Comment ça fonctionne :

  • MainFragmentActivity est l'activité qui étend FragmentActivity et héberge deux MapFragments.
  • MyMapActivity étend MapActivity et a MapView.
  • LocalActivityManagerFragment héberge LocalActivityManager.
  • MyMapFragment étend LocalActivityManagerFragmentet avec l'aide de TabHostcrée une instance interne de MyMapActivity.

Si vous avez le moindre doute, faites-le moi savoir

rallat
la source
0

Salut, j'ai résolu ce problème en mettant par Fragment dans une mise en page distincte, et j'ai rendu visible la mise en page associée et fait disparaître les autres visibilités.

Je veux dire:

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">

     <LinearLayout android:id="@+id/linearLayout1"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="horizontal">
           <Button android:layout_width="wrap_content"
                   android:id="@+id/button1"
                   android:layout_height="wrap_content"
                   android:text="Button1"></Button>
           <Button android:text="Button2"
                   android:id="@+id/button2"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"></Button>
           <Button android:text="Button3"
                   android:id="@+id/button3"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"></Button>
           <Button android:text="Button4"
                   android:id="@+id/button4"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"></Button>
   </LinearLayout>
   <LinearLayout android:layout_width="full_screen"
              android:layout_height="0dp"
              android:layout_weight="1"
              android:id="action_For_Button1"
              android:visibility="visible">
                    <Fragment android:layout_width="full_screen"
                              android:layout_height="full_screen"
                              android:id="fragment1"
                                        .
                                        .
                                        .
             / >
     </LinearLayout>

     <LinearLayout android:layout_width="full_screen"
              android:layout_height="0dp"
              android:id="action_For_Button1"
              android:layout_weight="1"
              android:visibility="gone">
                       <Fragment android:layout_width="full_screen"
                                 android:layout_height="full_screen"
                                 android:id="fragment2"
                                           .
                                           .
                                           .
             / >
        </LinearLayout>
                     .
                     .
                     .
        </LinearLayout>

J'ai supposé que vous ouvririez votre page lorsque le bouton 1 est cliqué.Vous pouvez contrôler la visibilité de votre fragment sur l'action de clic.Vous pouvez rendre la mise en page associée visible et les autres parties et par Fragment Manager, vous pouvez prendre votre fragment.Cette approche a fonctionné pour moi. Et puisque la vue qui a de la visibilité: parti est invisible et ne prend pas de place pour la mise en page, je pense que cette approche ne pose aucun problème d'espace.

PS: j'ai juste essayé d'expliquer que le code de ma solution peut avoir des erreurs de syntaxe ou une structure incomplète.

Sevil
la source