Поворот экрана с помощью ViewPager - проблема с FragmentManager и FragmentPagerAdapter

1

У меня возникли некоторые проблемы при работе с фрагментом с помощью ViewPager.

Что у меня есть:
MainActivity (скажем, MainActivity), которое содержит ViewPager для отображения некоторых фрагментов. Некоторые из них содержат интерфейс обратного вызова, который будет вызываться для выполнения MainActivity либо MainActivity в MainActivity.
MainActivity имеет класс FragmentPagerAdapter, который используется в качестве адаптера ViewPager. И List<Fragment> в FragmentPagerAdapter для хранения некоторого фрагмента, который будет отображаться в ViewPager.

Что я ожидаю:
При первом запуске Fragment вызывал методы интерфейса обратного вызова, когда я нажимал на кнопку, и MainActivity делала что-то внутри этого. Это работало отлично.
После поворота экрана я ожидал, что он будет работать так же, как при первом запуске, НО
NullPointerException: попытка вызвать метод для нулевого ссылочного объекта (в частности, интерфейса Fragment) ударила меня по лицу.

Что я знаю:
- getItem(int position) больше не будет вызываться после создания фрагмента. Вместо этого будет вызван instantiateItem(ViewGroup container, int position).
- FragmentManager сохранит некоторый фрагмент в mActive.mValues
ViewPager и фрагменты - как правильно сохранить состояние фрагмента? (Я ссылался на эту и некоторые другие те же темы в StackOverflow.)

Что я попробовал и увидел:
- Переопределить instantiateItem(ViewGroup container, int position)
- отлажено за 1 день. Я видел, что когда я getSupportFragmentManager() в методе MainActivity onCreate() суперструктору FragmentPagerAdapter, при первом запуске он имеет "адрес в памяти", предположим, что это "1a1a1a1". mActive.mValues FragmentManager сохранил некоторый "адрес в памяти" фрагмента, который идентичен содержащему их List<Fragment> (предположим, что это был "qwertyu"). Что означало, что это было правильно.
Но когда я повернул экран, снова передав getSupportFragmentManager(), "адрес в памяти" был совершенно другим, предположим, что "9f9f9f9". А FragmentManager mActive.mValues содержал другой набор фрагментов "адрес в памяти" (предположим, что "abcdeff"), хотя количество фрагментов в нем было равно числу фрагментов, которые были сохранены при первом запуске (до вращения),
Я добавил фрагмент в List<Fragment> с новым "адресом в памяти" (предположим, "abababa"), имеет интерфейс обратного вызова. Но когда я нажал на кнопку в нем, это был фрагмент, который был в FragmentManager mActive.mValues после поворота (с "адресом в памяти" является "abcdeff", как я предполагал выше), и тот не имел обратного вызова интерфейс (из-за того, что не был установлен в MainActivity первую очередь). И вызвал исключение NullPointerException, как упомянуто выше.

Мои вопросы сейчас:
- Прежде всего, как избавиться от этой проблемы !? Было бы лучше продолжать использовать FragmentPagerAdapter вместо другого класса. Но я рассмотрю возможность использования другого класса.
- Во-вторых, вы можете объяснить, почему FragmentManager сохранил экземпляр Fragment до ротации. Но после поворота он создает совершенно другой экземпляр Fragment, но все равно использует его вместо фрагмента, который был сохранен в List<Fragment>?

Вот код (я думаю, что я не использовал метод instantiateItem(ViewGroup container, int position) правильно, поэтому он все еще вызывал проблему).

public class MainActivity extends AppCompatActivity {

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

        //Attach the SectionsPagerAdapter to the ViewPager
        SectionsPagerAdapter pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
        ViewPager viewPager = findViewById(R.id.viewPager);
        viewPager.setAdapter(pagerAdapter);
    }


    //
    //
    //Adapter class
    private class SectionsPagerAdapter extends FragmentPagerAdapter {
        private static final int PAGE_HOME = 0;
        private int tabCount = 1;
        private List<Fragment> fragmentList = new ArrayList<>();
        private List<String> fragmentTitleList = new ArrayList<>();
        //private FragmentManager fragmentManager;

        SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
            //fragmentManager = fm;

            //Default HomeFragment
            HomeFragment homeFragment = new HomeFragment();
            //Callback interface
            homeFragment.setOnCategoryFragmentChangedListener(new HomeFragment.OnCategoryFragmentChangedListener() {
                //This method will be called when a button in HomeFragment is clicked
                @Override
                public void onAddNewCategory(String categoryName) {
                    addNewCategory(categoryName);
                }
            });
            fragmentList.add(homeFragment);
            fragmentTitleList.add("Home");
        }

        @Override
        public Fragment getItem(int position) {
            return fragmentList.get(position);
        }

        @Override
        public int getCount() {
            return tabCount;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return fragmentTitleList.get(position);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            fragmentList.set(position, (Fragment) super.instantiateItem(container, position));
            return fragmentList.get(position);
        }

        private void addNewCategory(String categoryName) {
            CategoryFragment fragment = new CategoryFragment();

            tabCount += 1;
            fragmentList.add(fragment);
            fragmentTitleList.add(categoryName);
            notifyDataSetChanged();
        }
    }
}

Пожалуйста помоги. Я схожу с ума уже 2 дня...!

Теги:
android-fragments

2 ответа

0
Лучший ответ

Ну что ж, сразу после того, как я заснул, я нашел решение. Это правда, что я не использовал метод instantiateItem() правильно. После повторной отладки я обнаружил, что метод instantiateItem() получает вызов всякий раз, когда я смахиваю (или выбираю, если использую также TabLayout) на другой фрагмент, и даже получаю вызов до getItem(int pos), независимо от того, что это при первом запуске или после вращение. Вот почему я думаю, что мы должны настроить Fragment в методе instantiateItem().
Итак, вот как я сейчас использую метод instantiateItem():

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            fragmentList.set(position, (Fragment) super.instantiateItem(container, position));
            Fragment fragment = fragmentList.get(position);
            if (position == PAGE_HOME) {
                ((HomeFragment) fragment).setOnCategoryFragmentChangedListener(new HomeFragment.OnCategoryFragmentChangedListener() {
                    @Override
                    public void onAddNewCategory(String categoryName) {
                        addNewCategory(categoryName);
                    }
                });
            }
            return fragment;
        }

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

0

Я считаю, что Android перезапускает действие во время изменения ориентации, создавая несколько экземпляров FragmentPagerAdapter и несколько экземпляров List.

Я не совсем понимаю ваш вопрос, но подозреваю, что instantiateItem ничего не делает в любом случае. Разве Fragment getItem(int pos) работает без переопределения instantiateItem()?

  • 0
    Да, getItem(int pos) работал без переопределения instantiateItem() . Но, как я уже сказал, после поворота экрана getItem(int pos) больше не будет вызываться для фрагментов, которые уже были созданы при первом запуске. В коде будет создан новый экземпляр HomeFragment, который будет добавлен в List<Fragment> ; но ViewPager не отображал этот экземпляр (который был добавлен в List ), вместо этого он отображал экземпляр в mActive.mValues FragmentManager , что вызвало NPE.
  • 0
    Мой вопрос о: «Как отобразить фрагменты, которые не из mActive.mValues FragmentManager (но в новом списке <Fragment>) после поворота экрана? (Дополнительный вопрос: и я должен сделать это, воссоздав List<> затем использовать его? Есть ли лучший способ, как переработка или повторное использование уже созданного фрагмента? ")

Ещё вопросы

Сообщество Overcoder
Наверх
Меню