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

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

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

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

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

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

То, что не отображается, однако, является вторым видом, "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) {
        }
    }
}

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

SparseArray<View> views = new SparseArray<View>();

@Override
public Object instantiateItem(View container, int position) {
    View root = <build your view here>;
    ((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);
       <refresh view with new data>
    }
    super.notifyDataSetChanged();
}

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

Ответ 5

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

@Override
public Parcelable saveState() {
    return null;
}

@Override
public void restoreState(Parcelable state, ClassLoader loader) {

}

Ответ 6

После нескольких разочарований, пытаясь решить все вышеперечисленные решения, чтобы решить эту проблему, а также попробовать множество решений по другим подобным вопросам, например , this и , которые все НЕ УДАЛИТЬ, чтобы решить эту проблему и сделать ViewPager для уничтожения старого Fragment и заполнить pager с новым Fragment s. Я решил проблему следующим образом:

1) Сделайте класс 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<PagerItem> mPagerItems;

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

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

public void setPagerItems(ArrayList<PagerItem> 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<PagerItem> pagerItems = new ArrayList<PagerItem>();
pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

mPagerAdapter.setPagerItems(pagerItems);
mPagerAdapter.notifyDataSetChanged();

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

Ответ 7

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

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

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

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

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

Ответ 8

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

Ответ 9

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

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

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

viewPager.setAdapter(adapter);

Ответ 10

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

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

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

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

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

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

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

Ответ 11

Спасибо 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();
    }
}

Ответ 12

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

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

Ответ 13

На всякий случай кто-либо использует 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;
}

Ответ 14

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

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

Ответ 15

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

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

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

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

Ответ 16

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

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

private View updatableView;

a в вашем файле 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);
}

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

Ответ 17

для меня работала 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 вызвал сбой для меня, поэтому не был вариант)

Ответ 18

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

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

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap<Integer, String>();
}

// 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) {

}}

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

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();

}}

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

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

Ответ 19

Всегда возвращаемый 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<String> {

    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 также поддерживает страницы, созданные фрагментами.

Ответ 20

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

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

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

ПРИМЕЧАНИЕ. Хранение идентификатора верхнего элемента вместо ссылки может быть лучшей идеей.

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

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);
  }
}

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

Ответ 21

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

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

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

Ответ 22

Вы можете добавить преобразование пейджера в Viewpager следующим образом:

myPager.setPageTransformer(true, new MapPagerTransform());

В приведенном ниже коде я изменил цвет моего представления во время выполнения при прокрутке пейджера

public class MapPagerTransform implements ViewPager.PageTransformer {


    public void transformPage(View view, float position) {
     LinearLayout  showSelectionLL=(LinearLayout)view.findViewById(R.id.showSelectionLL);

     if (position < 0) {

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else if (position >0){

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else {
                showSelectionLL.setBackgroundColor(Color.RED);
     }
   }
}

Ответ 23

Код ниже работал для меня.

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

public class Adapter extends FragmentPagerAdapter {

private int tabCount;
private Activity mActivity;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private int container_id;
private ViewGroup container;
private List<Object> 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<Integer, String>();
    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<Object> getObject(){
    return this.object;
}

@Override
public int getCount() {
    return tabCount;
}}

Затем внутри каждого созданного вами фрагмента создайте метод updateFragment. В этом методе вы изменяете вещи, которые нужно изменить во фрагменте. Например, в моем случае Fragment0 содержал GLSurfaceView, который отображает трехмерный объект на основе пути к файлу .ply, поэтому в моем методе updateFragment я изменяю путь к этому файлу ply.

затем создайте экземпляр ViewPager,

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

и экземпляр Adpater,

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

тогда сделай это,

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

Затем внутри класса вы инициализировали класс Adapter выше и создали viewPager, каждый раз, когда вы хотите обновить один из ваших фрагментов (в нашем случае Fragment0), используйте следующее:

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.

Это решение было основано на методике, предложенной Альваро Луисом Бустаманте.

Ответ 24

Я пробовал все эти решения для меня не работало, потому что все мои взгляды сложны. Поэтому трудно сохранить представление и обновить представление. Если у вас есть простой вид, содержащий только несколько TextView или что-то вроде этого, это легко и решения, размещенные здесь, работают. Но если у вас сложный вид, как у меня, и мне нужно изменить весь фрагмент, я думаю, что это невозможно сделать. Я изучаю код ViewPager, и кажется, что ViewPager хранит старые фрагменты. Это легко понять: найдите какое-то собственное приложение с вашего устройства, которое реализует салфетки и выполняет тест, изменяя язык с английского на арабский. В этом случае он должен изменить порядок фрагментов, потому что арабский язык RTL. Но это не сработало.

Ответ 25

Я использую notifyDataSetChanged() на ViewPager и CirclePageIndicator, после чего я вызываю destroyDrawingCache() на ViewPager, и он работает. Ни один из других решений не работал у меня.

Ответ 26

Вместо того, чтобы возвращать POSITION_NONE и снова создавать все фрагменты, вы можете сделать так, как я предложил здесь: Динамически обновлять ViewPager?

Ответ 27

1. Сначала вам нужно установить метод getItemposition в классе Pageradapter 2.Вы должны прочитать точную позицию вашего плеера просмотра 3. затем отправьте эту позицию как местоположение данных вашего нового 4.Нажмите кнопку обновления onclick listener внутри слушателя setonPageChange

этот программный код немного модифицирован для установки только определенного элемента позиции

public class MyActivity extends Activity {

private ViewPager myViewPager;
private List<String> 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<String>();
    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<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 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) {
    }
}
}

Ответ 29

Я думаю, что я сделал простой способ известить об изменениях в наборе данных:

Во-первых, немного измените способ работы функции instantiateItem:

    @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;
    }

для "updateView", заполните представление всеми данными, которые вы хотите заполнить (setText, setBitmapImage,...).

убедитесь, что destroyView работает следующим образом:

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

Теперь предположим, что вам нужно изменить данные, сделать это, а затем вызвать следующую функцию в 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
}

Например, если вы хотите уведомить обо всех представлениях, которые показывают viewPager, что что-то изменилось, вы можете вызвать:

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

Что это.

Ответ 30

Для того, что стоит, на KitKat + кажется, что adapter.notifyDataSetChanged() достаточно, чтобы показать новые представления, при условии, что у вас setOffscreenPageLimit достаточно высокий. Я могу получить желаемое поведение, выполнив viewPager.setOffscreenPageLimit(2).