Fragment innerhalb Fragment

137

Ich brauche Hilfe bei der Arbeit an Fragmenten innerhalb von Fragmenten. Tatsächlich habe ich ein Problem beim Drücken der Zurück-Taste. Anwendung Der Hauptbildschirm verfügt über Schaltflächen und das Drücken auf jede Schaltflächenansicht wird durch ein neues Fragment ersetzt (und dieses Fragment enthält in einem anderen Fragment). Das dynamische Hinzufügen / Ersetzen von Fragmenten funktioniert einwandfrei. Durch Drücken von Schaltfläche1 wird das Fragment ersetzt der Knopf wieder, bekam eine Ausnahme:

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

bedeutet, dass bereits Fragmente oder innere Fragmente hinzugefügt wurden und ich versuche, sie erneut hinzuzufügen. Jeder hat eine Idee, wie er mit Fragmenten innerhalb des Fragments arbeiten und sich problemlos hin und her bewegen kann, danke für die Unterstützung.

MainActivity, bei der Fragmente dynamisch hinzugefügt und ersetzt werden.

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

Hier ist Layout: 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>

Ich hoffe, ich habe versucht, mein Problem zu lösen.

Ijaz Ahmed
quelle
1
Der obige Code funktioniert perfekt für mich mit Android 3.1
Vivek
3
Mögliches Duplikat von Fragmenten innerhalb von Fragmenten
Vladimir
Weitere Informationen finden Sie unter stackoverflow.com/a/11020531/1219456
Raneez Ahmed

Antworten:

284

AFAIK, Fragmente können keine anderen Fragmente enthalten.


AKTUALISIEREN

Mit aktuellen Versionen des Android Support-Pakets - oder nativen Fragmenten auf API Level 17 und höher - können Sie Fragmente mithilfe von verschachteln getChildFragmentManager(). Beachten Sie, dass dies bedeutet, dass Sie die Android Support-Paketversion von Fragmenten auf API-Ebenen 11-16 verwenden müssen, da diese Version keine native Version von Fragmenten auf diesen Geräten hat getChildFragmentManager().

CommonsWare
quelle
30
Die Plattform ist hier wirklich scheiße. Ich habe drei Stunden damit verbracht, dies zu debuggen, da das innere Fragment beim ersten Mal gut gerendert würde, aber nach einer Änderung der Bildschirmausrichtung verschwinden würde . Keine Ausnahme, Protokollinformationen, nichts. Das Umschalten auf getChildFragmentManager()und Entfernen setRetainInstance(true)aus dem inneren Fragment (Mitleid) hat es behoben. Vielen Dank, dass Sie meinen Speck wieder gespeichert haben, @CommonsWare.
Felix
82

Geben Sie hier die Bildbeschreibung ein

Ich brauchte etwas mehr Kontext, also machte ich ein Beispiel, um zu zeigen, wie das gemacht wird. Das Hilfreichste, was ich während der Vorbereitung gelesen habe, war Folgendes:

Aktivität

activity_main.xml

Fügen Sie FrameLayoutIhrer Aktivität ein hinzu, um das übergeordnete Fragment zu speichern.

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

Laden Sie das übergeordnete Fragment und implementieren Sie die Fragment-Listener. (Siehe Fragmentkommunikation .)

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

Übergeordnetes Fragment

fragment_parent.xml

Fügen Sie einen weiteren FrameLayoutContainer für das untergeordnete Fragment hinzu.

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

Verwenden Sie getChildFragmentManagerin onViewCreated, um das untergeordnete Fragment einzurichten.

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

Kinderfragment

fragment_child.xml

Hier gibt es nichts Besonderes.

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

Auch hier gibt es nichts Besonderes.

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

Anmerkungen

  • Die Unterstützungsbibliothek wird verwendet, damit verschachtelte Fragmente vor Android 4.2 verwendet werden können.
Suragch
quelle
67

Seit Android 4.2 (API 17) sind verschachtelte Fragmente verfügbar: http://developer.android.com/about/versions/android-4.2.html#NestedFragments

Verwenden Sie getChildFragmentManager (), um ein Fragment in ein anderes Fragment zu platzieren.

Es ist auch in der Support-Bibliothek erhältlich!

Nik
quelle
Bitte aktualisieren Sie Ihre Antwort, um zu erwähnen, dass support lib bereits verschachtelte Fragmente von Android 1.6+ unterstützt
FindOutIslamNow
13

Fragmente können in andere Fragmente eingefügt werden. Sie müssen sie jedoch jedes Mal aus dem übergeordneten Fragment entfernen, wenn die onDestroyView()Methode des übergeordneten Fragments aufgerufen wird. Fügen Sie es erneut in die onCreateView()Methode des übergeordneten Fragments ein .

Mach einfach so:

@Override
    public void onDestroyView()
    {
                FragmentManager mFragmentMgr= getFragmentManager();
        FragmentTransaction mTransaction = mFragmentMgr.beginTransaction();
                Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment")
        mTransaction.remove(childFragment);
        mTransaction.commit();
        super.onDestroyView();
    }
