Обновлять ViewPager динамически?

316

Я не могу обновить содержимое в ViewPager.

Как правильно использовать методы instantiateItem () и getItem () в классе FragmentPagerAdapter?

Я использовал только getItem () для создания экземпляров и возврата моих фрагментов:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

Это сработало хорошо. За исключением того, что я не могу изменить содержание.

Итак, я нашел это: ViewPager PagerAdapter не обновляет представление

«Мой подход заключается в использовании метода setTag () для любого экземпляра представления в методе instantiateItem ()»

Теперь я хочу реализовать instantiateItem () для этого. Но я не знаю, что я должен вернуть (тип Object) и какова связь с getItem (int position)?

Я прочитал ссылку :

  • публичный абстрактный фрагмент getItem (int position)

    Вернуть фрагмент, связанный с указанной позицией.

  • открытый объект instantiateItem (контейнер ViewGroup, позиция int)

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

    контейнер Содержащий вид, в котором будет отображаться страница. position Позиция страницы, которую нужно создать.

    Возвращает

    Возвращает объект, представляющий новую страницу. Это не обязательно должен быть View, но может быть какой-то другой контейнер страницы.

но я все еще не понимаю

Вот мой код. Я использую пакет поддержки v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pager1);

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

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

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

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

Вот также кто-то с подобной проблемой, без ответов http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html

Ixx
источник
Чтобы получить лучшее представление о задействованных компонентах, это может помочь прочитать мой учебник о ViewPager с вкладками с отдельной историей обратной навигации. Он поставляется с рабочим , например , на GitHub и дополнительные ресурсы: medium.com/@nilan/...
marktani
есть какая-то проблема stackoverflow.com/questions/40149039/…
Альберт
Вы можете проверить этот пример в GitHub . Я надеюсь, что этот пример поможет вам.
Кабесас
Хорошее простое решение: stackoverflow.com/a/35450575/2162226
Джин Бо
Google недавно представила ViewPager2, который упрощает динамические обновления фрагментов / представлений. Вы можете увидеть примеры на github.com/googlesamples/android-viewpager2 или этот ответ на stackoverflow.com/a/57393414/1333448
Ив Делерм,

Ответы:

605

При использовании FragmentPagerAdapter или FragmentStatePagerAdapter лучше всего иметь дело исключительно с, getItem()а не соприкасаться instantiateItem()вообще. Интерфейс instantiateItem()- destroyItem()- isViewFromObject()в PagerAdapter - это интерфейс более низкого уровня, который FragmentPagerAdapter использует для реализации гораздо более простого getItem()интерфейса.

Прежде чем углубляться в это, я должен уточнить, что

если вы хотите отключить фактические отображаемые фрагменты, вам нужно избегать FragmentPagerAdapter и использовать FragmentStatePagerAdapter.

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

Я не рекомендую setTag()и findViewWithTag()обходной путь, указанный в сообщении, которое вы связали. Как вы выяснили, использование фрагментов setTag()и findViewWithTag()их использование не работает, так что это не очень хорошее совпадение.

Правильное решение - переопределить getItemPosition(). Когда notifyDataSetChanged()вызывается, ViewPager вызывает getItemPosition()все элементы в своем адаптере, чтобы узнать, нужно ли их переместить в другое положение или удалить.

По умолчанию getItemPosition()возвращает POSITION_UNCHANGED, что означает: «Этот объект в порядке, не разрушайте и не удаляйте его». Возвращение POSITION_NONEустраняет проблему, вместо этого говоря: «Этот объект больше не является элементом, который я отображаю, удалите его». Таким образом, он имеет эффект удаления и воссоздания каждого элемента в вашем адаптере.

Это абсолютно законное решение! Это исправление заставляет notifyDataSetChanged вести себя как обычный адаптер без перезапуска представления. Если вы реализуете это исправление и производительность удовлетворительная, вы отправляетесь в гонки. Работа выполнена.

Если вам нужна лучшая производительность, вы можете использовать более изящную getItemPosition()реализацию. Вот пример для пейджера, создающего фрагменты из списка строк:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

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

Что, если фрагмент не был воссоздан, но все равно нуждается в обновлении? Обновления живого фрагмента лучше всего обрабатываются самим фрагментом. В конце концов, это преимущество наличия фрагмента - это его собственный контроллер. Фрагмент может добавить слушателя или наблюдателя к другому объекту onCreate(), а затем удалить его onDestroy(), тем самым управляя обновлениями. Вам не нужно помещать весь код обновления внутрь, getItem()как в адаптере для ListView или других типов AdapterView.

