ViewPager не перерисовує вміст, залишається / стає порожнім


86

Ми страждаємо від дуже дивної проблеми з ViewPager тут. Ми вбудовуємо списки на кожну сторінку ViewPager і запускаємо notifyDataSetChanged як на адаптері списку, так і на адаптері перегляду пейджера подання під час оновлення даних списку.

Ми спостерігаємо те, що іноді сторінка не оновлює своє дерево перегляду, тобто залишається порожньою, а іноді навіть зникає під час пошуку на неї. Коли кілька разів прокручується сторінка вперед-назад, вміст раптово з’явиться знову. Здається, що Android не має тут оновлення представлення. Я також помітив, що при налагодженні засобу перегляду ієрархії, вибір подання завжди змусить його з’являтися знову, мабуть, тому, що засіб перегляду ієрархії змушує обраний вигляд перемальовувати себе.

Хоча я не міг зробити цю роботу програмно; скасування недійсності подання списку або навіть повного пейджера подання не мало ефекту.

Це стосується бібліотеки compatibility-v4_r7. Я також намагався використати останню редакцію, оскільки вона стверджує, що виправляє багато проблем, пов'язаних з переглядом пейджера, але це ще більше погіршує ситуацію (наприклад, жести порушуються, так що іноді мені більше не дозволяється перебирати всі сторінки).

Хто-небудь ще стикався з цими проблемами, чи ви уявляєте, що може бути причиною цього?

Відповіді:


44

Нарешті нам вдалося знайти рішення. Очевидно, наша реалізація страждала від двох питань:

  1. наш адаптер не видалив подання в destroyItem().
  2. ми кешували подання, щоб нам довелося надути макет лише один раз, і, оскільки ми не видаляли подання destroyItem(), ми не додавали його, instantiateItem()а просто повертали кешований вигляд, відповідний поточній позиції.

Я не надто глибоко заглядав у вихідний код ViewPager- і це не зовсім чітко, що ви повинні це робити - але в документі сказано:

знищитиItem ()
Видалити сторінку для заданої позиції. Адаптер відповідає за видалення подання зі свого контейнера, хоча він повинен переконатися, що це зроблено лише до моменту повернення з finishUpdate (ViewGroup).

і:

Дуже простий PagerAdapter може вибрати використовувати перегляди сторінок як ключові об'єкти, повертаючи їх з instantiateItem (ViewGroup, int) після створення та додаючи їх до батьківської ViewGroup. Відповідна реалізація знищенняItem (ViewGroup, int, Object) видалить View з батьківського ViewGroup, а isViewFromObject (View, Object) може бути реалізовано як return view == object;

Отже, мій висновок полягає в тому, що ViewPagerпокладається на базовий адаптер, який явно додає / видаляє своїх дочірніх елементів у instantiateItem()/ destroyItem(). Тобто, якщо ваш адаптер є підкласом PagerAdapter, ваш підклас повинен реалізувати цю логіку.

Примітка: пам’ятайте про це, якщо ви використовуєте списки всередині ViewPager.


2
У мене така сама проблема, але з FragmentPagerAdapter, який обробляє onDestroy для мене. Жодне з інших рішень тут також не працює.
Грег Енніс

14
Проблемою може бути також те, що хтось використовує getFragmentManger замість GetChildFragmentManager
Хлопчик

@Boy, що таке GetChildFragmentManager?
пожежа в норі

1
@Boy Wooooow .... Цілі два дні я виривав волосся, намагаючись зрозуміти, чому я не міг нічого оновити. ДЯКУЮ! (вони повинні дійсно, дійсно помітити в документації Google, щоб використовувати керуючий дочірніми файлами, тобто зовсім не те, що вам потрібен інший менеджер).
user0721090601

@futtetennista я роблю те ж саме .. перед це питання .. ви можете перевірити .. stackoverflow.com/questions/61727835 / ...
AskQ

55