Napolean
quelle
4
Das hat bei mir nicht funktioniert. Ich hatte ein Fragment, das eine Ansicht mit zwei untergeordneten Ansichten aufblähte. Darin onActivityCreated()wurden zwei Fragmente hinzugefügt, eines in jede der Ansichten mit getFragmentManager(). Dies funktionierte beim ersten Mal, aber bei Rotation und Wiederaufnahme war nur eines der Fragmente sichtbar. getChildFragmentManager()Stattdessen war das Verhalten korrekt . Der hier vorgeschlagene Ansatz warf eine Ausnahme auf, da die Aktivität onSaveInstanceState()bereits aufgerufen wurde. Durch das Festschreiben der Transaktion mit wurde commitAllowingStateLoss()die Ausnahme vermieden, das ursprüngliche Problem jedoch nicht gelöst.
user1978019
8

Ich habe dieses Problem gelöst. Sie können die Support-Bibliothek und verwenden ViewPager. Wenn Sie nicht per Geste wischen müssen, können Sie das Wischen deaktivieren. Hier ist ein Code, um meine Lösung zu verbessern:

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 ist hässlicher Code zum Testen, aber es verbessert, dass es möglich ist.

PPS Inside Fragment ChildFragmentManagersollte an übergeben werdenViewPagerAdapter

Vetalll
quelle
8

Sie können die getChildFragmentManager()Funktion verwenden.

Beispiel:

Übergeordnetes Fragment:

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

Übergeordnetes Layout ( 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>

Kinderfragment:

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
quelle
4

Es ist nichts kompliziertes. Wir können hier nicht verwenden getFragmentManager(). Für die Verwendung von Fragmenten innerhalb eines Fragments verwenden wir getChildFragmentManager(). Die Ruhe wird gleich sein.

Wijay Sharma
quelle
3

Sie können FrameLayoutdas Fragment hinzufügen und bei der Initialisierung durch ein anderes Fragment ersetzen.

Auf diese Weise können Sie das andere Fragment als innerhalb des ersten Fragments befindlich betrachten.

LoveBigPizza
quelle
2

Derzeit werden in verschachtelten Fragmenten die verschachtelten nur unterstützt, wenn sie programmgesteuert generiert werden! Derzeit werden also keine verschachtelten Fragmentlayouts im XML-Layoutschema unterstützt!

Andrea Bellitto
quelle
1

Das kann denjenigen helfen, die mit Kotlin arbeiten. Sie können die Erweiterungsfunktion verwenden. Erstellen Sie also eine Kotlin-Datei, sagen wir "util.kt", und fügen Sie diesen Code hinzu

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

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

Nehmen wir an, dies ist die Klasse des Kindes

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

Jetzt können Sie die Kinder zum hinzufügen Vater Fragment wie folgt

 FatherPresentation:Fragment()
{
  ...

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

Dabei ist R.id.fragmet_field die ID des Layouts, das das Fragment enthält. Dieses Lyout befindet sich natürlich im Vaterfragment. Hier ist ein Beispiel

dad_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
quelle
1

Es gibt keine Unterstützung für MapFragment, sagt Android-Team arbeitet daran seit Android 3.0. Hier weitere Informationen über das Thema Was aber Sie können es tun , indem ein Fragment zu schaffen , die eine zurückgibt MapActivity. Hier ist ein Codebeispiel. Danke an inazaruk:

Wie es funktioniert :

  • MainFragmentActivity ist die Aktivität, FragmentActivitydie zwei MapFragments erweitert und hostet.
  • MyMapActivity erweitert MapActivity und hat MapView.
  • LocalActivityManagerFragment hostet LocalActivityManager.
  • MyMapFragment wird erweitert LocalActivityManagerFragmentund erstellt mithilfe von TabHosteine interne Instanz von MyMapActivity.

Wenn Sie irgendwelche Zweifel haben, lassen Sie es mich bitte wissen

rallat
quelle
0

Hallo, ich habe dieses Problem gelöst, indem ich pro Fragment ein bestimmtes Layout erstellt habe. Und ich habe nur das zugehörige Layout sichtbar gemacht und die anderen Sichtbarkeiten verloren.

Ich meine:

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

Ich ging davon aus, dass Sie Ihre Seite öffnen, wenn auf Schaltfläche 1 geklickt wird. Sie können die Sichtbarkeit Ihres Fragments beim Klicken steuern. Sie können das zugehörige Layout sichtbar machen und die anderen verschwinden. Mit Fragment Manager können Sie Ihr Fragment übernehmen. Dieser Ansatz hat bei mir funktioniert. Und da eine Ansicht mit Sichtbarkeit: weg unsichtbar ist und für Layoutzwecke keinen Platz benötigt, verursacht dieser Ansatz meines Erachtens keine Platzprobleme.

PS: Ich habe gerade versucht zu erklären, dass mein Lösungscode möglicherweise Syntaxfehler oder eine unvollständige Struktur aufweist.

Sevil
quelle