Не удается правильно связаться с фрагментом после изменения ориентации

У меня возникли проблемы с определением правильного способа связи между фрагментами при использовании FragmentPagerAdapter.

Для некоторого контекста у меня есть действие, которое использует FragmentPagerAdapter для размещения двух фрагментов. Фрагмент A имеет кнопку, и при нажатии она меняет текст во фрагменте B.

Все работает так, как ожидалось, за исключением того, что когда я нажимаю кнопку после изменения ориентации, я получаю исключение нулевого указателя в EditText, который я меняю во фрагменте B. После нескольких дней отладки это выглядит так, когда я вызываю метод во фрагменте B, который изменяет текст что это, возможно, старый экземпляр Фрагмента.

Вот код:

FragmentPagerAdapter, который является внутренним классом в моей деятельности:

public class MyPagerAdapter extends FragmentPagerAdapter {

    public MyPagerAdapter (FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int i) {
        switch (i) {
            case 0:
                return fragmentA;
            case 1:
                return fragmentB;
        }
        return null;
    }

    @Override
    public int getCount() {
        return tabs.length;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return tabs[position];
    }
}

Фрагменты создаются в методе Activity onCreate следующим образом:

FragmentA fragmentA; //Declared outside of onCreate
FragmentB fragmentB; //Declared outside of onCreate

fragmentA = FragmentA.newInstance();
fragmentB = FragmentB.newInstance();

Во фрагменте, который я нажимаю на кнопку, я настраиваю интерфейс следующим образом:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        testTextInterface = (TestTextInterface) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement testTextInterface");
    }
}

Затем, когда я нажимаю кнопку, у меня есть OnClickListener, который запускает следующий код:

testTextInterface.onTextChanged("Some test text");

Что, в свою очередь, вызывает следующий метод в Activity:

@Override
public void onTextChanged(String testText) {
    fragmentB.updateText(testText); 

}

И, наконец, этот метод в FragmentB:

private void updateText(String incomingText){
    myEditText.setText(incomingText);
}

И все, это работает отлично, пока я не поверну устройство, вызывающее воссоздание Activity. Затем, когда я нажимаю кнопку, представление myEditText имеет значение null в FragmentB в методе updateText.

Я пытался понять, почему в течение 2 дней, поэтому мне было интересно, может ли кто-нибудь определить, где я ошибаюсь, или указать мне правильное направление, где я могу понять, что вызывает проблему?


person Donal Rafferty    schedule 20.11.2014    source источник
comment
Как вы прикрепляете фрагменты к активности? В oncreate? Если да, то при повороте устройства onCreate будет вызываться с пакетом, который не будет нулевым. Поэтому вы должны проверить, является ли savedInstanceState нулевым, и только потом прикреплять фрагменты. Кроме того, я бы избавился от обратного вызова onAttach и ссылки, которую вы сохраняете во фрагментах, на активность. Вместо этого, когда вам нужно вызвать слушателя в действии, просто выполните getActivity() в самом фрагменте.   -  person Joao Sousa    schedule 21.11.2014
comment
Еще одна вещь: в адаптере вы также сохраняете экземпляры фрагментов вместо создания новых фрагментов? Если это так, проверьте этот ответ.   -  person Joao Sousa    schedule 21.11.2014
comment
Я сам не прикрепляю фрагменты вручную, я полагаю, что FragmentPagerAdapter делает это автоматически в методе getItem?   -  person Donal Rafferty    schedule 21.11.2014


Ответы (2)


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

@Override
public void onTextChanged(String testText) {
    Fragment fragmentB = getFragmentManager().findFragmentByTag("FragmentB");
    fragmentB.updateText(testText);
}

И, конечно же, не забудьте установить тег фрагмента либо в XML, либо в коде.

person Bruce    schedule 21.11.2014
comment
Дело в том, что я не думаю, что смогу использовать это с FragmentPAgerAdapter, поскольку у фрагментов нет идентификатора? - person Donal Rafferty; 21.11.2014
comment
Если у вас есть только один экземпляр каждого типа фрагмента, все, что вам нужно сделать, это установить идентификатор или тег сразу после вызова newInstance. Таким образом, вы знаете, что каждый экземпляр FragmentA или FragmentB будет иметь тег или идентификатор, который вы ему присвоили. Android сохранит тег и идентификатор фрагмента после повторного создания фрагментов после поворота. - person Bruce; 21.11.2014

Я согласен с Брюсом, но если вам нужно сохранить ссылку на фрагмент в действии, вы можете сделать что-то вроде этого:

В своей деятельности реализуйте метод, скажем, «setFragmentInstance».

В вашем фрагменте переопределите метод onActivityCreated, в котором вы можете либо преобразовать активность в свою активность (если фрагмент всегда остается в этой активности) и вызвать этот метод setFragmentInstance, либо вы также можете добавить интерфейс во фрагмент, реализовать его активность и вызывает метод интерфейса в onActivityCreated для установки экземпляра фрагмента.

Я задавал один подобный вопрос раньше, но вскоре после этого мне стало ясно, см. Элементы DialogFragment становятся нулевыми после поворота экрана

person Qianqian    schedule 21.11.2014