Динамічно оновлювати 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)

    Поверніть фрагмент, пов'язаний із заданою позицією.

  • public Object instantiateItem (контейнер ViewGroup, позиція int)

    Створіть сторінку для заданої позиції. Адаптер несе відповідальність за додавання поданого вигляду до даного тут контейнера, хоча він повинен лише забезпечити це тим часом, коли він повернеться з FinUpdate (ViewGroup). Параметри

    контейнер Перегляд, що містить сторінку, в якій буде показана сторінка. Позиція Позиція сторінки, яку потрібно створити.

    Повертається

    Повертає об’єкт, що представляє нову сторінку. Це не повинно бути переглядом, але це може бути якийсь інший контейнер сторінки.

але я все одно не розумію.

Ось мій код. Я використовую пакет підтримки 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


Для кращого розуміння задіяних компонентів, можливо, допоможе прочитати мій підручник про ViewPager з вкладками з окремою історією навігації. Він подається
marktani


Ви можете перевірити цей приклад у GitHub . Я сподіваюся, що цей приклад вам допоможе.
Кабезас

Гарне просте рішення: stackoverflow.com/a/35450575/2162226
Джин Бо

Нещодавно компанія Google представила ViewPager2, який спрощує динамічні оновлення фрагментів / перегляду. Ви можете побачити exemples на github.com/googlesamples/android-viewpager2 або ця відповідь на stackoverflow.com/a/57393414/1333448
Ів Delerm

Відповіді:


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я бачу, що він видаляє мій фрагмент і створює новий екземпляр. Але новий екземпляр отримує пакет SaveInstanceState зі старим фрагментом. Як я можу повністю знищити фрагмент, щоб пакет було нульовим, коли його створили заново?
Співав

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
}

1
Це означає, що ваша MainActivity також потребує реалізації інтерфейсу ??? Будь ласка, допоможіть мені тут. У моїй теперішній реалізації я використовую свій клас MainActivity як комунікатор між фрагментами, тому мене розгубило це рішення.
Rakeeb Rajbhandari

1
Добре , я відповів на інше місце з повної реалізації , де я оновлюючи Fragmentдинамічно, мають вигляд: stackoverflow.com/questions/19891828 / ...
M-WaJeEh

@ M-WaJeEh, можливо, будь-який приклад, будь ласка !!! У мене є перегляд списку із переліком міст і після вибору міста відкривається viewpager (3 сторінки) з інформацією про це місто. це працює нормально. але після вибору іншого панелі перегляду міста відображається лише 3-денна d page on refreshes the 1-st page only after swiping pages? thw 2сторінка. дякую
SERG

4
серед усіх речей, які я бачив протягом останніх 2,5 днів мого трудового життя .. це єдиний, хто працював на мене .. всілякі арігато, спасибі, спациба, сукран .. н.
Оркун Озен

2
Мати Драконів !, це працює як шарм, єдине рішення працювало для мене ... thx a lot !!
Себастьян Герреро

24

для тих, хто все ще стикається з тією ж проблемою, з якою я стикався раніше, коли у мене є ViewPager7 фрагментів. за замовчуванням ці фрагменти завантажують англійський вміст із 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

Я зіткнувся з цією проблемою і, нарешті, вирішив її сьогодні, тому записую те, що я дізнався, і сподіваюся, що це корисно для того, хто є новим для Android ViewPagerта оновить, як і я. Я використовую FragmentStatePagerAdapterна рівні 17 API і зараз маю лише 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(). Потім дані оновлюються.


Я не знайшов нічого робити після оновлення даних (у пам’яті), просто зателефонуйте, notifyDataSetChanged()потім просто оновіть автоматично, тому що фрагмент (з графічним переглядом), який я використовую, оновить себе. якщо це не так, як статичний фрагмент, мені доведеться зателефонувати onItemPositon()і повертається з POSITION_NONE приводу цього
oscarthecat

Хтось має уявлення про те, який інструмент був використаний для виготовлення цієї діаграми?
JuiCe

12

Спробуйте destroyDrawingCache()на ViewPager після notifyDataSetChanged()у своєму коді.


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 FragmentManagerinstance, щоб зберегти його в моєму 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/…
viper

10

Чомусь жоден з відповідей не працював для мене, тому мені довелося перекрити метод RestoState, не викликаючи супер у своєму fragmentStatePagerAdapter. Код:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

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

}

1
Спробувавши все інше у цій відповіді ТА, це те, що працювало на мене. Я закінчую () діяльність, яка повертається до діяльності, в якій розміщується розглянутий PagerAdapter. Я хочу, щоб усі мої фрагменти були відтворені в цьому випадку, оскільки Активність, яку я щойно закінчив, може змінити стан, який використовується для малювання фрагментів.
JohnnyLambada

OMG .. він працює .. він працює .. kudos: 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пов'язане з ним міститься у фрагменті, не в активності)


2

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

Я зберігаю посилання на фрагменти, створені в getItem, у масиві. Це прекрасно працює до тих пір, поки активність не буде знищена через configChange або відсутність пам’яті чи будь-чого іншого (-> при поверненні до активності фрагменти повертаються до останнього стану, не викликаючи «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;
    }

Тож, з одного боку, я щасливий, що знайшов рішення, яке працює для мене, і хотів поділитися ним з вами, але я також хотів запитати, чи хтось ще спробував щось подібне і чи є якась причина, чому я не повинен це так? Поки для мене це дуже добре працює ...


Я зробив що - щось пов'язане з stackoverflow.com/a/23427215/954643 , хоча ви , ймовірно , слід видалити цей пункт з myFragmentsв 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.Post події

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

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));

1

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

Крім того, у посиланні також є мало рішень .. few може відповідати вашим вимогам.


З цієї URL-адреси я знайшов рішення. Я використав модифікований FragmentStatePagerAdapter з цього gist.github.com/ypresto/8c13cb88a0973d071a64 у своєму коді, і він почав працювати, як належить.
Рафаель

Класно. Так. У посилання також мало рішень.
cgr

1

Я пережив ту саму проблему і дуже багато разів шукав. Будь-яка відповідь, подана в stackoverflow або через google, не була вирішенням для моєї проблеми. Моя проблема була легкою. У мене є список, я показую цей список з viewpager. Коли я додаю новий елемент до заголовка списку, і я оновлюю зміни оглядачів, які змінилися. Моє остаточне рішення було дуже простим, будь-хто може використовувати. Коли до списку додається новий елемент, який потрібно оновити. Спочатку встановіть адаптер оглядача на нульове значення, а потім відтворіть адаптер і встановіть його на панель перегляду.

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

Переконайтеся, що ваш адаптер повинен поширити FragmentStatePagerAdapter


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

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


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. Переопределити сповіщенняDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Додайте перевірку правильності на знищенняItem (), щоб запобігти збоям:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }

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

0

Вам потрібна зміна елемента mFragments instantiateItem 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.