Фрагмент не замінюється, а ставиться поверх попереднього


104

Діяльність:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

Fragment1 fragment = new Fragment1();
Fragment2 fragment2 = new Fragment2();

transaction.replace(R.id.Fragment1, fragment);
transaction.addToBackStack(null);
transaction.commit();

FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction();
transaction2.replace(R.id.Fragment1, fragment2);
transaction2.addToBackStack(null);
transaction2.commit();

Код у вікні:

<fragment
    android:id="@+id/Fragment1"
    android:name="com.landa.fragment.Fragment1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_below="@+id/include1" /> 

Проблема полягає в тому, що вміст насправді не замінюється - він ставиться зверху (тому він перекривається).

Коли я натискаю назад, перший фрагмент відображається належним чином (без другого), але спочатку видно обидва (я хочу бачити лише останній).

Що я тут пропускаю?


У моєму випадку проблема була пов'язана з тим, що я не використовував відповідний ідентифікатор перегляду контейнера, тобто не передавав правильний containerViewId(у replaceметоді).
— nbro

Відповіді:


145

Ви робите тут дві речі неправильно:

  1. Ви не можете замінити фрагмент, який статично розміщений у xmlфайлі макета. Ви повинні створити контейнер (наприклад, а FrameLayout) у макеті, а потім додати фрагмент програмно, використовуючи FragmentTransaction.

  2. FragmentTransaction.replaceочікує ідентифікатора контейнера, який містить фрагмент, а не ідентифікатор фрагмента в якості першого параметра. Отже, ви повинні передавати перший аргумент як ідентифікатор контейнера, до якого ви додали перший фрагмент.

Ви можете ознайомитися з цим посиланням для отримання більш детальної інформації.


17
Використовувати FrameLayoutяк контейнер краще, ніж використовувати LinearLayout.
— Marcel Bro

3
Використання порожнього FrameLayout в діяльності також вирішило мою проблему
— Subtle Fox

1
Це працює, роблячи це. Але потім, коли я запускаю заявку і негайно натискаю кнопку назад, я опиняюсь у стані, коли фрагмент порожній, замість того, щоб виходити з програми?
— MasterScrat

@MasterScrat Пропустіть, addTobackStack(..)коли ви робите транзакцію вперше. Це дозволить уникнути розміщення порожнього контейнера FrameLayout на задній панелі. Ви все одно зможете повернутися до фрагменту, який ви додали.
— Алекс Меуер

Ось чудове відео, яке демонструє ваше пояснення. youtube.com/watch?v=EbcdMxAIr54
— SQL та Java Learner

32

У мене була подібна проблема, але моя проблема полягала в тому, що я використовував двох різних менеджерів фрагментів: одного з getSupportFragmentManager()і одного з getFragmentManager(). Якби я додавав один фрагмент із, SupportFragmentManagerа потім намагався замінити фрагмент на FragmentManager, фрагмент просто додався б зверху. Мені потрібно було змінити код, щоб той самий FragmentManagerбув використаний і це вирішило проблему.


17

Сайт розробників android пропонує використовувати a FrameLayoutдля динамічного завантаження фрагментів під час виконання. Ви жорстко закодували фрагмент у вашому XML-файлі. Тому його не можна видалити під час виконання, як зазначено у цьому посиланні:

http://developer.android.com/training/basics/fragments/creating.html

це посилання показує, як додати фрагменти через вашу програму:

http://developer.android.com/training/basics/fragments/fragment-ui.html


7

У мене була така ж проблема і я побачила всі відповіді, але жодна з них не містила моєї помилки! Перш ніж спробувати замінити поточний фрагмент, я показував фрагмент за замовчуванням у xml активності контейнера таким чином:

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

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="....ShopFragment"
        android:id="@+id/fragment"
        tools:layout="@layout/fragment_shop" />
</FrameLayout>

після цього, хоча я переходив FrameLayoutдоfragmentTransaction.replace() , але я мав точну проблему. він показував другий фрагмент зверху попереднього.

Проблема виправлена ​​шляхом вилучення фрагмента з xml та його програмного показу в onCreate()методі активності контейнера для попереднього перегляду за замовчуванням при запуску програми таким чином:

    fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.fragment_frame_layout,new ShopFragment());
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();

і активність контейнера xml:

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

4

Ви можете очистити backStack перед заміною фрагментів:

    FragmentManager fm = getActivity().getSupportFragmentManager();

    for (int i = 0; i < fm.getBackStackEntryCount(); i++) {
        fm.popBackStack();
    }

    // replace your fragments

проблема полягає в тому, що він оголосив фрагмент у своєму файлі макета xml. Отже, його не можна видалити / замінити під час виконання.
— Poonam Anthony

це спрацювало для мене, намагаючись замінити фрагмент, не очищаючи задню ранку, здавалося, залишив найновіший запис із заднім числом у верхній частині будь-якого іншого фрагмента.
— speedynomads

3
<FrameLayout
    android:id="@+id/fragment_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:windowBackground">
//Add fragment here
</FrameLayout>

Додайте android:background="?android:windowBackground">до контейнера, в якому знаходиться ваш фрагмент. У моєму випадку я додав FrameLayout, що повинно допомогти.


1

Використовуйте контейнер замість фрагмента

<FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hello World!" />

Зараз у MainActivity

if(condition)
    getFragmentManager().beginTransaction().replace(R.id.container,new FirstFragment()).commit();                    
else
    getFragmentManager().beginTransaction().replace(R.id.container, new SecondFragment()).commit();

