it-swarm.com.ru

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

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

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

Если вы нажмете кнопку в фрагменте A , то FragmentTransaction изменится FragmentC by FragmentB

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

MainActivity.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();


    }
}

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

fragment_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! 

30
Guillermo Barreiro

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

62
Guillermo Barreiro

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

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

3
oli

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

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

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 - Заменить

1
Kenny164

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

fragmentTransaction.addToBackStack(null); 

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

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

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

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

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

1
Panos Gr

Мы также видим эту проблему, но причина немного другая. Предлагаемое решение от 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, а затем добавлены (int, Fragment, String) с теми же аргументами, что и здесь.

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

0
Yuchen Zhong

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

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

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


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

transaction.add(R.id.mainContent, fragment, tag);
0
lm2a