ViewPager PagerAdapter не обновляет View

Я использую ViewPager из библиотеки совместимости. Мне удалось получить несколько просмотров, которые я могу просмотреть.

Однако мне сложно понять, как обновить ViewPager новым набором представлений.

Я пробовал всевозможные вещи, такие как вызов mAdapter.notifyDataSetChanged() , mViewPager.invalidate() даже при создании нового адаптера каждый раз, когда я хочу использовать новый список данных.

Ничего не помогло, текстовые комментарии остаются неизменными по сравнению с исходными данными.

Обновление: я сделал небольшой тестовый проект, и я почти смог обновить представления. Я вставлю class ниже.

Однако, как представляется, обновление не является вторым видом, «B» остается, он должен отображать «Y» после нажатия кнопки обновления.

 public class ViewPagerBugActivity extends Activity { private ViewPager myViewPager; private List data; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); data = new ArrayList(); 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 data; private Context ctx; public MyViewPagerAdapter(Context ctx, List 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) { } } } 

30 Solutions collect form web for “ViewPager PagerAdapter не обновляет View”

Существует несколько способов добиться этого.

Первый вариант проще, но бит более неэффективен.

Переопределите getItemPosition в своем PagerAdapter следующим образом:

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

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

Второй вариант, предложенный Альваро Луисом Бустаманте (ранее alvarolb) , заключается в методе setTag() в instantiateItem() при создании нового представления. Затем вместо использования notifyDataSetChanged() вы можете использовать findViewWithTag() чтобы найти представление, которое хотите обновить.

Второй подход очень гибкий и высокопроизводительный. Престижность alvarolb для оригинального исследования.

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

Последние несколько дней я работал с PagerAdapter и ViewPager , и я нашел следующее:

Метод notifyDataSetChanged() в PagerAdapter будет уведомлять ViewPager только о ViewPager что базовые страницы были изменены. Например, если вы создали / удалили страницы динамически (добавление или удаление элементов из вашего списка), ViewPager должен позаботиться об этом. В этом случае я думаю, что ViewPager определяет, следует ли удалять или создавать новые представления с помощью getItemPosition() и getCount() .

Я думаю, что ViewPager после notifyDataSetChanged() принимает дочерние представления и проверяет свою позицию с помощью getItemPosition() . Если для дочернего представления этот метод возвращает POSITION_NONE , ViewPager понимает, что представление было удалено, вызвав destroyItem() и удалив это представление.

Таким образом, переопределение getItemPosition() для всегда возврата POSITION_NONE является абсолютно неправильным, если вы хотите обновлять содержимое страниц, потому что ранее созданные представления будут уничтожены, а новые будут создаваться каждый раз, когда вы вызываете notifyDatasetChanged() . Возможно, это не так неправильно только для нескольких TextView , но когда у вас есть сложные представления, например ListViews, заполненные из базы данных, это может быть реальной проблемой и пустой тратой ресурсов.

Таким образом, существует несколько подходов к эффективному изменению содержимого представления без необходимости удаления и повторного создания представления. Это зависит от проблемы, которую вы хотите решить. Мой подход заключается в использовании метода setTag() для любого инстанцированного представления в методе instantiateItem() . Поэтому, когда вы хотите изменить данные или сделать недействительными ViewPager , вы можете вызвать метод findViewWithTag() на ViewPager чтобы получить ранее ViewPager представление и изменить / использовать его, как вы хотите, без необходимости удаления / создания нового представления каждый раз, когда вы хотите обновить некоторое значение.

Представьте, например, что у вас есть 100 страниц со 100 TextView и вы хотите периодически обновлять одно значение. С подходами, описанными ранее, это означает, что вы удаляете и создаете 100 TextView для каждого обновления. Это не имеет смысла…

Измените FragmentPagerAdapter на FragmentStatePagerAdapter .

Переопределите getItemPosition() и верните POSITION_NONE .

В конце концов, он будет слушать notifyDataSetChanged() .

Ответ, данный alvarolb, безусловно, лучший способ сделать это. Основываясь на его ответе, простой способ реализовать это – просто сохранить активные представления по положению:

 SparseArray views = new SparseArray(); @Override public Object instantiateItem(View container, int position) { View root = ; ((ViewPager) container).addView(root); views.put(position, root); return root; } @Override public void destroyItem(View collection, int position, Object o) { View view = (View)o; ((ViewPager) collection).removeView(view); views.remove(position); view = null; } 