Зауважте, що ваш клас фрагментів повинен імпортувати 'android.app.Fragment' Переконайтесь, що ви використовуєте реалізацію "support.v4" або "original" та не змішуєте їх. Я отримував цю проблему, тому що я їх змішав.


0

Я згоден з відповіддю Сапана Дівакара. Але я знайшов рішення, як це зробити.

По-перше, у своїй діяльності (наприклад activity_main), де у вас є всі макети, додайте FrameLayout. Це має бути висота і ширина match parent. Крім того, дайте йомуid .

Тепер у вашому фрагменті, який замінить поточний макет, зателефонуйте onPause()та onResume(). Ініціалізуйте всі внутрішні контейнери View. І встановити їх , visibilityщоб GONEвonResume() і VISIBLEвonPause() .

ПРИМІТКА . Не той, FrameLayoutякий ви замінюєте цим фрагментом.

Припустимо , що у вас є ViewPager, TabLayout, ImageViewі звичай <fragment>в activity_main.xml. Додайте, FrameLayoutяк зазначено вище.

В abcFragment.java, в onClick()функції, додайте addToBackstack(null)в transaction.

Приклад коду:

в xyzFragment, який збирається замінити abcFragment (і все, що показано у activity_main.xml), зробіть це

@Override
    public void onResume() {
        super.onResume();

    // On resume, make everything in activity_main invisible except this fragment.
    slidingTabs = getActivity().findViewById(R.id.sliding_tabs);
    viewPager = getActivity().findViewById(R.id.pager);
    miniPlayerFragment = getActivity().findViewById(R.id.mini_pager);

    slidingTabs.setVisibility(View.GONE);
    viewPager.setVisibility(View.GONE);
    miniPlayerFragment.setVisibility(View.GONE);
}

@Override
public void onPause() {
    super.onPause();

    // Make everything visible again
    slidingTabs = getActivity().findViewById(R.id.sliding_tabs);
    viewPager = getActivity().findViewById(R.id.pager);
    miniPlayerFragment = getActivity().findViewById(R.id.mini_pager);

    slidingTabs.setVisibility(View.VISIBLE);
    viewPager.setVisibility(View.VISIBLE);
    miniPlayerFragment.setVisibility(View.VISIBLE);
}

Щасливого кодування!


0

Для усунення проблеми, що перекривається, ви можете використовувати метод setTransition від FragmentTransaction.

transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

0

У Oncreate ()

  BaseFragmentloginpage fragment = new BaseFragmentloginpage();
            android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.frame, fragment);
            fragmentTransaction.isAddToBackStackAllowed();
            fragmentTransaction.commit();

тепер це завжди буде ваш фрагмент за замовчуванням ... якщо ви додасте фрагмент у xml макет, він завжди буде перекриватися, тому встановіть початковий вміст з фрагментом під час виконання


0

У мене була така ж проблема після використання цієї проблеми

  1. Візьміть макет кадру, де u замінює фрагмент ур

    <FrameLayout
                    android:id="@+id/fragment_place"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
  2. і код для заміни

    public void selectFrag(View view) {
    Fragment fr;
    
      if(view == findViewById(R.id.button2))
    {
         fr = new FragmentTwo();
    
     }else {
         fr = new FragmentOne();
     }
     FragmentManager fm = getFragmentManager();
     FragmentTransaction fragmentTransaction = fm.beginTransaction();
     fragmentTransaction.replace(R.id.fragment_place, fr);
     fragmentTransaction.commit();

якщо ви хочете цілий приклад, я надішлю вам просто коментар ......


0

У мене теж було таке ж питання, але це було тому, що я не змінив колір тла фрагмента, який намагався додати до мого кадру.

Спробуйте це зробити, за допомогою layout_heightі layout_widthвстановітьmatch_parent


0

Я колись мав цю проблему і дізнався, що це було тому, що я випадково видалив android:backgroundатрибут, який відсутній у вашому xml-коді. Я вважаю, це працює так, як коли ви фарбуєте стіну, android:backgroundце колір, яким буде стіна, а інші види розміщуються поверх основного кольору. Якщо ви не пофарбуєте стіну до позиціонування своїх поглядів, вони опиняться поверх того, що вже було у цій стіні, у вашому випадку - іншого вашого фрагмента. Не знаю, чи це допоможе вам, удачі.


0

Я намагався, перш за все, намагатися виправити проблему, але досі не отримав рішення. Я не знаю, чому це не працює на мене. Я згоден з вищезазначеними відповідями, тому що так багато з ними згодні.

Я просто продовжую відповідь і нижче - це спосіб, який працює для мене.

Я додав цю властивість android:clickable="true"у верхній батьківський макет у fragment.xmlфайлі.

Сподіваюся, хтось допоможе.


0

Дякую Сапану Дівакару та ін. Ця відповідь мені допомогла. Я просто створив RelativeLayout без фрагмента всередині нього, використав RelativeLayout як перегляд батьків або групи, вказав цей груповий вигляд у транзакції, і він працював.

Код:

getSupportFragmentManager().beginTransaction()
                            .replace(R.id.content_group_view, itemDetailFragment).commit();

XML:

 <RelativeLayout
         android:id="@+id/content_group_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent">

 </RelativeLayout>

0

Перевірте ідентифікатор макета кадру та замініть його, як показано нижче

fragmentTransactionChange = MainActivity.this.getSupportFragmentManager().beginTransaction();
fragmentTransactionChange.replace(R.id.progileframe, changeFragment);
fragmentTransactionChange.addToBackStack(null);
fragmentTransactionChange.commit();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.