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


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" /> 

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

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

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


У моєму випадку проблема була пов'язана з тим, що я не використовував відповідний ідентифікатор перегляду контейнера, тобто не передавав правильний containerViewIdreplaceметоді).
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.