ViewPager не перерисовывает содержимое, остается/не заполняется
Мы страдаем от очень странной проблемы с ViewPager. Мы вставляем списки на каждую страницу ViewPager и запускаем notifyDataSetChanged как в адаптере списка, так и в адаптере пейджера представления при обновлении данных списка.
Что мы наблюдаем, так это то, что иногда страница не обновляет свое дерево представлений, т.е. остается пустым или иногда даже исчезает при подкачке к ней. При переходе назад и вперед несколько раз содержимое будет внезапно появляться. Кажется, что Android отсутствует в обновлении для просмотра. Я также заметил, что при отладке с помощью средства просмотра иерархии выбор представления всегда заставит его снова появиться, по-видимому, потому, что просмотрщик иерархии заставляет выбранное представление перерисовывать себя.
Я не мог сделать эту работу программно; недействительность представления списка или всего пейджера представления не имела никакого эффекта.
Это с библиотекой совместимости v4_r7. Я также попытался использовать последнюю версию, поскольку он утверждает, что исправляет многие проблемы, связанные с представлением пейджера, но это еще больше усугубило ситуацию (например, жесты были сломаны, чтобы иногда не пропускать страницы через все страницы).
Кто-нибудь еще сталкивается с этими проблемами, или у вас есть представление о том, что может быть причиной этого?
Ответы
Ответ 1
Наконец-то нам удалось найти решение. По-видимому, наша реализация пострадала от двух проблем:
- наш адаптер не удалил представление в
destroyItem()
.
- мы кэшировали представления, поэтому нам нужно было раздуть наш макет только один раз, и поскольку мы не удаляли представление в
destroyItem()
, мы не добавляли его в instantiateItem()
, а просто возвращали кешированный вид, соответствующий к текущей позиции.
Я не смотрел слишком глубоко в исходном коде ViewPager
- и это не совсем ясно, что вы должны это делать, - но в документах говорится:
destroyItem()
Удалите страницу для данной позиции. Адаптер отвечает за удаление представления из своего контейнера, хотя он должен только убедиться, что это делается к тому времени, когда оно возвращается из finalUpdate (ViewGroup).
и:
Очень простой PagerAdapter может использовать страницу "Представления" как ключевые объекты, возвращая их из instantiateItem (ViewGroup, int) после создания и добавляя их в родительскую ViewGroup. Соответствующая реализация destroyItem (ViewGroup, int, Object) удалит View из родительской ViewGroup, а isViewFromObject (View, Object) может быть реализована как объект return == object;.
Итак, я пришел к выводу, что ViewPager
полагается на свой базовый адаптер для явного добавления/удаления своих дочерних элементов в instantiateItem()
/destroyItem()
. То есть, если ваш адаптер является подклассом PagerAdapter
, ваш подкласс должен реализовать эту логику.
Примечание: обратите внимание на этот, если вы используете списки внутри ViewPager
.
Ответ 2
Если ViewPager
установлен внутри фрагмента с FragmentPagerAdapter
, используйте getChildFragmentManager()
вместо getSupportFragmentManager()
в качестве параметра для инициализации FragmentPagerAdapter
.
mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());
Вместо
mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
Ответ 3
У меня была такая же проблема, но я фактически уничтожил представление в destroyItem (я думал). Однако проблема заключалась в том, что я уничтожил ее с помощью viewPager.removeViewAt(index);
insted 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);
}
Ответ 4
ViewPager пытается делать умные вещи вокруг повторного использования элементов, но для этого требуется, чтобы вы вернули новые позиции позиций, когда все изменилось. Попробуйте добавить это в свой PagerAdapter:
public int getItemPosition (Object object) { return POSITION_NONE; }
В основном это говорит ViewPager, что все изменилось (и заставляет его повторно создавать все). Это единственное, что я могу вспомнить с головы.
Ответ 5
У меня была проблема с теми же симптомами, но другая причина, которая оказалась глупой ошибкой с моей стороны. Думаю, я добавлю его здесь, если он поможет кому-нибудь.
У меня был ViewPager с использованием FragmentStatePagerAdapter, который имел два фрагмента, но позже я добавил третий. Тем не менее, я забыл, что ограничение по умолчанию на странице экрана - 1 - поэтому, когда я переключусь на новый третий фрагмент, первый будет уничтожен, а затем воссоздан после переключения. Проблема заключалась в том, что моя деятельность отвечала за уведомление этих фрагментов для инициализации их пользовательского интерфейса. Это произошло, когда жизненные циклы активности и фрагмента были одинаковыми, но чтобы исправить это, мне пришлось изменить фрагменты, чтобы инициализировать свой собственный пользовательский интерфейс в течение их жизненного цикла запуска. В итоге я также запустил изменение setOffscreenPageLimit на 2, чтобы все три фрагмента были сохранены в любое время (в этом случае они безопасны, так как они не очень интенсивно для памяти).
Ответ 6
В библиотеке поддержки Android есть демонстрационная активность, которая включает ViewPager с ListView на каждой странице. Вероятно, вам стоит взглянуть и посмотреть, что он делает.
В Eclipse (с Android Dev Tools r20):
- Выберите
New > Android Sample Project
- Выберите целевой уровень API (я предлагаю новейший доступный)
- Выберите
Support4Demos
- Щелкните правой кнопкой мыши проект и выберите
Android Tools > Add Support Library
- Запустите приложение и выберите
Fragment
, а затем Pager
Код для этого находится в src/com.example.android.supportv4.app/FragmentPagerSupport.java
. Удачи!
Ответ 7
Я столкнулся с этим и имел очень похожие проблемы. Я даже спросил его о переполнении стека.
Для меня в родительском элементе родителя моего представления кто-то подклассифицирован LinearLayout
и переопределяет requestLayout()
без вызова super.requestLayout()
. Это предотвратило вызов onMeasure
и onLayout
на мой ViewPager (хотя иерархический просмотр вручную их вызывает вручную). Без измерения они будут отображаться как пустые в ViewPager.
Итак, проверьте свои содержащие представления. Удостоверьтесь, что они подклассы из View и не слепо переопределяют requestLayout или что-то подобное.
Ответ 8
Имела ту же проблему, что связано с ListView
(потому что мой пустой вид отображается отлично, если список пуст). Я просто назвал requestLayout()
проблематичным ListView
. Теперь он рисует отлично!
Ответ 9
Я столкнулся с этой проблемой при использовании 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();
}
Ответ 10
Для меня проблема возвращалась к активности после того, как процесс приложения был убит. Я использую настраиваемый адаптер пейджера просмотра, измененный из источников Android. Пейджер представления встроен непосредственно в действие.
Вызов viewPager.setCurrentItem(position, true);
(с анимацией) после установки данных и notifyDataSetChanged(), похоже, работает, но если параметр установлен в false, это не так, и фрагмент пуст. Это краевой случай, который может кому-то помочь.
Ответ 11
У меня была аналогичная проблема. Я просматриваю кеширование, потому что мне нужно всего 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];