Fragment Fragment wewnętrzny

137

Potrzebuję pomocy przy pracy nad fragmentem wewnątrz fragmentu, właściwie mam problem z wciśnięciem przycisku Wstecz. Na ekranie głównym aplikacji znajdują się przyciski, a naciśnięcie każdego przycisku powoduje zastąpienie widoku nowego fragmentem (i ten fragment zawiera inny fragment), dynamiczne dodawanie / zastępowanie fragmentu działa dobrze, po naciśnięciu przycisku button1 fragment jest zastępowany, to samo dzieje się po naciśnięciu przycisku, ale jeśli naciśnę przycisk znowu, dostałem wyjątek:

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

oznacza, że ​​fragment lub fragmenty wewnętrzne są już dodane i próbuję je dodać ponownie, każdy ma pomysł, jak pracować z fragmentem wewnątrz fragmentu i poruszać się tam iz powrotem bez problemu, dzięki za wsparcie.

MainActivity, w którym fragmenty są dynamicznie dodawane i zastępowane.

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

Oto układ: 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>

Mam nadzieję, że próbowałem rozwiązać mój problem.

Ijaz Ahmed
źródło
1
powyższy kod działa idealnie dla mnie z Androidem 3.1
Vivek
3
możliwy duplikat fragmentów w obrębie fragmentów
Vladimir,
aby uzyskać więcej informacji, patrz stackoverflow.com/a/11020531/1219456
Raneez Ahmed

Odpowiedzi:

284

AFAIK, fragmenty nie mogą zawierać innych fragmentów.


AKTUALIZACJA

Przy obecnych wersjach pakietu Android Support - lub natywnych fragmentach na poziomie API 17 i wyższych - możesz zagnieżdżać fragmenty za pomocą getChildFragmentManager(). Zwróć uwagę, że oznacza to, że musisz użyć wersji pakietu Android Support dla fragmentów na poziomach API 11-16, ponieważ chociaż na tych urządzeniach istnieje natywna wersja fragmentów, ta wersja nie ma getChildFragmentManager().

CommonsWare
źródło
30
Platforma jest tutaj naprawdę do niczego. Spędziłem trzy godziny na debugowaniu tego, ponieważ wewnętrzny fragment wyrenderowałby się dobrze za pierwszym razem, ale zniknąłby po zmianie orientacji ekranu. Bez wyjątku, informacje z dziennika, nic. Przełączenie do getChildFragmentManager()i usunięcie setRetainInstance(true)z wewnętrznego fragmentu (szkoda) naprawiło to. Dzięki za ponowne uratowanie mojego bekonu, @CommonsWare.
Felix,
82

wprowadź opis obrazu tutaj

Potrzebowałem więcej kontekstu, więc zrobiłem przykład, aby pokazać, jak to się robi. Najbardziej pomocna rzecz, jaką przeczytałem podczas przygotowań, to:

Czynność

activity_main.xml

Dodaj FrameLayoutdo swojej aktywności, aby trzymać fragment nadrzędny.

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

Załaduj fragment nadrzędny i zaimplementuj detektory fragmentów. (Zobacz fragment komunikacji .)

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 nadrzędny

fragment_parent.xml

Dodaj kolejny FrameLayoutpojemnik na fragment podrzędny.

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

Użyj getChildFragmentManagerin, onViewCreatedaby skonfigurować fragment podrzędny.

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 podrzędny

fragment_child.xml

Nie ma tu nic specjalnego.

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

Nie ma tu też nic specjalnego.

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

Uwagi

Suragch
źródło
67

Od wersji Android 4.2 (API 17) zagnieżdżone fragmenty stają się dostępne http://developer.android.com/about/versions/android-4.2.html#NestedFragments

Aby umieścić fragment wewnątrz innego fragmentu, użyj metody getChildFragmentManager ()

Jest również dostępny w bibliotece wsparcia!

Nik
źródło
Zaktualizuj swoją odpowiedź, aby wspomnieć, że obsługa lib obsługuje już zagnieżdżone frakcje z Androida
1.6+
13

Fragmenty mogą być dodawane wewnątrz innych fragmentów, ale wtedy będziesz musiał usunąć go z fragmentu nadrzędnego za każdym razem, gdy onDestroyView()zostanie wywołana metoda fragmentu nadrzędnego. I ponownie dodaj to w onCreateView()metodzie Parent Fragment .

Po prostu zrób to:

@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
źródło
4
To nie zadziałało dla mnie. Miałem fragment, który zawyżył widok zawierający widoki dwojga dzieci. W onActivityCreated()nim dodano dwa fragmenty, po jednym w każdym z widoków, używając getFragmentManager(). To zadziałało za pierwszym razem, ale po obróceniu i wznowieniu widoczny był tylko jeden z fragmentów. getChildFragmentManager()Zamiast tego zachowanie było poprawne . Podejście sugerowane tutaj stanowiło wyjątek, ponieważ działanie onSaveInstanceState()zostało już wywołane. Zatwierdzenie transakcji przy użyciu commitAllowingStateLoss()uniknęło wyjątku, ale nie rozwiązało pierwotnego problemu.
user1978019
8

Rozwiązałem ten problem. Możesz skorzystać z biblioteki pomocy technicznej i ViewPager. Jeśli nie potrzebujesz przesuwania gestem, możesz wyłączyć przesuwanie. Oto kod, który ulepszy moje rozwiązanie:

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

PS To brzydki kod do testów, ale poprawia, że ​​jest to możliwe.

Fragment PPS Inside ChildFragmentManagernależy przekazać doViewPagerAdapter

Vetalll
źródło
8

możesz użyć getChildFragmentManager()funkcji.

przykład:

Fragment nadrzędny:

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

Układ nadrzędny ( 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 podrzędny:

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
źródło
4

To nic skomplikowanego. Nie możemy getFragmentManager()tutaj użyć . Aby użyć fragmentów wewnątrz fragmentu , używamy getChildFragmentManager(). Reszta będzie taka sama.

Wijay Sharma
źródło
3

Możesz dodać FrameLayoutdo fragmentu i zastąpić go innym fragmentem podczas inicjalizacji.

W ten sposób możesz uznać, że drugi fragment znajduje się wewnątrz pierwszego fragmentu.

LoveBigPizza
źródło
2

Obecnie w zagnieżdżonych fragmentach zagnieżdżone są obsługiwane tylko wtedy, gdy są generowane programowo! Więc w tej chwili żaden zagnieżdżony układ fragmentów nie jest obsługiwany w schemacie układu xml!

Andrea Bellitto
źródło
1

To może pomóc tym, którzy pracują w Kotlinie Możesz użyć funkcji rozszerzenia, więc stwórz plik kotlin powiedzmy "util.kt" i dodaj ten fragment kodu

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

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

Powiedzmy, że to klasa dziecka

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

Teraz możesz dodać dzieci do fragmentu ojca w ten sposób

 FatherPresentation:Fragment()
{
  ...

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

gdzie R.id.fragmet_field to id układu, który będzie zawierał fragment. Ten tekst znajduje się oczywiście wewnątrz fragmentu ojca. Oto przykład

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
źródło
1

Nie ma wsparcia dla MapFragment, zespół Androida twierdzi, że pracuje nad tym od Androida 3.0. Tutaj więcej informacji o problemie Ale co możesz zrobić, tworząc fragment, który zwraca plik MapActivity. Oto przykład kodu. Dzięki inazaruk:

Jak to działa :

  • MainFragmentActivity to działanie, które rozszerza FragmentActivityi obsługuje dwa MapFragments.
  • MyMapActivity rozszerza MapActivity i ma MapView.
  • LocalActivityManagerFragment hosty LocalActivityManager.
  • MyMapFragment rozszerza LocalActivityManagerFragmenti za pomocą TabHosttworzy wewnętrzną instancję MyMapActivity.

Jeśli masz jakiekolwiek wątpliwości, daj mi znać

rallat
źródło
0

Cześć, rozwiązałem ten problem, umieszczając każdy fragment w odrębnym układzie, a także sprawiłem, że powiązany układ był widoczny, a pozostałe widoczności zniknęły.

Mam na myśli:

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

Założyłem, że otworzysz swoją stronę po kliknięciu przycisku 1.Możesz kontrolować widoczność swojego fragmentu po kliknięciu akcji.Możesz sprawić, by powiązany układ był widoczny, a inne zniknęły, a dzięki Menedżerowi fragmentów możesz wziąć swój fragment.To podejście zadziałało dla mnie. A ponieważ widok, który ma widoczność: zniknął, jest niewidoczny i nie zajmuje miejsca na potrzeby układu, myślę, że takie podejście nie powoduje żadnego problemu z przestrzenią.

PS: Właśnie próbowałem wyjaśnić, że kod mojego rozwiązania może zawierać błędy składniowe lub niekompletną strukturę.

Sevil
źródło