findFragmentByTag() возвращает значение null после выполнения FragmentTransaction с использованием метода replace()

Мое приложение для Android состоит из трех фрагментов: A, B и C. Они загружаются в два контейнера, определенные в макете MainActivity.

Когда приложение запускается, оно показывает фрагмент A, загруженный в контейнер left_container, и фрагмент C в контейнер right_container.

Если вы нажмете кнопку в фрагменте A, FragmentTransaction заменит фрагмент C на фрагмент B.

На данный момент все ок. Но проблема возникает, когда я пытаюсь получить ссылку на загруженный фрагментB, используя findFragmentByTag(), потому что он возвращает null. Я использовал замену метода в FragmentTransaction и закончил с commit(), но нет способа вызвать метод FragmentB. Мой код:

Основная активность.java:

 public class MainActivity extends Activity{
 static String fragmentTag = "FRAGMENTB_TAG";

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    //Adds the left container's fragment
    getFragmentManager().beginTransaction().add(R.id.left_container, new FragmentA()).commit(); //Adds the fragment A to the left container

    //Adds the right container's fragment
    getFragmentManager().beginTransaction().add(R.id.right_container, new FragmentC()).commit(); //Adds the Fragment C to the right container
 }

 /**
 * Called when the button "Activate Fragment B" is pressed
 */
public void buttonListener(View v){

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.right_container, new FragmentB(),fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
        ft.commit(); //Finishes the transaction


        //!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
        ((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();


    }
}

ФрагментB.java:

public class FragmentB extends Fragment {

public View onCreateView(LayoutInflater inflater,
        ViewGroup container,
        Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_b, container,false);

}


/**
 * Gets a reference to the text_fragment_b TextView and calls its method setText(), changing "It doesn't work" text by "It works!"
 */
public void testView(){
    TextView tv = (TextView)getView().findViewById(R.id.text_fragment_b);
    tv.setText("It works!");
}

}

Activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<FrameLayout android:id="@+id/left_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>    
<FrameLayout android:id="@+id/right_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>

</LinearLayout>

фрагмент_b.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" 
android:layout_margin="5sp">

<TextView 
    android:id="@+id/text_fragment_b"
    android:text="It doesn't works!"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

</LinearLayout>

Пожалуйста помогите! Я новичок в Android-разработке!


person Guillermo Barreiro    schedule 29.12.2013    source источник


Ответы (8)


Я исправил это! Я позвонил getSupportFragmentManager().executePendingTransactions() после совершения транзакции, и это сработало! После вызова этого метода я могу получить фрагмент, используя методы findFragmentById() и findFragmentByTag().

person Guillermo Barreiro    schedule 16.01.2014
comment
Я бы предпочел переопределить onAttachFragment и действовать, когда фрагмент будет готов. - person Guillaume; 09.05.2014
comment
Я не понимаю, как это решает проблему или куда поместить executePendingTransactions(). - person IgorGanapolsky; 30.08.2014
comment
@igor-ganapolsky Вы должны поставить executePendingTransactions()после вызова метода .commit() FragmentTransaction. Это заставляет систему выполнить транзакцию, если она еще не была выполнена. - person Guillermo Barreiro; 31.08.2014
comment
Это не сработало для меня. Добавьте его после commit() и сохраните фрагмент значения null - person Sami Eltamawy; 14.05.2015
comment
Если вы не используете библиотеку поддержки совместимости с v4, я думаю, вам следует использовать getFragmentManager().executePendingTransactions(), а не getSupportFragmentManager().executePendingTransactions(). - person ToolmakerSteve; 11.11.2015
comment
Не работает для меня. Получение FragmentManager уже вызывает исключение транзакций в некоторых случаях при вызове executePendingTransactions(), и некоторые другие фрагменты перепутались. - person Jonas; 03.01.2017
comment
Нет радости. По-прежнему получает значение null, даже если getFragmentManager().executePendingTransactions(); помещается после вызова .commit(). - person Hephaestus; 05.05.2017
comment
Для тех, кто добавил executePendingTransactions() и не добился успеха, просто попробуйте добавить объект Fragment в свой класс и сослаться на него в своем методе, выполняющем транзакцию фрагмента. Это не идеально, но это обходной путь. Убедитесь, что вы обновляете объект Fragment каждый раз, когда заменяете/добавляете фрагмент, чтобы объект не был нулевым... - person Shn_Android_Dev; 20.07.2018
comment
Я не понял, почему этот ответ правильный)) Это не помогает для findFragmentByTag. Где ваш полный ответ и реальный результат? - person Georgiy Chebotarev; 22.08.2019

если вы используете setRetainInstance(true), вы не можете использовать findFragmentByTag() в onCreate из Activity. Сделайте это в onResume

см. документацию: setRetainInstance

person oli    schedule 15.09.2015