И последнее: только то, что FragmentPagerAdapter не уничтожает фрагмент, не означает, что getItemPosition совершенно бесполезно в FragmentPagerAdapter. Вы все еще можете использовать этот обратный вызов для изменения порядка фрагментов в ViewPager. Однако он никогда не удалит их полностью из FragmentManager.

Билл Филлипс
источник
4
Не работает, все равно ничего не обновляет ... выложил мой код.
Ixx
64
Ладно, разобрался с проблемой - нужно использовать FragmentStatePagerAdapter, а не FragmentPagerAdapter. FragmentPagerAdapter никогда не удаляет фрагменты из FragmentManager, он только отсоединяет и присоединяет их. Проверьте документы для получения дополнительной информации - в общем, вы хотите использовать FragmentPagerAdapter только для фрагментов, которые являются постоянными. Я отредактировал мой пример с исправлением.
Билл Филлипс
1
Я рассмотрю это в какой-то момент позже ... спасибо за ваш ответ. И тогда я принимаю ваш ответ, если он работает. Перед вашим последним комментарием я реализовал удаление и добавление нового ViewPager каждый раз, и это работает. Не очень хорошее решение, но сейчас у меня нет времени его менять.
Ixx
12
Вы правы, изменение класса адаптера на FragmentStatePagerAdapter решило все мои проблемы. Спасибо!
Ixx
2
Даже когда я возвращаю POSITION_NONE во время использования FragmentStatePagerAdapter, я вижу, что он удаляет мой фрагмент и создает новый экземпляр. Но новый экземпляр получает Bundle saveInstanceState с состоянием старого фрагмента. Как я могу полностью уничтожить фрагмент, чтобы Bundle был нулевым при повторном создании?
Поет
142