Затем один раз, переопределив метод notifyDataSetChanged вы можете обновить представления …

 @Override public void notifyDataSetChanged() { int key = 0; for(int i = 0; i < views.size(); i++) { key = views.keyAt(i); View view = views.get(key);  } super.notifyDataSetChanged(); } 

Фактически вы можете использовать аналогичный код в notifyDataSetChanged instantiateItem и notifyDataSetChanged для обновления вашего представления. В моем коде я использую тот же самый метод.

Имела та же проблема. Для меня это работало над расширением FragmentStatePagerAdapter и переопределить следующие методы:

 @Override public Parcelable saveState() { return null; } @Override public void restoreState(Parcelable state, ClassLoader loader) { } 

После нескольких часов разочарования, пытаясь решить все вышеперечисленные решения, чтобы решить эту проблему, а также попробовать множество решений по другим подобным вопросам, вроде этого , это и все, что ViewPager чтобы решить эту проблему и заставить ViewPager уничтожить старый Fragment и заполнить pager с новым Fragment . Я решил проблему следующим образом:

1) Сделайте class ViewPager для ViewPager FragmentPagerAdapter следующим образом:

  public class myPagerAdapter extends FragmentPagerAdapter { 

2) Создайте элемент для ViewPager который сохранит title и fragment следующим образом:

 public class PagerItem { private String mTitle; private Fragment mFragment; public PagerItem(String mTitle, Fragment mFragment) { this.mTitle = mTitle; this.mFragment = mFragment; } public String getTitle() { return mTitle; } public Fragment getFragment() { return mFragment; } public void setTitle(String mTitle) { this.mTitle = mTitle; } public void setFragment(Fragment mFragment) { this.mFragment = mFragment; } } 

3) Создайте конструктор ViewPager моего экземпляра FragmentManager чтобы сохранить его в моем class следующим образом:

 private FragmentManager mFragmentManager; private ArrayList mPagerItems; public MyPagerAdapter(FragmentManager fragmentManager, ArrayList pagerItems) { super(fragmentManager); mFragmentManager = fragmentManager; mPagerItems = pagerItems; } 

4) Создайте метод для повторной установки данных adapter с новыми данными, удалив все предыдущие fragment из самого fragmentManager mentalManager, чтобы adapter снова установил новый fragment из нового списка следующим образом:

 public void setPagerItems(ArrayList pagerItems) { if (mPagerItems != null) for (int i = 0; i < mPagerItems.size(); i++) { mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit(); } mPagerItems = pagerItems; } 

5) Из Activity или Fragment контейнера не переустанавливайте адаптер с новыми данными. Установите новые данные с помощью метода setPagerItems с новыми данными следующим образом:

 ArrayList pagerItems = new ArrayList(); pagerItems.add(new PagerItem("Fragment1", new MyFragment1())); pagerItems.add(new PagerItem("Fragment2", new MyFragment2())); mPagerAdapter.setPagerItems(pagerItems); mPagerAdapter.notifyDataSetChanged(); 

Я надеюсь, что это помогает.

Спустя два с половиной года после того, как ОП задал свой вопрос, этот вопрос по-прежнему остается проблемой. Очевидно, что приоритет Google в этом не особо высок, поэтому вместо того, чтобы найти исправление, я нашел обходное решение. Большой прорыв для меня – это выяснить, какова реальная причина проблемы (см. Принятый ответ в этом сообщении ). Как только стало очевидно, что проблема в том, что любые активные страницы не были должным образом обновлены, мой обход был очевиден:

В моем fragmentе (страницы):

  • Я взял весь код, который заполняет форму из onCreateView и помещает ее в функцию PopulateForm, которую можно вызывать из любого места, а не из фреймворка. Эта функция пытается получить текущий вид с помощью getView, и если это значение равно null, оно просто возвращается. Важно, чтобы PopulateForm содержал только код, который отображает – все остальные коды, которые создают слушатели FocusChange и т. П., Все еще находятся в OnCreate
  • Создайте логическое значение, которое может использоваться как флаг, указывающий, что форма должна быть перезагружена. Mine – mbReloadForm
  • Переопределите OnResume (), чтобы вызвать PopulationForm (), если установлен mbReloadForm.