Начну с извинений, так как я еще совсем новичок...

Я думаю, что проблема может заключаться в объявлении статической строки fragmentTag, которая не получает должным образом доступ из экземпляров класса, просто измените эту строку на:

private final static String FRAGMENT_TAG = "FRAGMENTB_TAG"; // using uppercase since it's a constant

Кроме того, я был бы более точным при объявлении экземпляров, например:

public void buttonListener(View v){

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.right_container, new FragmentB(), FRAGMENT_TAG);
    ft.commit();

    FragmentB fragB = (FragmentB) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
    fragB.testView();
}

Я надеюсь, что вы разберетесь с этим, так как я видел этот вопрос, опубликованный ранее, и был удивлен, что он еще не получил никакой активности.

Кроме того, вот пара ссылок на документацию Android по замене:

Обучение Android — замена

Справочник по Android – заменить

person Kenny164    schedule 29.12.2013
comment
Это не сработало. Метод findFragmentByTag() по-прежнему возвращает null, несмотря на изменения, о которых вы мне сказали. - person Guillermo Barreiro; 31.12.2013
comment
Попробуйте заменить FRAGMENT_TAG обычными строковыми значениями, например, ft.replace(R.id.right_container, new FragmentB(), "fragmentB"); - person Kenny164; 31.12.2013
comment
Ни то, ни другое не работает. Я не могу найти ошибку в своем коде, и я пробовал много изменений, но я все еще получаю нулевой фрагмент в методе findFragmentByTag() - person Guillermo Barreiro; 31.12.2013
comment
@ Kenny164 FRAGMENT_TAG в любом случае является строковой константой. В чем смысл его замены?? - person IgorGanapolsky; 30.08.2014

У меня была такая же проблема, и я понял, что есть очень простой способ исправить это. При использовании тега не забудьте добавить

fragmentTransaction.addToBackStack(null); 

метод, чтобы ваш фрагмент возобновлялся, а не уничтожался, как указано в руководствах для разработчиков.

Если вы не вызываете addToBackStack() при выполнении транзакции, удаляющей фрагмент, то этот фрагмент уничтожается при фиксации транзакции, и пользователь не может вернуться к нему. Принимая во внимание, что если вы вызываете addToBackStack() при удалении фрагмента, то фрагмент останавливается и позже возобновляется, если пользователь возвращается назад.

Вы можете найти это в конце этого раздела.

Каждый раз, когда я пытался вернуться к своему созданному Фрагменту, оказывалось, что он уже уничтожен, поэтому я потерял около 30 минут, пытаясь понять, почему мой Фрагмент не был найден с помощью простого вызова findFragmentByTag();.

Надеюсь это поможет!

person Panos Gr    schedule 17.10.2018

Убедитесь, что вы правильно добавляете или заменяете фрагмент

Следующий оператор добавит фрагмент, но вернет значение null при использовании getFragmentManager().findFragmentByTag(tag):

transaction.add(R.id.mainContent, fragment);


Таким образом, это будет работать;

transaction.add(R.id.mainContent, fragment, tag);
person lm2a    schedule 16.10.2016

Мы также видим эту проблему, но причина немного другая. Предлагаемое решение https://stackoverflow.com/a/21170693/1035008 нам не подходит.

void updateFragment(Fragment newFragment) {
    FragmentTransaction ft = getFragmentManager().beginTransaction();

    // We have these 2 lines extra
    Fragment current = getChildFragmentManager().findFragmentByTag(fragmentTag);           
    if (current != null) { ft.remove(current); }

    ft.replace(R.id.right_container, newFragment, fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
    ft.commit(); //Finishes the transaction

    //!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
    ((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();
}

И после прочтения документации о замене:

Заменить существующий фрагмент, который был добавлен в контейнер. По сути, это то же самое, что и вызов remove(Fragment) для всех добавленных в данный момент фрагментов, которые были добавлены с одним и тем же containerViewId, а затем add(int, Fragment, String) с теми же аргументами, что и здесь.

Я понимаю, что вызов remove не был необходим, так как он выполняется replace автоматически. Итак, после удаления ft.remove(current) все работает нормально.

person Yuchen    schedule 14.12.2016

В моем случае я использовал код для замены и добавления в BackStack, но установил неправильный тег:

val fragment = { SomeFragment.newInstance() }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, WrongAnotherFragment.TAG)

Конечно, supportFragmentManager.findFragmentByTag(SomeFragment.TAG) не нашел SomeFragment.

person CoolMind    schedule 02.04.2020

Для меня, вероятно, это была ошибка новичка, что я звонил super.onCreate(savedInstanceState); после того, как пытался получить доступ к Фрагменту, используя findFragmentByTag.

Я переместил super.onCreate(savedInstanceState) вверх по порядку, и он начал работать для меня.

person Mohit Tater    schedule 12.06.2020