Я использую ViewPager из библиотеки совместимости. У меня, к счастью, есть несколько просмотров, которые я могу пролистать.
Тем не менее, мне трудно понять, как обновить ViewPager с новым набором представлений.
Я перепробовал все виды звонков mAdapter.notifyDataSetChanged()
, mViewPager.invalidate()
даже создавая новый адаптер каждый раз, когда хочу использовать новый Список данных.
Ничего не помогло, текстовые представления остаются неизменными по сравнению с исходными данными.
Обновление: я сделал небольшой тестовый проект, и я почти смог обновить представления. Я вставлю класс ниже.
Однако, что не обновляется, так это 2-й вид, «B» остается, он должен отображать «Y» после нажатия кнопки обновления.
public class ViewPagerBugActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
Button updateButton = (Button) findViewById(R.id.update_button);
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateViewPager();
}
});
}
private void updateViewPager() {
data.clear();
data.add("X");
data.add("Y");
data.add("Z");
myViewPager.getAdapter().notifyDataSetChanged();
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
android
android-viewpager
C0deAttack
источник
источник
Ответы:
Есть несколько способов добиться этого.
Первый вариант проще, но немного неэффективен.
Переопределите
getItemPosition
в вашем,PagerAdapter
как это:Таким образом, при вызове
notifyDataSetChanged()
пейджер представлений удалит все представления и перезагрузит их все. Как таковой эффект перезагрузки получается.Второй вариант, предложенный Альваро Луисом Бустаманте (ранее alvarolb) , заключается в
setTag()
методеinstantiateItem()
создания экземпляра нового представления. Затем вместо того, чтобы использоватьnotifyDataSetChanged()
, вы можете использовать,findViewWithTag()
чтобы найти представление, которое вы хотите обновить.Второй подход очень гибкий и высокопроизводительный. Слава Альваролбу за оригинальное исследование.
источник
Я не думаю, что есть какая-то ошибка в
PagerAdapter
. Проблема в том, что понять, как это работает, немного сложно. Глядя на решения, объясненные здесь, на мой взгляд, возникает недопонимание и, следовательно, неэффективное использование экземпляров.Последние несколько дней я работал с
PagerAdapter
иViewPager
, и я нашел следующее:notifyDataSetChanged()
Метод наPagerAdapter
только уведомляет ,ViewPager
что основные страницы изменились. Например, если вы создали / удалили страницы динамически (добавляя или удаляя элементы из вашего списка), об этомViewPager
следует позаботиться. В этом случае я думаю , чтоViewPager
определяет , будет ли новый вид должен быть удален или экземпляр , используяgetItemPosition()
иgetCount()
методы.Я думаю, что
ViewPager
послеnotifyDataSetChanged()
вызова принимает его дочерние представления и проверяет их позицию сgetItemPosition()
. Если для дочернего представления этот метод возвращаетсяPOSITION_NONE
, онViewPager
понимает, что представление было удалено, вызываяdestroyItem()
и удаляя это представление.Таким образом, переопределение
getItemPosition()
всегда возвращатьPOSITION_NONE
является абсолютно неправильным, если вы хотите обновить только содержимое страниц, поскольку ранее созданные представления будут уничтожены, а новые будут создаваться при каждом вызовеnotifyDatasetChanged()
. Может показаться, что это не так неправильно всего на несколькоTextView
секунд, но когда у вас есть сложные представления, такие как ListViews, заполняемые из базы данных, это может быть реальной проблемой и пустой тратой ресурсов.Таким образом, существует несколько подходов для эффективного изменения содержимого представления без необходимости повторного удаления и создания экземпляра представления. Это зависит от проблемы, которую вы хотите решить. Мой подход заключается в использовании
setTag()
метода для любого экземпляра вinstantiateItem()
методе. Поэтому, когда вы хотите изменить данные или сделать недействительным нужное представление, вы можете вызватьfindViewWithTag()
методViewPager
для получения ранее созданного экземпляра представления и изменить / использовать его по своему усмотрению без необходимости удалять / создавать новое представление каждый раз, когда вы хотите обновить какое-то значение.Представьте, например, что у вас есть 100 страниц со 100
TextView
с, и вы хотите периодически обновлять только одно значение. С подходами, описанными ранее, это означает, что вы удаляете и создаете экземпляры 100TextView
с при каждом обновлении. Это не имеет смысла...источник
setTag
в виду в методе.onInstantiateItem
Хотели бы вы обновить этот ответ кодированием? Спасибо.Изменение
FragmentPagerAdapter
ToFragmentStatePagerAdapter
.Переопределить
getItemPosition()
метод и вернутьсяPOSITION_NONE
.В конце концов, он будет слушать
notifyDataSetChanged()
пейджер.источник
Ответ, данный alvarolb, безусловно, лучший способ сделать это. Основываясь на его ответе, простой способ реализовать это - просто сохранить активные представления по позициям:
Затем, переопределив
notifyDataSetChanged
метод, вы можете обновить представления ...На самом деле вы можете использовать похожий код
instantiateItem
иnotifyDataSetChanged
для обновления вашего представления. В моем коде я использую точно такой же метод.источник
Была такая же проблема. Для меня это работало над расширением FragmentStatePagerAdapter и переопределением следующих методов:
источник
После нескольких часов разочарования при попытке всех вышеуказанных решений , чтобы преодолеть эту проблему , а также пытается множество решений на других подобные вопросы , как это , это и это , который все FAILED со мной , чтобы решить эту проблему и сделать ,
ViewPager
чтобы разрушить староеFragment
и заполнитьpager
с новыйFragment
с. Я решил проблему следующим образом:1) Сделайте так, чтобы
ViewPager
класс расширялсяFragmentPagerAdapter
следующим образом:2) Создайте элемент для
ViewPager
храненияtitle
иfragment
следующих действий:3) Создайте конструктор объекта
ViewPager
take myFragmentManager
для сохранения его в myclass
следующим образом:4) Создайте метод для переустановки
adapter
данных с новыми данными, удалив все предыдущие непосредственноfragment
из самогоfragmentManager
себя, чтобыadapter
снова установить новыйfragment
из нового списка следующим образом:5) Из контейнера
Activity
илиFragment
не переинициализируйте адаптер с новыми данными. Установите новые данные с помощью методаsetPagerItems
с новыми данными следующим образом:Я надеюсь, что это помогает.
источник
У меня была та же проблема, и мое решение использует
FragmentPagerAdapter
с переопределениемFragmentPagerAdapter#getItemId(int position)
:По умолчанию этот метод возвращает позицию элемента. Я полагаю, что
ViewPager
проверяет,itemId
был ли изменен и воссоздает страницу, только если она была. Но не переопределенная версия возвращает ту же позицию, что иitemId
и в случае, если страница на самом деле отличается, и ViewPager не определяет, что страница заменена, и ее необходимо создать заново.Чтобы использовать это,
long id
необходимо для каждой страницы. Обычно ожидается, что он будет уникальным, но для этого случая я предлагаю, чтобы он просто отличался от предыдущего значения для той же страницы. Таким образом, здесь можно использовать непрерывный счетчик в адаптере или случайные целые числа (с широким распределением).Я думаю, что это более последовательный способ, а не использование тегов зрения, упомянутых в качестве решения в этой теме. Но, вероятно, не для всех случаев.
источник
PagerAdapter
. Что это за класс?FragmentPagerAdapter
Я нашел очень интересное решение этой проблемы. Вместо использования FragmentPagerAdapter , который хранит в памяти все фрагменты, мы можем использовать FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ), который каждый раз перезагружает фрагмент, когда мы его выбираем.
Реализации обоих адаптеров идентичны. Таким образом, нам нужно просто изменить « extension FragmentPagerAdapter » на « extends FragmentStatePagerAdapter »
источник
После долгих поисков этой проблемы я нашел действительно хорошее решение, которое я считаю правильным. По сути, instantiateItem вызывается только тогда, когда создается экземпляр представления, и никогда больше, если только представление не разрушено (это то, что происходит, когда вы переопределяете функцию getItemPosition для возврата POSITION_NONE). Вместо этого вы хотите сохранить созданные представления и либо обновить их в адаптере, сгенерировать функцию get, чтобы кто-то другой мог ее обновить, либо функцию set, которая обновляет адаптер (мой любимый).
Итак, в вашем MyViewPagerAdapter добавьте переменную вроде:
в вашем instantiateItem:
таким образом, вы можете создать функцию, которая будет обновлять ваш вид:
Надеюсь это поможет!
источник
instantiateItem
, поэтому мои представления не обновлялись. По твоему ответу я это понял. +1Через два с половиной года после того, как ФП поставил свой вопрос, эта проблема все еще остается проблемой. Очевидно, что приоритет Google в этом не очень высок, поэтому вместо того, чтобы найти решение, я нашел обходной путь. Большим прорывом для меня стало выяснение истинной причины проблемы (см. Принятый ответ в этом посте ). Как только стало очевидно, что проблема заключается в том, что какие-либо активные страницы не обновляются должным образом, мой обходной путь стал очевидным:
В моем фрагменте (страницы):
В моей деятельности, где я делаю загрузку страниц:
После этого, когда вы перезагрузите второй набор страниц, ошибка все равно заставит некоторых отображать старые данные. Однако теперь они будут обновлены, и вы увидите новые данные - ваши пользователи не будут знать, что страница когда-либо была неверной, потому что это обновление произойдет до того, как они увидят страницу.
Надеюсь, это поможет кому-то!
источник
Все эти решения не помогли мне. таким образом я нашел рабочее решение: можно
setAdapter
каждый раз, но этого недостаточно. Вы должны сделать это перед сменой адаптера:и после этого:
источник
ViewPager
внутри фрагмент, поэтому заменитьslideShowPagerAdapter.getFragmentManager()
наgetChildFragmentManager()
. Может бытьgetFragmentManager()
, поможет в вашем случае. Я использовалFragmentPagerAdapter
, нетFragmentStatePagerAdapter
. Также см. Stackoverflow.com/a/25994654/2914140 для хакерского способа.Гораздо более простой способ: используйте
FragmentPagerAdapter
и оберните свои постраничные представления фрагментами. Они обновляютсяисточник
Спасибо rui.araujo и Альваро Луису Бустаманте. Сначала я пытаюсь использовать путь rui.araujo, потому что это легко. Это работает, но когда данные меняются, страница будет перерисовываться, очевидно. Это плохо, поэтому я стараюсь использовать способ Альваро Луиса Бустаманте. Идеально. Вот код:
И когда данные меняются:
источник
На всякий случай, если кто-то использует FragmentStatePagerAdapter адаптер на основе (который позволит ViewPager создавать минимум страниц, необходимых для отображения, максимум 2 для моего случая), ответ @ rui.araujo о перезаписи getItemPosition в вашем адаптере не вызовет значительных потерь, но все же может быть улучшена.
В псевдокоде:
источник
getItemPosition()
когда изменился набор данных, и ViewPager будет знать, изменились они или нет. к сожалению, ViewPager не сделал этого.У меня была похожая проблема, в которой у меня было четыре страницы, и одна из страниц обновляла представления на трех других. Мне удалось обновить виджеты (SeekBars, TextViews и т. Д.) На странице рядом с текущей страницей. Последние две страницы будут иметь неинициализированные виджеты при вызове
mTabsAdapter.getItem(position)
.Чтобы решить мою проблему, я использовал
setSelectedPage(index)
перед звонкомgetItem(position)
. Это создаст экземпляр страницы, что позволит мне изменять значения и виджеты на каждой странице.После всех обновлений я бы использовал с
setSelectedPage(position)
последующимnotifyDataSetChanged()
.Вы можете увидеть небольшое мерцание в ListView на главной странице обновления, но ничего заметного. Я не проверил это полностью, но это решает мою непосредственную проблему.
источник
Я просто публикую этот ответ на тот случай, если кто-то еще посчитает его полезным. Чтобы сделать то же самое, я просто взял исходный код ViewPager и PagerAdapter из библиотеки совместимости и скомпилировал его в моем коде (вам нужно разобраться со всеми ошибками и импортировать самостоятельно, но это определенно можно сделать).
Затем в CustomViewPager создайте метод с именем updateViewAt (int position). Само представление может быть получено из элементов ArrayList, определенных в классе ViewPager (необходимо установить Id для представлений в экземпляре элемента и сравнить этот идентификатор с позицией в методе updateViewAt ()). Затем вы можете обновить представление по мере необходимости.
источник
Я думаю, у меня есть логика ViewPager.
Если мне нужно обновить набор страниц и отобразить их на основе нового набора данных, я вызываю notifyDataSetChanged () . Затем ViewPager делает несколько вызовов getItemPosition () , передавая ему Fragment как объект. Этот фрагмент может быть либо из старого набора данных (который я хочу удалить), либо из нового (который я хочу отобразить). Итак, я переопределяю getItemPosition (), и там я должен как-то определить, является ли мой фрагмент из старого набора данных или из нового.
В моем случае у меня есть двухпанельный макет со списком верхних элементов на левой панели и представлением пролистывания (ViewPager) справа. Таким образом, я храню ссылку на мой текущий верхний элемент внутри моего PagerAdapter, а также внутри каждого фрагмента страницы, для которого была создана конкретная страница. Когда выбранный верхний элемент в списке изменяется, я сохраняю новый верхний элемент в PagerAdapter и вызываю notifyDataSetChanged () . И в переопределенном getItemPosition () я сравниваю верхний элемент из моего адаптера с верхним элементом из моего фрагмента. И только если они не равны, я возвращаю POSITION_NONE. Затем PagerAdapter восстанавливает все фрагменты, которые вернули POSITION_NONE.
НОТА.Лучше было бы сохранить идентификатор верхнего элемента вместо ссылки.
Фрагмент кода ниже немного схематичный, но я адаптировал его из реально работающего кода.
Спасибо всем предыдущим исследователям!
источник
Код ниже работал для меня.
Создайте класс, который расширяет класс FragmentPagerAdapter, как показано ниже.
Затем внутри каждого созданного вами фрагмента создайте метод updateFragment. В этом методе вы изменяете вещи, которые нужно изменить во фрагменте. Например, в моем случае Fragment0 содержал GLSurfaceView, который отображает трехмерный объект на основе пути к файлу .ply, поэтому в моем методе updateFragment я меняю путь к этому файлу ply.
затем создайте экземпляр ViewPager,
и экземпляр Adpater,
тогда сделай это,
Затем внутри класса вы инициализировали класс Adapter выше и создали viewPager, каждый раз, когда вы хотите обновить один из ваших фрагментов (в нашем случае Fragment0), используйте следующее:
Это решение было основано на методике, предложенной Альваро Луисом Бустаманте.
источник
1. Прежде всего, вам нужно установить метод getItemposition в вашем классе Pageradapter. 2. Вы должны прочитать Точную позицию вашего View Pager. 3. Затем отправить эту позицию в качестве местоположения данных вашего нового 4.. слушатель
этот программный код немного изменен, чтобы установить только конкретный элемент позиции
источник
что сработало для меня
viewPager.getAdapter().notifyDataSetChanged();
а в адаптере выкладываю свой код для обновления вида внутри
getItemPosition
вот такможет быть, не самый правильный способ сделать это, но это сработало (
return POSITION_NONE
уловка вызвала сбой для меня, поэтому не вариант)источник
Вы можете динамически обновлять все фрагменты, вы можете увидеть в три этапа.
В вашем адаптере:
Сейчас в вашей деятельности:
Наконец в вашем фрагменте, что-то вроде этого:
Вы можете увидеть полный код здесь .
Спасибо Альваро Луису Бустаманте.
источник
Всегда возвращать
POSITION_NONE
это просто, но немного неэффективно, потому что это вызывает создание экземпляров всех страниц, которые уже были созданы.Я создал библиотеку ArrayPagerAdapter для динамического изменения элементов в PagerAdapter.
Внутренне, адаптеры этой библиотеки вернуться
POSITION_NONE
наgetItemPosiition()
только тогда , когда это необходимо.С помощью этой библиотеки вы можете динамически изменять элементы, как описано ниже.
Библиотека Thils также поддерживает страницы, созданные Fragments.
источник
Это для всех таких, как я, которым необходимо обновить Viewpager из службы (или другого фонового потока), и ни одно из предложений не сработало: после небольшой проверки логов я понял, что метод notifyDataSetChanged () никогда не возвращается. getItemPosition (Object object) называется все концами там без дальнейшей обработки. Затем я нашел в документах родительского класса PagerAdapter (не в документах подклассов): «Изменения в наборе данных должны происходить в основном потоке и должны заканчиваться вызовом notifyDataSetChanged ()». Итак, рабочее решение в этом случае (с использованием FragmentStatePagerAdapter и getItemPosition (Object object) было установлено для возврата POSITION_NONE):
а затем вызов notifyDataSetChanged ():
источник
Вы можете добавить преобразование пейджера на Viewpager, как это
В приведенном ниже коде я изменил цвет моего представления во время выполнения при прокрутке пейджера
источник
я знаю, что я поздно, но это может кому-то помочь. Я просто расширяю принимающий ответ, и я также добавил комментарий к нему.
хорошо,
сам ответ говорит, что это неэффективно
поэтому для того, чтобы он обновлялся только при необходимости, вы можете сделать это
источник
ViewPager не был разработан для поддержки динамического изменения вида.
У меня было подтверждение этого при поиске другой ошибки, связанной с этой https://issuetracker.google.com/issues/36956111 и, в частности, https://issuetracker.google.com/issues/36956111#comment56.
Этот вопрос немного устарел , но Google недавно решил эту проблему с помощью ViewPager2. . Это позволит заменить ручные (необслуживаемые и потенциально ошибочные) решения на стандартные. Это также предотвращает ненужное воссоздание представлений, как делают некоторые ответы.
Для примеров ViewPager2 вы можете проверить https://github.com/googlesamples/android-viewpager2
Если вы хотите использовать ViewPager2, вам нужно добавить следующую зависимость в ваш файл build.gradle:
Затем вы можете заменить ваш ViewPager в вашем XML-файле на:
После этого вам нужно будет заменить ViewPager на ViewPager2 в вашей активности
ViewPager2 требуется либо RecyclerView.Adapter, либо FragmentStateAdapter, в вашем случае это может быть RecyclerView.Adapter
В случае, если вы использовали TabLayout, вы можете использовать TabLayoutMediator:
После этого вы сможете обновить свои представления, изменив данные вашего адаптера и вызвав метод notifyDataSetChanged.
источник
Вместо того, чтобы
POSITION_NONE
снова возвращать и создавать все фрагменты, вы можете сделать, как я предложил здесь: динамически обновлять ViewPager?источник
Я думаю, что я сделал простой способ уведомить об изменениях набора данных:
Во-первых, немного изменим способ работы функции instantiateItem:
для "updateView" заполните представление всеми данными, которые вы хотите заполнить (setText, setBitmapImage, ...).
убедитесь, что destroyView работает так:
Теперь предположим, что вам нужно изменить данные, сделать это, а затем вызвать следующую функцию в PagerAdapter:
Например, если вы хотите уведомить все представления, отображаемые viewPager, о том, что что-то изменилось, вы можете вызвать:
Вот и все.
источник
Что бы это ни стоило, на KitKat + кажется, что этого
adapter.notifyDataSetChanged()
достаточно для отображения новых представлений, при условии, что выsetOffscreenPageLimit
достаточно высоко. Я могу получить желаемое поведение, делаяviewPager.setOffscreenPageLimit(2)
.источник
Я на самом деле использовать
notifyDataSetChanged()
наViewPager
иCirclePageIndicator
и после этого я вызываюdestroyDrawingCache()
наViewPager
и это работает .. Ни один из других решений не работает для меня.источник