Якщо значення ViewPagerвстановлено у фрагменті з a FragmentPagerAdapter, використовуйте getChildFragmentManager()замість параметра getSupportFragmentManager()як параметр для ініціалізації вашого FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

Замість

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

2
Хороший улов, здається, це вирішує мою проблему при використанні FragmentPagerAdapter
Tobliug

1
Дякую - це спрацювало. Я намагався змусити getItem()на FragmentPagerAdapterякий була вкладена в відтвореному фрагменті.
kosiara - Bartosz Kosarzycki

вау ця робота, як чарівність. Думаю, проблема пов’язана з надуванням фрагмента перегляду
сторінок перегляду

Такі часи, як я, я хотів би, щоб у нас були такі плескання, як Середні, щоб я міг дати вам 1000+ хлопань для цього. Хороша робота, це має бути прийнятою відповіддю
Codelicious

21

У мене була точно така ж проблема, але я насправді знищив вигляд у killItem (я думав). Проте проблема полягала в тому, що я знищив його, використовуючи viewPager.removeViewAt(index);insted ofviewPager.removeView((View) object);

Неправильно:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

Праворуч:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}

Дякую за підказку. Довгий час вивчав проблему.
Моріц

2
Це спрацювало для мене, але чи знаєте ви, чому перша проблема?
HannahMitt

@HannahMitt removeViewAt видалить будь-який вигляд на цій конкретній позиції в поточному списку дітей ViewPager, а сторінки можуть додаватися до ViewPager у будь-якому порядку (тому сторінка 0 може бути насправді в ViewPager за індексом 1 або іншим). removeView буде перебирати список дочірніх об'єктів і видаляти точно вказаний об'єкт.

2
Не працює для мене: Не вдається передати перегляд фрагменту. об'єктом тут є фрагмент.
FRK

viewpager повинен бути статичним?
Канагалінгам,

10

ViewPager намагається робити розумні речі навколо повторного використання елементів, але вимагає повернення нових позицій елементів, коли щось змінилося. Спробуйте додати це до свого PagerAdapter:

public int getItemPosition (Object object) { return POSITION_NONE; }

Це в основному повідомляє ViewPager, що все змінилося (і змушує його повторно створити все). Це єдине, що я можу придумати з маківки.


1
Так, я читав про цю опцію в цій темі: stackoverflow.com/questions/7263291/… - однак, це схоже на підхід кувалди. Напевно, повинен бути більш елегантний спосіб? Цікаво, чи пов’язано це з використанням списків як сторінок пейджера?
Маттіас

Це трохи підхід кувалди, але з цього можна попрацювати. тобто повернути правильне значення з методу.
Кріс Бейнс,

@ChrisBanes Як ви вже говорили, зателефонувавши return POSITION_NONE;, forces it to re-instantiate everythingале в моєму випадку я не хочу повторно створювати всі екземпляри , то чи можна було б це видалити, viewне очищаючи наявні речі? Будь ласка, дайте мені знати
Ritesh Adulkar


2

Спробував забагато рішень, але несподівано viewPager.post()вдалося

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });

1
Боже мій. чому ніхто не підтримав це? Я отримую дивну поведінку, коли повторно ввожу фрагмент, а переглядач всередині не відтворюється сам, і трохи затримуючи його, це насправді вирішило проблему!
Фугогуго

0

Бібліотека підтримки Android має демонстраційну активність, яка включає ViewPager із ListView на кожній сторінці. Ви, мабуть, повинні поглянути і подивитися, що це робить.

В Eclipse (з Android Dev Tools r20):

  1. Виберіть New > Android Sample Project
  2. Виберіть цільовий рівень API (пропоную найновіший з доступних)
  3. Виберіть Support4Demos
  4. Клацніть проект правою кнопкою миші та виберіть Android Tools > Add Support Library
  5. Запустіть програму та виберіть, Fragmentа потімPager

Код для цього в src/com.example.android.supportv4.app/FragmentPagerSupport.java. Удачі!