Вместо того , чтобы вернуться POSITION_NONEиз getItemPosition()и вызывая полный вид отдыха, сделайте следующее:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(updateData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

Ваши фрагменты должны реализовывать UpdateableFragmentинтерфейс:

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

и интерфейс:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

Ваш класс данных:

public class UpdateData {
    //whatever you want here
}
M-WaJeEh
источник
1
Означает ли это, что вашей MainActivity также необходимо реализовать интерфейс ??? Пожалуйста, помогите мне здесь. В моей текущей реализации я использую свой класс MainActivity в качестве коммуникатора между фрагментами, поэтому я был озадачен этим решением.
Ракеб Раджбхандари
1
Хорошо, я ответил в другом месте с полной реализацией, где я обновляюсь Fragmentдинамически, посмотрите: stackoverflow.com/questions/19891828/…
M-WaJeEh
@ M-WaJeEh, может быть, какой-нибудь пример, пожалуйста! У меня есть список со списком городов, и после выбора города открывается окно просмотра (3 страницы) с информацией об этом городе. все работает нормально но после выбора другого города окно просмотра показывает только 3-ю d page on refreshes the 1-st page only after swiping pages? thw 2страницу, всегда пустую. спасибо
СЕРГ
4
среди всего, что я видел за последние 2,5 дня моей трудовой жизни ... это единственный, который работал на меня ... всевозможные аригато, спасибо, spaciba, sukran .. n.
Оркун Озен
2
Мать Драконов !, это работает как шарм, единственное решение сработало для меня ... спасибо большое !!
Себастьян Герреро
24

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

1- Вы должны использовать FragmentStatePagerAdapter, как указано выше.

2- на mainActivity я переопределил onResume и сделал следующее

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3-я переопределила getItemPosition()в mPagerAdapter и вернула его POSITION_NONE.

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

работает как шарм

Зиад Голмиш
источник
1
Работает только в одной ситуации. Когда вы удаляете последний элемент в viewPager. Тогда он не удаляется из списка.
Владо Панджич
Я динамически добавлял фрагменты в левую и правую части текущего фрагмента в окне просмотра. Этот работал! Спасибо.
h8pathak
15

Я столкнулся с этой проблемой и, наконец, решил ее сегодня, поэтому я записываю то, что узнал, и надеюсь, что это будет полезно для тех, кто ViewPagerплохо знаком с Android, и обновляет его, как и я. Я использую FragmentStatePagerAdapterв API уровень 17 и в настоящее время есть только 2 фрагмента. Я думаю, что должно быть что-то не так, пожалуйста, поправьте меня, спасибо. введите описание изображения здесь

  1. Сериализованные данные должны быть загружены в память. Это можно сделать используя CursorLoader/ AsyncTask/ Thread. Будет ли он загружен автоматически, зависит от вашего кода. Если вы используете CursorLoader, он загружается автоматически, так как есть зарегистрированный наблюдатель данных.

  2. После вызова viewpager.setAdapter(pageradapter)адаптера getCount()постоянно вызывается для создания фрагментов. Таким образом, если данные загружаются, getCount()можно вернуть 0, поэтому вам не нужно создавать фиктивные фрагменты, если данные не отображаются.

  3. После загрузки данных адаптер не будет автоматически строить фрагменты, так getCount()как он по-прежнему равен 0, поэтому мы можем установить фактически загруженный номер данных, который будет возвращен getCount(), а затем вызвать адаптер notifyDataSetChanged(). ViewPagerначать создавать фрагменты (только первые 2 фрагмента) по данным в памяти. Это сделано, прежде чем notifyDataSetChanged()возвращается. Тогда у вас ViewPagerесть нужные вам фрагменты.

  4. Если данные в базе данных и памяти оба обновлены (запись через), или только данные в памяти обновлены (обратная запись), или обновлены только данные в базе данных. В двух последних случаях, если данные автоматически не загружаются из базы данных в память (как упоминалось выше). ViewPagerИ адаптер пейджера просто иметь дело с данными в памяти.

  5. Поэтому, когда данные в памяти обновляются, нам просто нужно вызвать адаптер notifyDataSetChanged(). Поскольку фрагмент уже создан, адаптер onItemPosition()будет вызван до notifyDataSetChanged()возврата. Ничего не нужно делать в getItemPosition(). Затем данные обновляются.

oscarthecat
источник
я обнаружил, что ничего не нужно делать после того, как вы обновите данные (в памяти), просто позвоните, а notifyDataSetChanged()затем просто обновитесь автоматически, потому что фрагмент (с диаграммой), который я использую, обновится сам. если это не так, как статический фрагмент, мне придется позвонить onItemPositon()и POSITION_NONE
вернуть
Кто-нибудь имеет представление о том, какой инструмент был использован для создания этой диаграммы?
JuiCe
12

Попробуйте destroyDrawingCache()на ViewPager после notifyDataSetChanged()в вашем коде.

czaku
источник
3
Тоже самое. Работает отлично, спасибо. Мне было особенно тяжело, потому что я не использую Fragmentsв своем видении пейджер. Так что спасибо тебе!
Сет
11

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

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) Создайте конструктор объекта ViewPagertake my FragmentManagerдля сохранения его в my 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();

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

Сами Элтамави
источник
Можете ли вы помочь мне в моей проблеме stackoverflow.com/questions/40149039/…
Альберт
можете вы помочь мне с этим вопросом stackoverflow.com/questions/41547995/...
гадюка
10

По какой-то причине ни один из ответов не сработал для меня, поэтому мне пришлось переопределить метод restoreState, не вызывая super в моем fragStatePagerAdapter. Код:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

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

}
hordurh
источник
1
После попытки всего остального в этом SO ответе, это тот, который работал для меня. Я заканчиваю () действие, возвращающееся к действию, в котором находится рассматриваемый PagerAdapter. Я хочу, чтобы все мои фрагменты были воссозданы в этом случае, потому что действие, которое я только что закончил, могло изменить состояние, используемое для рисования фрагментов.
JohnnyLambada
OMG .. это работает .. это работает .. слава: D В моем случае мне нужно удалить объект текущего элемента viewpager. но текущий фрагмент не обновляется после попытки всех вышеуказанных методов / шагов.
Рахул
3

Я немного изменил решение, предоставленное Биллом Филлипсом, чтобы удовлетворить мои потребности

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

так что getItemPosition()возвращается POSITION_NONEтолько для тех фрагментов, которые в данный момент в FragmentManagerкогда getItemPositionвызывается. (Обратите внимание, что это FragmentStatePagerи ViewPagerсвязанное с ним содержится во фрагменте, а не в действии)

adwait
источник
2

У меня была похожая проблема, но я не хочу доверять существующим решениям (жестко закодированные имена тегов и т. Д.), И я не мог заставить решение M-WaJeEh работать на меня. Вот мое решение:

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