В моей деятельности, где я загружаю страницы:

  • Перейдите на страницу 0, прежде чем ничего менять. Я использую FragmentStatePagerAdapter, поэтому я знаю, что на две или три страницы больше всего влияет. Переход на страницу 0 гарантирует, что у меня будет только проблема на страницах 0, 1 и 2.
  • Прежде чем очистить старый список, возьмите его размер (). Таким образом, вы знаете, на сколько страниц влияет ошибка. Если> 3, уменьшите его до 3 – если вы используете другой PagerAdapter, вам нужно будет посмотреть, сколько страниц вам нужно иметь (возможно, все?)
  • Перезагрузите данные и вызовите страницуAdapter.notifyDataSetChanged ()
  • Теперь, для каждой из затронутых страниц, посмотрите, активна ли страница с помощью pager.getChildAt (i) – это говорит вам, есть ли у вас представление. Если да, вызовите pager.PopulateView (). Если нет, установите флаг ReloadForm.

После этого, когда вы перезагружаете второй набор страниц, ошибка все равно заставит некоторых отображать старые данные. Однако теперь они будут обновлены, и вы увидите новые данные – ваши пользователи не будут знать, что страница была некорректной, потому что это обновление произойдет до того, как они увидели страницу.

Надеюсь, это поможет кому-то!

Гораздо проще: используйте FragmentPagerAdapter и оберните просмотренные fragmentы на fragmentы. Они обновляются

На всякий случай, если кто-то использует адаптер на основе FragmentStatePagerAdapter (который позволит ViewPager создавать минимальные страницы, необходимые для показа цели, не более 2 для моего случая), ответ @ rui.araujo на переписывание getItemPosition в вашем адаптере не приведет к значительным отходам, но он все еще может быть улучшена.

В псевдокоде:

 public int getItemPosition(Object object) { YourFragment f = (YourFragment) object; YourData d = f.data; logger.info("validate item position on page index: " + d.pageNo); int dataObjIdx = this.dataPages.indexOf(d); if (dataObjIdx < 0 || dataObjIdx != d.pageNo) { logger.info("data changed, discard this fragment."); return POSITION_NONE; } return POSITION_UNCHANGED; } 

У меня была та же проблема, и мое решение переопределяет ViewPagerAdapter#getItemId(int position) :

 @Override public long getItemId(int position) { return mPages.get(position).getId(); } 

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

Чтобы использовать это, для каждой страницы требуется long id . Обычно ожидается, что он будет уникальным, но я предлагаю в этом случае, чтобы он просто отличался от предыдущего значения для одной и той же страницы. Таким образом, здесь можно использовать непрерывный счетчик в адаптере или случайных целых числах (с широким распределением).

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

После многого поиска этой проблемы я нашел действительно хорошее решение, которое, я думаю, является правильным путем. По сути, instantiateItem вызывается только при представлении представления и никогда больше, если только представление не уничтожено (это происходит, когда вы переопределяете функцию getItemPosition для возврата POSITION_NONE). Вместо этого вы хотите сохранить созданные представления и либо обновить их в адаптере, либо сгенерировать функцию get, чтобы кто-то мог ее обновить, либо установить функцию, которая обновляет адаптер (мой любимый).

Итак, в MyViewPagerAdapter добавьте переменную типа:

 private View updatableView; 

в вашем instantiateItem:

  public Object instantiateItem(View collection, int position) { updatableView = new TextView(ctx); //My change is here view.setText(data.get(position)); ((ViewPager)collection).addView(view); return view; } 

поэтому, таким образом, вы можете создать функцию, которая обновит ваше представление:

 public updateText(String txt) { ((TextView)updatableView).setText(txt); } 

Надеюсь это поможет!

У меня была аналогичная проблема, в которой у меня было четыре страницы, и на одном из страниц были обновлены взгляды на другие три. Я смог обновить виджеты (SeekBars, TextViews и т. Д.) На странице, смежной с текущей страницей. На последних двух страницах были бы неинициализированные виджеты при вызове mTabsAdapter.getItem(position) .

Чтобы решить мою проблему, я использовал setSelectedPage(index) перед вызовом getItem(position) . Это создаст экземпляр страницы, позволяя мне изменять значения и виджеты на каждой странице.

После всего обновления я бы использовал setSelectedPage(position) за которым следует notifyDataSetChanged() .

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

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

Затем в CustomViewPager создайте метод updateViewAt (int position). Само представление можно получить из объектов ArrayList, определенных в classе ViewPager (вам нужно установить идентификатор для представлений в экземпляре элемента и сравнить этот идентификатор с позицией в методе updateViewAt ()). Затем вы можете обновить представление по мере необходимости.

Все это решение мне не помогло. таким образом, я нашел рабочее решение: каждый раз вы можете setAdapter , но этого недостаточно. вы должны сделать это перед сменой адаптера:

 FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); List fragments = fragmentManager.getFragments(); for (Fragment f : fragments) { transaction.remove(f); } transaction.commit(); 