дякую - я подивлюсь на це! Можливо, я помічу щось, у чому ми помиляємось.
Маттіас

0

Я зіткнувся з цим і мав дуже схожі проблеми. Я навіть запитав це про переповнення стека.

Для мене хтось із батьків одного з моїх поглядів підкласував LinearLayoutі замінив, requestLayout()не викликаючи super.requestLayout(). Це не дозволило onMeasureі onLayoutне було викликано на моєму ViewPager (хоча hierarchyviewer вручну їх викликає). Без вимірювання вони відображатимуться порожніми у ViewPager.

Тож перевірте свої містять погляди. Переконайтеся, що вони підкласу з View і не сліпо перевизначають requestLayout або щось подібне.


0

Була та сама проблема, з якою щось пов’язано ListView(оскільки мій порожній вигляд відображається чудово, якщо список порожній). Я щойно звернувся requestLayout()до проблемного ListView. Тепер він малює чудово!


0

Я зіткнувся з цією ж проблемою під час використання ViewPager та FragmentStatePagerAdapter. Я спробував використати обробник із затримкою в 3 секунди для виклику invalidate () та requestLayout (), але це не спрацювало. Що вдалося, це скидання кольору фону viewPager наступним чином:

MyFragment.java

    private Handler mHandler;
    private Runnable mBugUpdater;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = new ViewPager(getActivity());
        //...Create your adapter and set it here...

        mHandler = new Handler();
        mBugUpdater = new Runnable(){
            @Override
            public void run() {
                mVp.setBackgroundColor(mItem.getBackgroundColor());
                mHandler = null;
                mBugUpdater = null;
            }           
        };
        mHandler.postDelayed(mBugUpdater,50);

        return rootView;
    }

    @Override
    public void onPause() {
        if(mHandler != null){
            //Remove the callback if it hasn't triggered yet
            mHandler.removeCallbacks(mBugUpdater);
            mHandler = null;
            mBugUpdater = null;
        }
        super.onPause();
     }

0

У мене були проблеми з тими ж симптомами, але інша причина, яка виявилася дурною помилкою з мого боку. Думав, я додам його сюди на випадок, якщо це комусь допоможе.

У мене був ViewPager за допомогою FragmentStatePagerAdapter, який раніше мав два фрагменти, але пізніше я додав третій. Однак я забув, що за замовчуванням обмеження розміру сторінки поза екраном - 1, тож, коли я перемикався на новий третій фрагмент, перший з них знищувався, а потім відтворювався після повернення назад. Проблема полягала в тому, що моя діяльність відповідала за повідомлення цих фрагментів для ініціалізації стану їхнього інтерфейсу. Це трапилося, коли активність та фрагменти життєвих циклів були однаковими, але щоб виправити це, мені довелося змінити фрагменти, щоб ініціалізувати власний інтерфейс під час життєвого циклу запуску. Врешті-решт я також завершив зміну setOffscreenPageLimit до 2, щоб усі три фрагменти постійно залишалися живими (в даному випадку безпечно, оскільки вони не потребували великої пам’яті).


-1

У мене була подібна проблема. Я кешую перегляди, оскільки мені потрібно лише 3 перегляди в ViewPager. Коли я ковзаю вперед, все в порядку, але коли я починаю ковзати назад, виникає помилка, це говорить про те, що "у моєму поданні вже є батько". Рішення - видалити непотрібні елементи вручну.

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];

-1

Для мене проблема поверталася до активності після того, як процес програми був убитий. Я використовую спеціальний адаптер пейджера подання, модифікований з джерел Android. Пейджер подання вбудований безпосередньо в діяльність.

Дзвінок viewPager.setCurrentItem(position, true);

(з анімацією) після встановлення даних і notifyDataSetChanged (), здається, працює, але якщо для параметра встановлено значення false, це не означає, а фрагмент порожній. Це надзвичайний випадок, який може комусь допомогти.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.