ViewPager не перерисовывает содержимое, остается / становится пустым


мы страдаем от очень странной проблемы с ViewPager здесь. Мы вставляем списки на каждой странице ViewPager и запускаем notifyDataSetChanged как на адаптере списка, так и на адаптере пейджера представления при обновлении данных списка.

мы наблюдаем, что иногда страница не обновляет свое дерево просмотра, т. е. остается пустой, а иногда даже исчезает при подкачке к нему. При перелистывании назад и вперед несколько раз, содержание внезапно появится снова. Кажется, что Android отсутствует посмотреть обновление можно здесь. Я также заметил, что при отладке с помощью Hierarchy viewer выбор представления всегда заставит его снова появиться, по-видимому, потому, что Hierarchy viewer заставляет выбранное представление перерисовывать себя.

Я не мог сделать эту работу программно, хотя; аннулирование представления списка или даже всего пейджера представления не имело никакого эффекта.

Это с библиотекой совместимости-v4_r7. Я также попытался использовать последнюю версию, поскольку она утверждает, что исправляет многие проблемы, связанные с просмотр пейджера, но это еще больше ухудшило ситуацию (например, жесты были сломаны, чтобы иногда он не позволял мне перелистывать все страницы.)

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

11 83

11 ответов:

нам наконец-то удалось найти решение. По-видимому, наша реализация пострадала от двух проблем:

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

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

destroyItem()
удалить страницу для данной позиции. Адаптер отвечает за удаление представления из своего контейнера, хотя он должен только гарантировать, что это будет сделано к моменту его возвращения из finishUpdate(ViewGroup).

и:

очень простой PagerAdapter может выбрать, чтобы использовать страницу Рассматривает себя как ключевые объекты, возвращая их из instantiateItem (ViewGroup, int) после создания и добавления их в родительскую ViewGroup. Соответствующая реализация destroyItem(ViewGroup, int, Object) удалит представление из родительской ViewGroup, а isViewFromObject(View, Object) может быть реализован как return view == object;.

Итак, мой вывод таков ViewPager полагается на свой базовый адаптер для явного добавления / удаления его дочерних элементов в instantiateItem()/destroyItem(). То есть, если ваш адаптер является подклассом PagerAdapter, ваш подкласс должен реализовать эту логику.

Примечание стороны: будьте в курсе этой если вы используете списки внутри ViewPager.

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

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

вместо

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

у меня была точно такая же проблема, но я фактически уничтожил вид в destroyItem (я думал). Однако проблема в том, что я уничтожил его с помощью viewPager.removeViewAt(index); вместо viewPager.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);
}

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

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

это в основном говорит ViewPager, что все изменилось (и заставляет его повторно создавать все). Это единственное, что приходит мне в голову.

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

У меня был ViewPager с использованием FragmentStatePagerAdapter, который раньше имел два фрагмента, но позже я добавил третий. Однако я забыл, что ограничение по умолчанию для страницы вне экрана равно 1-поэтому, когда я переключусь на новый третий фрагмент, первый будет уничтожен, а затем воссоздан после переключения обратно. Проблема было то, что моя деятельность отвечала за уведомление этих фрагментов для инициализации их состояния пользовательского интерфейса. Это произошло, когда жизненные циклы activity и fragment были одинаковыми, но чтобы исправить это, мне пришлось изменить фрагменты, чтобы инициализировать их собственный пользовательский интерфейс во время их жизненного цикла запуска. В конце концов, я также изменил setOffscreenPageLimit на 2, чтобы все три фрагмента всегда оставались живыми (безопасными в этом случае, поскольку они не были очень интенсивными для памяти).

библиотека поддержки 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. Удачи вам!

я столкнулся с этим и очень похожие вопросы. Я даже он попросил на переполнение стека.

для меня, в родитель родитель на мой взгляд кто-то подкласса LinearLayout и переопределяло requestLayout() без вызова super.requestLayout(). Это помешало onMeasure и onLayout от вызова на моем ViewPager (хотя hierarchyviewer вручную вызывает их). Без измерения они будут отображаться как пустые в ViewPager.

так что проверьте ваши содержащие представления. Убедитесь, что они подкласс из поля зрения и не слепо переопределить requestLayout или что-нибудь подобное.

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

я столкнулся с этой же проблемой при использовании 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();
     }

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

вызов viewPager.setCurrentItem(position, true);

(с анимацией) после установки данных и notifyDataSetChanged (), кажется, работает, но если параметр установлен в false, это не так, и фрагмент пуст. Это крайний случай, который может кому-то помочь.

у меня была аналогичная проблема. Я кэширую представления, потому что мне нужно только 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];