и после этого:

 viewPager.setAdapter(adapter); 

Спасибо rui.araujo и Альваро Луис Бустаманте. Сначала я пытаюсь использовать путь rui.araujo, потому что это легко. Он работает, но когда данные меняются, страница будет явно перерисовываться. Это плохо, поэтому я стараюсь использовать способ Альваро Луиса Бустаманте. Идеально. Вот код:

 @Override protected void onStart() { super.onStart(); } private class TabPagerAdapter extends PagerAdapter { @Override public int getCount() { return 4; } @Override public boolean isViewFromObject(final View view, final Object object) { return view.equals(object); } @Override public void destroyItem(final View container, final int position, final Object object) { ((ViewPager) container).removeView((View) object); } @Override public Object instantiateItem(final ViewGroup container, final int position) { final View view = LayoutInflater.from( getBaseContext()).inflate(R.layout.activity_approval, null, false); container.addView(view); ListView listView = (ListView) view.findViewById(R.id.list_view); view.setTag(position); new ShowContentListTask(listView, position).execute(); return view; } } 

И когда данные меняются:

 for (int i = 0; i < 4; i++) { View view = contentViewPager.findViewWithTag(i); if (view != null) { ListView listView = (ListView) view.findViewById(R.id.list_view); new ShowContentListTask(listView, i).execute(); } } 

Я нашел очень интересное решение этой проблемы. Вместо использования FragmentPagerAdapter , в котором хранятся все fragmentы, мы можем использовать FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ), которые перезагружают fragment каждый раз, когда мы его выбираем.

Реализации обоих адаптеров идентичны. Итак, нам нужно просто изменить « расширить FragmentPagerAdapter » на « расширить FragmentStatePagerAdapter »

для меня работала viewPager.getAdapter().notifyDataSetChanged();

и в адаптере, помещающем ваш код для обновления вида внутри getItemPosition так

 @Override public int getItemPosition(Object object) { if (object instanceof YourViewInViewPagerClass) { YourViewInViewPagerClass view = (YourViewInViewPagerClass)object; view.setData(data); } return super.getItemPosition(object); } 

может быть, не самый правильный способ обойти это, но это сработало ( return POSITION_NONE трюка return POSITION_NONE вызвал сбой для меня, так что это не вариант)

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

В вашем адаптере:

 public class MyPagerAdapter extends FragmentPagerAdapter { private static int NUM_ITEMS = 3; private Map mFragmentTags; private FragmentManager mFragmentManager; public MyPagerAdapter(FragmentManager fragmentManager) { super(fragmentManager); mFragmentManager = fragmentManager; mFragmentTags = new HashMap(); } // Returns total number of pages @Override public int getCount() { return NUM_ITEMS; } // Returns the fragment to display for that page @Override public Fragment getItem(int position) { switch (position) { case 0: return FirstFragment.newInstance(); case 1: return SecondFragment.newInstance(); case 2: return ThirdFragment.newInstance(); default: return null; } } // Returns the page title for the top indicator @Override public CharSequence getPageTitle(int position) { return "Page " + position; } @Override public Object instantiateItem(ViewGroup container, int position) { Object object = super.instantiateItem(container, position); if (object instanceof Fragment) { Fragment fragment = (Fragment) object; String tag = fragment.getTag(); mFragmentTags.put(position, tag); } return object; } public Fragment getFragment(int position) { Fragment fragment = null; String tag = mFragmentTags.get(position); if (tag != null) { fragment = mFragmentManager.findFragmentByTag(tag); } return fragment; }} 

Теперь в вашей деятельности:

 public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{ MyPagerAdapter mAdapterViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager); mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(mAdapterViewPager); viewPager.addOnPageChangeListener(this); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { Fragment fragment = mAdapterViewPager.getFragment(position); if (fragment != null) { fragment.onResume(); } } @Override public void onPageScrollStateChanged(int state) { }} 

