Фрагмент не заменяется, а кладется поверх предыдущего


105

Деятельность:

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файл макета. Вы должны создать контейнер (например, a FrameLayout) в макете, а затем программно добавить фрагмент, используя FragmentTransaction.

  2. FragmentTransaction.replaceожидает идентификатор контейнера, который содержит фрагмент, а не идентификатор фрагмента в качестве первого параметра. Таким образом, вы должны передать первый аргумент в качестве идентификатора контейнера, в который вы добавили первый фрагмент.

Вы можете обратиться к этой ссылке для получения более подробной информации.


17
FrameLayoutЛучше использовать в качестве контейнера, чем использовать LinearLayout.
Марсель Бро

3
Использование пустого FrameLayout в Activity решило и мою проблему
Subtle Fox

1
Это работает. Но затем, когда я запускаю свое приложение и сразу же нажимаю кнопку «Назад», я попадаю в состояние, в котором фрагмент пуст, вместо того, чтобы выйти из приложения?
MasterScrat

@MasterScrat Не указывайте addTobackStack(..)при совершении транзакции в первый раз. Это позволит избежать помещения пустого контейнера FrameLayout в задний стек. Вы по-прежнему сможете вернуться к добавляемому фрагменту.
Alex Meuer

Вот отличное видео, демонстрирующее ваше объяснение. youtube.com/watch?v=EbcdMxAIr54
SQL and Java Learner

32

У меня была аналогичная проблема, но моя проблема заключалась в том, что я использовал два разных менеджера фрагментов: один от getSupportFragmentManager()и один от getFragmentManager(). Если бы я добавил один фрагмент с помощью, SupportFragmentManagerа затем попытался заменить фрагмент на FragmentManager, фрагмент просто добавился бы поверх. Мне нужно было изменить код, чтобы использовать тот же код, FragmentManagerи это решило проблему.


17

Сайт разработчика Android предлагает использовать 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

это сработало для меня, попытка заменить фрагмент без очистки backstack, похоже, оставила самую последнюю запись backstack поверх любого другого фрагмента.
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» или «оригинальные» реализации и не смешиваете их. У меня возникла эта проблема, потому что я их смешал.


0

Я согласен с ответом Сапана Дивакара. Но я нашел обходной путь, как это сделать.

Во-первых, в вашей деятельности (например activity_main), где у вас есть все макеты, добавьте файл FrameLayout. Его высота и ширина должны быть match parent. Кроме того, дайте ему id.

Теперь в вашем фрагменте, который заменит текущий макет, вызовите onPause()и onResume(). Инициализируйте все внутренние контейнеры View. И установить их visibilityToGONE в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. Возьмите макет кадра, где вы заменяете фрагмент ur

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