Поле фрагмента равно нулю после поворота устройства в Android - PullRequest
0 голосов
/ 30 декабря 2018

Когда я запускаю приложение, все работает нормально, но когда я поворачиваюсь в альбомную ориентацию, он падает, потому что в Fragment есть поле, NULL.

Я не использую setRetainInstance(true) или добавление фрагментов.FragmentManager Я создаю новые фрагменты при запуске приложения и при его повороте.

В Activity OnCreate() я создаю Fragment и добавляю их к viewPager следующим образом.

   protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         ParentBasicInfoFragment parentBasicInfoFragment = new ParentBasicInfoFragment();
         ParentUTCFragment parentUTCFragment = new ParentUTCFragment();
         ParentEventsFragment parentEventsFragment = new ParentEventsFragment();
         this.mFragments = new ArrayList<>();
         this.mFragments.add(parentBasicInfoFragment);
         this.mFragments.add(parentUTCFragment);
         this.mFragments.add(parentEventsFragment);
         this.viewpage.setOffscreenPageLimit(3);
         setCurrentTab(0);
         this.viewpage.setAdapter(new MainActivityPagerAdapter(getSupportFragmentManager(), this.mFragments));
    }

Тогда у меня есть кнопка тестирования в приложении, которая при нажатии будет работать как

  public void test(View view) {
      ((BaseFragment) MainActivity.this.mFragments.get(MainActivity.this.viewpage.
                getCurrentItem())).activityNotifiDataChange("hello");
  }

Это будет работать, и текущий Fragments в ViewPager будет иметьметод, activityNotifiDataChange(), который вызывается, и все в порядке.

Когда я поворачиваю приложение и делаю то же самое, нажимая кнопку, activityNotifiDataChange() вызывается нормально, но есть исключение нулевого указателя, потому чтоArrayList<Fragment> mFragment теперь NULL.

Вот небольшой пример проекта Android Studio, показывающий это поведение: https://drive.google.com/file/d/1Swqu59HZNYFT5hMTqv3eNiT9NmakhNEb/view?usp=sharing

Запустите приложение и нажмите кнопку с именем «PRESS TEST», затем поверните устройство и нажмитеснова нажмите кнопку и посмотрите, как происходит сбой приложения

ОБНОВЛЕНИЕ РЕШЕНИЯ благодаря @GregMoens и @ EpicPandaForce

public class MainActivityPagerAdapter extends PersistenPagerAdapter<BaseFragment> {

    private static int NUM_ITEMS = 3;

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

    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return ParentBasicInfoFragment.newInstance(0, "Page # 1");
            case 1:
                return ParentUTCFragment.newInstance(1, "Page # 2");
            case 2:
                return ParentEventsFragment.newInstance(2, "Page # 3");
            default:
                return null;
        }
    }
}

public abstract class PersistenPagerAdapter<T extends BaseFragment> extends FragmentPagerAdapter {
    private SparseArray<T> registeredFragments = new SparseArray<T>();

    public PersistenPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public T instantiateItem(ViewGroup container, int position) {
        T fragment = (T)super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public T getRegisteredFragment(ViewGroup container, int position) {
        T existingInstance = registeredFragments.get(position);
        if (existingInstance != null) {
            return existingInstance;
        } else {
            return instantiateItem(container, position);
        }
    }
}

Ответы [ 4 ]

0 голосов
/ 30 декабря 2018

Основная проблема, которую я вижу с вашим приложением, заключается в том, что вы неправильно понимаете, как работает FragmentPagerAdapter.Я часто это вижу, и это связано с отсутствием хороших javadocs в классе.Адаптер должен быть реализован так, чтобы при вызове getItem (position) возвращал новый экземпляр фрагмента.И тогда getItem (position) будет вызываться пейджером только тогда, когда ему нужен новый экземпляр для этой позиции.Вы не должны предварительно создавать фрагменты и затем переходить в адаптер.Вы также не должны держать сильные ссылки на фрагменты из вашей деятельности или из родительских фрагментов (например, ParentBasicInfoFragment).Помните, что менеджер фрагментов управляет фрагментами, а вы также управляете фрагментами, обновляя их и сохраняя ссылки на них.Это вызывает конфликт, и после ротации вы пытаетесь вызвать activityNotifiDataChange () для фрагмента, который фактически не инициализирован (onCreate () не был вызван).Использование отладчика и идентификаторов объектов отслеживания подтвердит это.

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

0 голосов
/ 30 декабря 2018

Использование setRetainInstance(true) не очень хороший подход.Если вам нужна такая же простая информация, как: позиция recyclerView, выбранный элемент recyclerView, возможно, какая-то модель (Parcelable), вы можете сделать это с помощью метода onSaveInstanceState / onRestoreInstanceState, , есть одно ограничение - 1 МБ . Вот пример с анимацией, как это работает.

Для более длительного использования стойкости используйте SharedPreferences , Комната (Google ORM) или вы можете попробоватьиспользовать ViewModel с LiveData (лучше всего подходит для некоторых данных, которые должны существовать во время сеанса пользователя).

0 голосов
/ 30 декабря 2018
this.mFragments = new ArrayList<>();

Потому что это неправильно.Вы никогда не должны содержать ссылку на список фрагментов, если вы используете ViewPager.Просто верните новый экземпляр Fragment из getItem(int position) из FragmentPagerAdapter, и все готово.

Но чтобы исправить свой код, вы должны полностью удалить mFragments.

0 голосов
/ 30 декабря 2018
//change in AndroidManifest.xml file,
<activity
        android:name=".activity.YourActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:screenOrientation="sensor"
         />

//YourActivity in which you defines fragment.
 //may be it helps
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...