Наконец, в вашем fragmentе, что-то вроде этого:

 public class YourFragment extends Fragment { // newInstance constructor for creating fragment with arguments public static YourFragment newInstance() { return new YourFragment(); } // Store instance variables based on arguments passed @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } // Inflate the view for the fragment based on layout XML @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment, container, false); } @Override public void onResume() { super.onResume(); //to refresh your view refresh(); }} 

Здесь вы можете увидеть полный код.

Спасибо Альваро Луис Бустаманте.

Всегда возвращать POSITION_NONE просто, но немного неэффективно, потому что это вызывает создание экземпляра всей страницы, которая уже была создана.

Я создал библиотеку ArrayPagerAdapter для динамического изменения элементов в PagerAdapters.

Внутренне адаптеры этой библиотеки возвращают POSITION_NONE на getItemPosiition() только при необходимости.

Вы можете изменять элементы динамически, как показано ниже, используя эту библиотеку.

 @Override protected void onCreate(Bundle savedInstanceState) { /** ... **/ adapter = new MyStatePagerAdapter(getSupportFragmentManager() , new String[]{"1", "2", "3"}); ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter); adapter.add("4"); adapter.remove(0); } class MyPagerAdapter extends ArrayViewPagerAdapter { public MyPagerAdapter(String[] data) { super(data); } @Override public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) { View v = inflater.inflate(R.layout.item_page, container, false); ((TextView) v.findViewById(R.id.item_txt)).setText(item); return v; } } 

Библиотека Thils также поддерживает страницы, созданные Фрагментами.

Это ужасная проблема, и я рад представить отличное решение; простой, эффективный и эффективный!

См. Ниже, код показывает, используя флаг, чтобы указать, когда возвращать POSITION_NONE

 public class ViewPagerAdapter extends PagerAdapter { // Members private boolean mForceReinstantiateItem = false; // This is used to overcome terrible bug that Google isn't fixing // We know that getItemPosition() is called right after notifyDataSetChanged() // Therefore, the fix is to return POSITION_NONE right after the notifyDataSetChanged() was called - but only once @Override public int getItemPosition(Object object) { if (mForceReinstantiateItem) { mForceReinstantiateItem = false; return POSITION_NONE; } else { return super.getItemPosition(object); } } public void setData(ArrayList newContent) { mDisplayContent = newContent; mForceReinstantiateItem = true; notifyDataSetChanged(); } } 

Полагаю, у меня есть логика ViewPager.

Если мне нужно обновить набор страниц и отобразить их на основе нового набора данных, я вызываю notifyDataSetChanged () . Затем ViewPager выполняет несколько вызовов getItemPosition () , передавая там fragment как объект. Этот fragment может быть либо из старого набора данных (который я хочу сбросить), либо из нового (который я хочу отобразить). Итак, я переопределяю getItemPosition (), и там мне нужно как-то определить, есть ли мой fragment из старого набора данных или из нового.

В моем случае у меня есть двухпанельный макет со списком верхних элементов на левой панели и вид смены (ViewPager) справа. Итак, я сохраняю ссылку на мой текущий верхний элемент внутри моего PagerAdapter, а также внутри каждой экземплярной страницы Fragment. Когда выбранный верхний элемент в списке изменяется, я сохраняю новый верхний элемент в PagerAdapter и вызываю notifyDataSetChanged () . И в переопределенном getItemPosition () я сравниваю верхний элемент из моего адаптера с верхним элементом из моего fragmentа. И только если они не равны, я возвращаю POSITION_NONE. Затем PagerAdapter восстанавливает все fragmentы, которые возвратили POSITION_NONE.

ЗАМЕТКА. Лучшая идея – сохранить идентификатор верхнего элемента вместо ссылки.

Ниже приведен fragment кода, но я адаптировал его из фактически действующего кода.

 public class SomeFragment extends Fragment { private TopItem topItem; } public class SomePagerAdapter extends FragmentStatePagerAdapter { private TopItem topItem; public void changeTopItem(TopItem newTopItem) { topItem = newTopItem; notifyDataSetChanged(); } @Override public int getItemPosition(Object object) { if (((SomeFragment) object).getTopItemId() != topItem.getId()) { return POSITION_NONE; } return super.getItemPosition(object); } } 

Спасибо всем предыдущим исследователям!

Это для всех, подобных мне, которые должны обновить Viewpager из службы (или другого фонового streamа), и ни одно из предложений не сработало: после того, как я понял, что метод notifyDataSetChanged () никогда не возвращается. getItemPosition (Объект объекта) называется всем концом без дальнейшей обработки. Затем я нашел в документах родительского classа PagerAdapter (не находится в документах подclassов), «Изменения набора данных должны происходить в основном streamе и должны заканчиваться вызовом notifyDataSetChanged ()». Таким образом, рабочим решением в этом случае было (с помощью FragmentStatePagerAdapter и getItemPosition (объект объекта), установленным для возврата POSITION_NONE):

а затем вызов notifyDataSetChanged ():

 runOnUiThread(new Runnable() { @Override public void run() { pager.getAdapter().notifyDataSetChanged(); } }); 

Следующий код работал для меня.

Создайте class, который расширяет class FragmentPagerAdapter, как показано ниже.

 public class Adapter extends FragmentPagerAdapter { private int tabCount; private Activity mActivity; private Map mFragmentTags; private FragmentManager mFragmentManager; private int container_id; private ViewGroup container; private List object; public Adapter(FragmentManager fm) { super(fm); } public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) { super(fm); mActivity = mA; mFragmentManager = fm; object = new ArrayList<>(); mFragmentTags = new HashMap(); this.tabCount = numberOfTabs; } @Override public Fragment getItem(int position) { switch (position) { case 0: return Fragment0.newInstance(mActivity); case 1: return Fragment1.newInstance(mActivity); case 2: return Fragment2.newInstance(mActivity); default: return null; }} @Override public Object instantiateItem(ViewGroup container, int position) { Object object = super.instantiateItem(container, position); if (object instanceof Fragment) { Log.e("Already defined","Yes"); Fragment fragment = (Fragment) object; String tag = fragment.getTag(); Log.e("Fragment Tag","" + position + ", " + tag); mFragmentTags.put(position, tag); }else{ Log.e("Already defined","No"); } container_id = container.getId(); this.container = container; if(position == 0){ this.object.add(0,object); }else if(position == 1){ this.object.add(1,object); }else if(position == 2){ this.object.add(2,object); } return object; } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); if (object instanceof Fragment) { Log.e("Removed" , String.valueOf(position)); } } @Override public int getItemPosition (Object object) { int index = 0; if(this.object.get(0) == object){ index = 0; }else if(this.object.get(1) == object){ index = 1; }else if(this.object.get(2) == object){ index = 2; }else{ index = -1; } Log.e("Index" , "..................." + String.valueOf(index)); if (index == -1) return POSITION_NONE; else return index; } public String getFragmentTag(int pos){ return "android:switcher:"+R.id.pager+":"+pos; } public void NotifyDataChange(){ this.notifyDataSetChanged(); } public int getcontainerId(){ return container_id; } public ViewGroup getContainer(){ return this.container; } public List getObject(){ return this.object; } @Override public int getCount() { return tabCount; }} 