Чтобы избежать этой проблемы, я реализовал instantiateItem (ViewGroup, int) и обновил там мой массив, например так:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

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

JPM
источник
Я сделал кое-что связанное с stackoverflow.com/a/23427215/954643 , хотя вы, вероятно, должны удалить элемент из myFragmentsin, public void destroyItem(ViewGroup container, int position, Object object)а также, чтобы вы не захватили устаревшую ссылку на fragement.
qix
1

Я использую библиотеку EventBus для обновления Fragmentконтента в ViewPager. Логика проста, как документ EventBus, как это сделать . Не нужно контролировать FragmentPagerAdapterэкземпляр. Код здесь:

1: Определить события

Определите, какое сообщение необходимо обновить.

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2. Подготовить подписчиков

Напишите ниже код во фрагменте, который необходимо обновить.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3.Пост события

Напишите ниже код в другом Activityили другом, Fragmentкоторый должен обновить параметр

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));
Fantasy Fang
источник
1

Если вы хотите использовать FragmentStatePagerAdapter, ознакомьтесь с https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary% 20Stars & groupby = & sort = & id = 37990 . Существуют проблемы с FragmentStatePagerAdapter, которые могут вызывать или не беспокоить ваш вариант использования.

Кроме того, ссылка имеет несколько решений .. мало кто может удовлетворить ваши требования.

CGR
источник
С этого URL я нашел решение. Я использовал измененный FragmentStatePagerAdapter из этого списка gist.github.com/ypresto/8c13cb88a0973d071a64 в своем коде, и он начал работать так, как и должно быть.
Рафаэль
Прохладно. Да. У Link тоже есть несколько решений.
Cgr
1

Я жил той же проблемой, и я искал слишком много раз. Любой ответ, данный в stackoverflow или через Google, не был решением для моей проблемы. Моя проблема была легкой. У меня есть список, я показываю этот список с ViewPager. Когда я добавляю новый элемент в заголовок списка и обновляю ничего не изменилось. Мое окончательное решение было очень простым для любого. Когда новый элемент добавлен в список и хотите обновить список. Сначала установите для адаптера viewpager значение null, затем заново создайте адаптер и установите для него значение i для viewpager.

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

Убедитесь, что ваш адаптер должен расширять FragmentStatePagerAdapter

nebyan
источник
1

Вот моя реализация, которая включает в себя информацию от @Bill Phillips One большую часть времени получает фрагментированное кэширование, за исключением случаев, когда данные изменились. Просто, и, кажется, работает нормально.

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);
voam
источник
0

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

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

Я только хочу обновить страницу 0 после onResume ().

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

В моем FragmentsMain есть общедоступное целое число «страница», которое может сказать мне, является ли это страница, которую я хочу обновить.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;
Тед Се
источник
0

Я знаю, что опаздываю на вечеринку. Я исправил проблему, позвонив TabLayout#setupWithViewPager(myViewPager);сразу послеFragmentPagerAdapter#notifyDataSetChanged();

theapache64
источник
0

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

С этим ограничением это решение является безопасным и должно быть безопасным для использования в производстве.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

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

Кевин Грант
источник
0

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

  1. Скопируйте исходный код для FragmentStatePagerAdapter (кажется, не обновлялся веками).

  2. Переопределить notifyDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Добавьте проверку работоспособности для destroyItem (), чтобы предотвратить сбои:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }
Meanman
источник
0

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

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


    public MyFragmentStatePageAdapter(FragmentManager fm) {
        super(fm);
    }


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


    @Override
    public int getCount() {
        return titles.size();
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        fragmentPositions.remove(object);
    }
}
Джон О'Рейли
источник
0

Используйте FragmentStatePagerAdapter вместо FragmentPagerAdapter, если вы хотите воссоздать или перезагрузить фрагмент на основе индекса. Например, если вы хотите перезагрузить фрагмент, отличный от FirstFragment, вы можете проверить экземпляр и вернуть позицию следующим образом

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }
HemangNirmal
источник
0

Вам нужно изменить элемент mFragments для экземпляра getItemPosition.

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

Основан на AndroidX FragmentStatePagerAdapter.java, поскольку mFragmentsположение элементов не изменяется при вызове notifyDataSetChanged ().

Источник: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Пример: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

Вы можете запустить этот проект, чтобы подтвердить, как работать. https://github.com/cuichanghao/infivt

Чанхао Цуй
источник