Then inside each Fragment you created, create an updateFragment method. In this method you change the things you need to change in the fragment. For example in my case, Fragment0 contained a GLSurfaceView which displays a 3d object based on a path to a .ply file, so inside my updateFragment method I change the path to this ply file.

then create a ViewPager instance,

 viewPager = (ViewPager) findViewById(R.id.pager); 

and an Adpater instance,

 adapter = new Adapter(getSupportFragmentManager(), 3, this); 

then do this,

 viewPager.setAdapter(adapter); viewPager.setOffscreenPageLimit(1); 

Then inside the class were you initialized the Adapter class above and created a viewPager, every time you want to update one of your fragments (in our case Fragment0) use the following:

 adapter.NotifyDataChange(); adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager. fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0. fragment0.updateFragment() method which include the updates on this fragment adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0. 

This solution was based on the technique suggested by Alvaro Luis Bustamante.

I have tried all this solutions did not work for me because all my views are complex. So it is difficult to save the View and update the View. If you have a simple View that contains only a few TextView or something like this, it’s easy and the solutions posted here works. But if you have complex View like mine, and need to change the entire Fragment, I think that’s impossible to do. I have investigating the ViewPager code, and it seems that the ViewPager keeps the old fragments. It’s easy to realize it : Search for some native application from your device that implements swipe and do the test changing the language from English to Arabic. In this case, it should change the order of fragments, because Arabic is a RTL language. But it did not work this way.

Instead of returning POSITION_NONE and creating all fragments again, you can do as I suggested here: Update ViewPager dynamically?

1.First you have to set the getItemposition method in your Pageradapter class 2.You have to read the Exact position of your View Pager 3.then send that position as data location of your new one 4.Write update button onclick listener inside the setonPageChange listener

that program code is little bit i modified to set the particular position element only

 public class MyActivity extends Activity { private ViewPager myViewPager; private List data; public int location=0; public Button updateButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); data = new ArrayList(); data.add("A"); data.add("B"); data.add("C"); data.add("D"); data.add("E"); data.add("F"); myViewPager = (ViewPager) findViewById(R.id.pager); myViewPager.setAdapter(new MyViewPagerAdapter(this, data)); updateButton = (Button) findViewById(R.id.update); myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i2) { //Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show(); } @Override public void onPageSelected( int i) { // here you will get the position of selected page final int k = i; updateViewPager(k); } @Override public void onPageScrollStateChanged(int i) { } }); } private void updateViewPager(final int i) { updateButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show(); data.set(i, "Replaced "+i); myViewPager.getAdapter().notifyDataSetChanged(); } }); } private class MyViewPagerAdapter extends PagerAdapter { private List data; private Context ctx; public MyViewPagerAdapter(Context ctx, List data) { this.ctx = ctx; this.data = data; } @Override public int getCount() { return data.size(); } @Override public int getItemPosition(Object object) { return POSITION_NONE; } @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) { } } } 

I think I’ve made a simple way to notify of data set changes:

First, change a bit the way the instantiateItem function works:

  @Override public Object instantiateItem(final ViewGroup container, final int position) { final View rootView = mInflater.inflate(...,container, false); rootView.setTag(position); updateView(rootView, position); container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mViewPager.setObjectForPosition(rootView, position); return rootView; } 

for “updateView” , fill the view with all the data you wish to fill (setText,setBitmapImage,…) .

verify that destroyView works like this:

  @Override public void destroyItem(final ViewGroup container, final int position, final Object obj) { final View viewToRemove = (View) obj; mViewPager.removeView(viewToRemove); } 

Now, suppose you need to change the data, do it, and then call the next function on the PagerAdapter :

  public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos, final NotifyLocation toPos) { final int offscreenPageLimit = viewPager.getOffscreenPageLimit(); final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit : mSelectedPhotoIndex + offscreenPageLimit; final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit : mSelectedPhotoIndex + offscreenPageLimit; if (fromPosInt < = toPosInt) { notifyDataSetChanged(); for (int i = fromPosInt; i <= toPosInt; ++i) { final View pageView = viewPager.findViewWithTag(i); mPagerAdapter.updateView(pageView, i); } } } public enum NotifyLocation { MOST_LEFT, CENTER, MOST_RIGHT } 

For example if you wish to notify all of the views that are being shown by the viewPager that something has changed, you can call:

 notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT); 

Вот и все.

For what it’s worth, on KitKat+ it seems that adapter.notifyDataSetChanged() is enough to cause the new views to show up, provided that you’ve setOffscreenPageLimit sufficiently high. I’m able to get desired behavior by doing viewPager.setOffscreenPageLimit(2) .

I actually use notifyDataSetChanged() on ViewPager and CirclePageIndicator and after that I call destroyDrawingCache() on ViewPager and it works.. None of the other solutions worked for me.

if you want to solve Viewpager update problem check this project, it will solve your problem.

and more understand check that;

happy coding…

  • Как изменить страницу ViewPager?
  • Interesting Posts

    Является ли мой MAC-адрес общедоступным при просмотре Интернета?

    Maven Не удалось разрешить зависимости, артефакты не могут быть разрешены

    Использование «Пожалуйста, выберите» f: selectItem с нулевым / пустым значением внутри ap: selectOneMenu

    Имеют ли эти утверждения о указателях одинаковый эффект?

    Как остановить wuauserv

    Высокая контрастность и проблемы с цветом шрифта Firefox-Font?

    Самое простое решение для создания образа диска

    Почему Spark быстрее, чем Hadoop Map Reduce

    Java 8: Обязательная проверка обработки исключений в lambda-выражениях. Почему обязательный, а не факультативный?

    Где я могу найти функцию «clamp» в .NET?

    Что на самом деле делает функция «Скрыть указатель при наборе текста»?

    Запуск беззнаковых Java-апплетов

    Возможно ли вернуть прежнюю систему предложений в Firefox 43.0?

    Какой самый простой способ проверки статуса SMART для вашего жесткого диска?

    iPhone Safari Web App открывает ссылки в новом окне

    Давайте будем гением компьютера.