ViewPager PagerAdapter nie aktualizuje widoku

597

Korzystam z ViewPager z biblioteki kompatybilności. Udało mi się wyświetlić kilka widoków, które mogę przewijać.

Trudno mi jednak wymyślić, jak zaktualizować ViewPager za pomocą nowego zestawu widoków.

Próbowałem różnych rzeczy, takich jak dzwonienie mAdapter.notifyDataSetChanged(), mViewPager.invalidate()nawet tworząc zupełnie nowy adapter za każdym razem, gdy chcę użyć nowej listy danych.

Nic nie pomogło, widoki tekstowe pozostają niezmienione od pierwotnych danych.

Aktualizacja: Zrobiłem mały projekt testowy i prawie byłem w stanie zaktualizować widoki. Wkleję klasę poniżej.

To, co nie wydaje się aktualizować, to drugi widok, pozostaje „B”, powinien on wyświetlać „Y” po naciśnięciu przycisku aktualizacji.

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) {
        }
    }
}
C0deAttack
źródło
1
Proszę zobaczyć stackoverflow.com/questions/12510404/…, aby poznać potencjalny błąd związany z FragmentStatePagerAdapter.
samis
2
Przepraszam, że to pogłębiam. Twoje pytanie naprawdę mi pomogło, dzięki! Jedyne, czego nie rozumiem, to to, jak zmienia się odniesienie do danych w PagerAdapter po ich aktualizacji w działaniu
Ewanw,
Spraw, by twój adapter przedłużył BaseAdapter Zobacz to: stackoverflow.com/questions/13664155/…
1
Najwyraźniej jeśli ponownie ustawisz adapter na pager widoku, resetuje się.
EpicPandaForce
mam problem z pager stackoverflow.com/questions/41217237/...
Chris

Odpowiedzi:

856

Można to osiągnąć na kilka sposobów.

Pierwsza opcja jest łatwiejsza, ale nieco bardziej nieefektywna.

Zastąp getItemPositionw PagerAdapterten sposób:

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

W ten sposób, kiedy zadzwonisz notifyDataSetChanged(), pager widoku usunie wszystkie widoki i załaduje je ponownie. W ten sposób uzyskuje się efekt przeładowania.

Druga opcja sugerowane przez Alvaro Luis Bustamante (poprzednio alvarolb) , jest setTag()sposobem, w instantiateItem()przypadku instancji nowego widoku. Następnie zamiast używać notifyDataSetChanged()możesz findViewWithTag()znaleźć widok, który chcesz zaktualizować.

Drugie podejście jest bardzo elastyczne i wydajne. Uznanie dla alvarolba za oryginalne badania.

rui.araujo
źródło
4
Lepsze niż moja praca, wydaje się, że nie ma żadnych skutków ubocznych, dzięki.
C0deAttack
3
Mam podobny problem. Mój viewPager pokaże nowe wyniki wyszukiwania, ale nie przywraca domyślnie pierwszego wpisu kursora. Czy ktoś wie, jak zresetować pozycję podczas aktualizacji zestawu danych?
mAndroid
1
@mAndroid powinieneś zadać osobne pytanie zawierające więcej szczegółów.
rui.araujo,
1
zwracanie POSITION_NONE najwyraźniej dodaje dużej nieefektywności do ogólnej aplikacji, ponieważ powoduje to, że Android usuwa widok związany z pytaną pozycją. Widok musi zostać ponownie utworzony przed wyświetleniem. Rozwiązuje problem braku aktualizacji widoków, ale prawdopodobnie nie jest najlepszym rozwiązaniem dla złożonych układów.
wufoo,
11
Lepsza odpowiedź (bez usuwania wszystkich elementów) -> stackoverflow.com/a/10852046/898056
yhpark
484

Nie wydaje mi się, aby w pliku był jakikolwiek błąd PagerAdapter. Problem polega na tym, że zrozumienie, jak to działa, jest trochę skomplikowane. Patrząc na wyjaśnione tutaj rozwiązania, istnieje nieporozumienie, a zatem z mojego punktu widzenia słabe wykorzystanie utworzonych widoków.

W ciągu ostatnich kilku dni pracowałem z PagerAdapteri ViewPagerznalazłem następujące:

Ta notifyDataSetChanged()metoda PagerAdapterpowiadomi tylko, ViewPagerże strony znajdujące się poniżej uległy zmianie. Na przykład, jeśli utworzyłeś / usunąłeś strony dynamicznie (dodając lub usuwając elementy z listy), ViewPagerpowinieneś się tym zająć. W tym przypadku myślę, że ViewPagerokreśla, czy nowy widok powinien zostać usunięty lub utworzony za pomocą metod getItemPosition()i getCount().

Myślę, że ViewPagerpo odebraniu notifyDataSetChanged()połączenia jego widok dziecka jest sprawdzany za pomocą getItemPosition(). Jeśli dla widoku podrzędnego ta metoda zwraca POSITION_NONE, oznacza, ViewPagerże widok został usunięty, wywołując destroyItem()i usuwając ten widok.

W ten sposób zastąpienie opcji getItemPosition()powrotu zawsze POSITION_NONEjest całkowicie niepoprawne, jeśli chcesz tylko zaktualizować zawartość stron, ponieważ wcześniej utworzone widoki zostaną zniszczone, a nowe będą tworzone za każdym razem, gdy zadzwonisz notifyDatasetChanged(). Może się wydawać, że nie jest tak źle tylko przez kilka TextViewsekund, ale jeśli masz złożone widoki, takie jak ListViews wypełnione z bazy danych, może to być prawdziwy problem i marnowanie zasobów.

Istnieje więc kilka podejść do skutecznej zmiany zawartości widoku bez konieczności jego usuwania i tworzenia ponownie. To zależy od problemu, który chcesz rozwiązać. Moje podejście polega na użyciu tej setTag()metody w dowolnym utworzonym widoku tej instantiateItem()metody. Więc jeśli chcesz zmienić dane lub unieważnić potrzebny widok, możesz wywołać findViewWithTag()metodę w ViewPagercelu pobrania wcześniej utworzonego widoku i zmodyfikować / użyć go, jak chcesz, bez konieczności usuwania / tworzenia nowego widoku za każdym razem, gdy chcesz zaktualizować jakąś wartość.

Wyobraź sobie na przykład, że masz 100 stron po 100 TextViews i chcesz okresowo aktualizować tylko jedną wartość. Dzięki omówionym wcześniej metodom oznacza to, że usuwasz i tworzysz 100 sekund TextViewdla każdej aktualizacji. To nie ma sensu...

Alvaro Luis Bustamante
źródło
11
+1 - Twoje rozwiązanie działa nie tylko jak urok, natrafiłem na dokładnie opisany tutaj problem, kiedy musiałem aktualizować strony zawierające karty z widokami list, tekstami i obrazami. (OutOfErrorExceptions i takie ...) Dziękujemy!
schlingel,
3
@alvarolb: Witam, tak naprawdę nie wiem, co masz na myśli setTagw metodzie. onInstantiateItemCzy chcesz zaktualizować tę odpowiedź za pomocą kodowania? Dzięki.
Huy Tower
35
Byłoby idealnie, gdybyś napisał przykład.
Sami Eltamawy
13
Jak ktoś mówi: przykład należy docenić!
Jey10
15
Od 6 lat nikt nie może dać przykładu.
Oussaki,
80

Zmień FragmentPagerAdapterTO FragmentStatePagerAdapter.

Przesłoń getItemPosition()metodę i zwróć POSITION_NONE.

W końcu będzie nasłuchiwać notifyDataSetChanged()pager na widoku.

Zestaw
źródło
Myślę, że powinieneś poprawnie zaimplementować cały getItemPosition ORAZ zwrócić POSITION_NONE, jeśli fragment nie zostanie znaleziony.
tkhduracell,
46

Odpowiedź udzielona przez alvarolb jest zdecydowanie najlepszym sposobem na to. Opierając się na jego odpowiedzi, łatwym sposobem na wdrożenie tego jest po prostu przechowywanie aktywnych widoków według pozycji:

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

Następnie raz, zastępując notifyDataSetChangedmetodę, możesz odświeżyć widoki ...

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

Możesz użyć podobnego kodu instantiateItemi notifyDataSetChangedodświeżyć widok. W moim kodzie używam dokładnie tej samej metody.

Grimmace
źródło
8
czy możesz podać pełny przykład adaptera? czy robi się to za pomocą Fragmentów, czy nie?
9
<odśwież widok z nowymi danymi> ?? co to znaczy albo musimy dodać, że on tu widzi, albo nic?
Akarsh M
Ta metoda aktualizuje wszystkie widoki. Jeśli chcesz zaktualizować tylko jeden widok, możesz napisać własną metodę replaceDataItemChanged w następujący sposób: public void replaceDataItemChanged (int pos) {TextView tv = (TextView) views.get (pos) .findViewById (R.id.tv); tv.setText (list.get (pos)); super.notifyDataSetChanged (); }
Kai Wang
Ten kod ulega awarii, jeśli nowy rozmiar pagera jest mniejszy niż stary
Anton Duzenko
@AntonDuzenko poprawnie, usuwanie widoków jest dość trudne.
MLProgrammer-CiM
28

Miałem ten sam problem. Dla mnie zadziałało rozszerzenie FragmentStatePagerAdapter i zastąpienie poniższych metod:

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

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

}
Mario Mosca
źródło
To jedyne rozwiązanie, które zadziałało dla mnie. Moim przypadkiem nie jest aktualizacja fragmentu, ale dynamiczne usuwanie lub dodawanie fragmentów. Bez rozszerzenia FragmentStatePagerAdapter ViewPager zwraca fragmenty w pamięci podręcznej, co jest niepoprawną pozycją. Dzięki!
Sira Lam,
Działa dobrze na bibliotece wsparcia 28.0.0
Oleksii K.
Zastąpić, aby co zrobić? Jak utworzyć obiekt stanu powiązany z adapterem?
Phani Rithvij,
Dzięki stary. To rozwiązanie działa tylko
Parthi
20

Po godzinach frustracji podczas próby wszystkich powyższych rozwiązań w celu przezwyciężenia tego problemu, a także próbują wiele rozwiązań na innych podobnych pytań, takich jak ten , ten i ten , który wszystko FAILED ze mną, aby rozwiązać ten problem i aby ViewPagerzniszczyć stary Fragmenti wypełnić pagerz nowy Fragments. Rozwiązałem problem w następujący sposób:

1) Ustaw ViewPagerklasę na rozszerzenia FragmentPagerAdapterw następujący sposób:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Utwórz element dla ViewPagertego sklepu titlei fragmentnastępujące elementy:

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) Ustaw konstruktor ViewPagerweź moją FragmentManagerinstancję, aby przechował ją w classnastępujący sposób:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

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

4) Tworzenie metodę do ponownego ustawiania adapterdanych z nowych danych, usuwając wszystkie poprzednie fragmentze fragmentManagersobą bezpośrednio, aby adapterustawić nowy fragmentz nową listą ponownie w następujący sposób:

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) Z kontenera Activitylub Fragmentnie inicjuj ponownie adaptera nowymi danymi. Ustaw nowe dane za pomocą metody setPagerItemsz nowymi danymi w następujący sposób:

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

Mam nadzieję, że to pomoże.

Sami Eltamawy
źródło
@Tomer, z czym miałeś problem?
Sami Eltamawy
9

Miałem ten sam problem i moje rozwiązanie korzysta FragmentPagerAdapterz zastępowania FragmentPagerAdapter#getItemId(int position):

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

Domyślnie ta metoda zwraca pozycję elementu. Przypuszczam, że ViewPagersprawdza, czy itemIdzostał zmieniony i odtwarza stronę tylko wtedy, gdy tak było. Ale wersja bez przesłonięcia zwraca tę samą pozycję, itemIdnawet jeśli strona jest faktycznie inna, a ViewPager nie definiuje, że strona jest zastępowana i należy ją odtworzyć.

Aby tego użyć, long idjest potrzebny na każdej stronie. Zwykle oczekuje się, że będzie unikalny, ale w tym przypadku sugeruję, że powinien on różnić się od poprzedniej wartości dla tej samej strony. Możliwe jest więc użycie licznika ciągłego w adapterze lub liczbach całkowitych losowych (o szerokim rozkładzie).

Myślę, że jest to bardziej spójny sposób, a raczej użycie tagów widoku wspomnianych jako rozwiązanie w tym temacie. Ale prawdopodobnie nie we wszystkich przypadkach.

atlascoder
źródło
1
To jest. Sprawdziłem to: po prostu POSITION_NONE to za mało. Dziękuję za wspaniałą odpowiedź! :)
Slava
Ta funkcja nie jest nawet obecna w PagerAdapter. Co to za klasa?
Saket,
@Saket, masz rację, klasa jestFragmentPagerAdapter
atlascoder
6

Znalazłem bardzo interesującą decyzję dotyczącą tego problemu. Zamiast używać FragmentPagerAdapter , który przechowuje w pamięci wszystkie fragmenty, możemy użyć FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ), który przeładowuje fragment za każdym razem, gdy go wybieramy.

Realizacje obu adapterów są identyczne. Musimy więc po prostu zmienić „ przedłużyć FragmentPagerAdapter ” na „ przedłużyć FragmentStatePagerAdapter

Рома Богдан
źródło
Dobra uwaga, pomoże innym rozwiązać problemy z pamięcią viewPager związane z fragmentem
Ashu Kumar
Świetna odpowiedź, bardzo mi to pomogło.
Sergio Marani
5

Po wielu poszukiwaniach tego problemu znalazłem naprawdę dobre rozwiązanie, które moim zdaniem jest właściwą drogą do rozwiązania tego problemu. Zasadniczo instantiateItem jest wywoływany tylko wtedy, gdy widok jest tworzony i nigdy więcej, chyba że widok zostanie zniszczony (dzieje się tak, gdy przesłonisz funkcję getItemPosition, aby zwrócić POSITION_NONE). Zamiast tego zapisz utworzone widoki i zaktualizuj je w adapterze, wygeneruj funkcję get, aby ktoś inny mógł ją zaktualizować, lub ustaw funkcję, która aktualizuje adapter (moje ulubione).

Tak więc w MyViewPagerAdapter dodaj zmienną, taką jak:

private View updatableView;

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

w ten sposób możesz stworzyć funkcję, która zaktualizuje twój widok:

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

Mam nadzieję że to pomoże!

Niemiecki
źródło
Zdałem sobie sprawę, że nie ustawiałem danych instantiateItem, dlatego moje widoki nie były aktualizowane. Z twojej odpowiedzi zrozumiałem. +1
Phani Rithvij
5

Dwa i pół roku po tym, jak OP zadał swoje pytanie, ta kwestia wciąż jest, no cóż, nadal problemem. To oczywiste, że priorytet Google w tym zakresie nie jest szczególnie wysoki, więc zamiast znaleźć rozwiązanie, znalazłem obejście. Ogromnym przełomem było dla mnie odkrycie, jaka była prawdziwa przyczyna problemu (patrz zaakceptowana odpowiedź w tym poście ). Gdy stało się jasne, że problem polega na tym, że wszystkie aktywne strony nie są odpowiednio odświeżane, moje obejście było oczywiste:

W moim fragmencie (na stronach):

  • Wziąłem cały kod wypełniający formularz z onCreateView i umieściłem go w funkcji o nazwie PopulateForm, którą można wywoływać z dowolnego miejsca, a nie z frameworka. Ta funkcja próbuje uzyskać bieżący widok za pomocą getView, a jeśli ma wartość null, po prostu zwraca. Ważne jest, aby PopulateForm zawierał tylko kod, który wyświetla - cały inny kod, który tworzy detektory FocusChange i tym podobne, jest nadal w OnCreate
  • Utwórz wartość logiczną, która może być używana jako flaga wskazująca, że ​​formularz musi zostać ponownie załadowany. Mój jest mbReloadForm
  • Zastąp OnResume (), aby wywołać PopulateForm (), jeśli ustawiono mbReloadForm.

W mojej działalności, gdzie ładuję strony:

  • Przejdź do strony 0 przed zmianą czegokolwiek. Korzystam z FragmentStatePagerAdapter, więc wiem, że dotyczy to maksymalnie dwóch lub trzech stron. Przejście na stronę 0 gwarantuje, że będę mieć problem tylko na stronach 0, 1 i 2.
  • Przed wyczyszczeniem starej listy wybierz jej rozmiar (). W ten sposób wiesz, ile stron dotyczy błąd. Jeśli> 3, zmniejsz go do 3 - jeśli używasz innego PagerAdapter, będziesz musiał zobaczyć, z iloma stronami masz do czynienia (może wszystkie?)
  • Załaduj ponownie dane i wywołaj pageAdapter.notifyDataSetChanged ()
  • Teraz, dla każdej strony, której dotyczy problem, sprawdź, czy strona jest aktywna za pomocą pager.getChildAt (i) - informuje to, czy masz widok. Jeśli tak, wywołaj pager.PopulateView (). Jeśli nie, ustaw flagę ReloadForm.

Po tym, gdy przeładujesz drugi zestaw stron, błąd nadal spowoduje, że niektóre wyświetlą stare dane. Zostaną one jednak odświeżone i zobaczysz nowe dane - użytkownicy nie będą wiedzieli, że strona była zawsze niepoprawna, ponieważ odświeżenie nastąpi zanim zobaczą stronę.

Mam nadzieję, że to komuś pomoże!

RichardP
źródło
5

Wszystkie te rozwiązania mi nie pomogły. dlatego znalazłem działające rozwiązanie: możesz za setAdapterkażdym razem, ale to nie wystarczy. powinieneś to zrobić przed zmianą adaptera:

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

a potem:

viewPager.setAdapter(adapter);
Mahdi Perfect
źródło
Dzięki! W moim przypadku użyłem ViewPagerfragmentu wewnętrznego, więc zastąpiłem slideShowPagerAdapter.getFragmentManager()go getChildFragmentManager(). Może getFragmentManager()pomoże w twoim przypadku. Użyłem FragmentPagerAdapternie FragmentStatePagerAdapter. Zobacz także stackoverflow.com/a/25994654/2914140 na hacky sposób.
CoolMind,
5

Znacznie łatwiejszy sposób: użyj FragmentPagerAdapteri zawiń stronicowane widoki na fragmenty. Są aktualizowane

alfongj
źródło
5

Dziękujemy rui.araujo i Alvaro Luis Bustamante. Na początku staram się używać sposobu rui.araujo, ponieważ jest to łatwe. Działa, ale gdy dane się zmienią, strona przerysuje oczywiście. Jest źle, więc staram się używać sposobu Alvaro Luisa Bustamante. Jest idealny. Oto kod:

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

A kiedy zmieniają się dane:

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();
    }
}
evan.z
źródło
4

Na wypadek, gdyby ktoś używał adaptera opartego na FragmentStatePagerAdapter (który pozwoli ViewPagerowi stworzyć minimum stron potrzebnych do wyświetlania, maksymalnie 2 w moim przypadku), odpowiedź @ rui.araujo na nadpisanie getItemPosition w twoim adapterze nie spowoduje znacznych strat, ale nadal można ulepszyć.

W pseudokodzie:

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;
}
Jerry Tian
źródło
w ViewPager dość łatwo było poznać pozycję z przeszłości, najdoskonalszą implementacją jest po prostu zwrócenie nowej pozycji w getItemPosition()przypadku zmiany zestawu danych, a ViewPager będzie wiedział, że zostały zmienione, czy nie. niestety ViewPager tego nie zrobił.
VinceStyling
3

Miałem podobny problem, w którym miałem cztery strony, a jedna ze stron zaktualizowała widoki w pozostałych trzech. Udało mi się zaktualizować widżety (SeekBars, TextViews itp.) Na stronie sąsiadującej z bieżącą stroną. Ostatnie dwie strony miałyby niezainicjowane widżety podczas połączenia mTabsAdapter.getItem(position).

Aby rozwiązać problem, skorzystałem z niego setSelectedPage(index)przed zadzwonieniem getItem(position). Spowoduje to utworzenie instancji strony, co pozwoli mi zmieniać wartości i widżety na każdej stronie.

Po wszystkich aktualizacjach użyłbym, setSelectedPage(position)a następnie notifyDataSetChanged().

Możesz zobaczyć lekkie migotanie w ListView na głównej stronie aktualizacji, ale nic zauważalnego. Nie przetestowałem tego gruntownie, ale to rozwiązuje mój bezpośredni problem.

enKoder
źródło
Cześć, jestem zainteresowany twoim kodem, mam do czynienia z podobnym problemem, jestem w stanie zaktualizować sąsiednie fragmenty, ale obecny fragment, który jest widoczny, nie jest aktualizowany. Jestem bardzo zdezorientowany wszystkimi odpowiedziami tutaj. jak mogę zaktualizować widoki w bieżącym fragmencie pokazanym przez adapter VIewPager
Pankaj Nimgade
3

Właśnie zamieszczam tę odpowiedź na wypadek, gdyby ktokolwiek uznał ją za przydatną. Aby zrobić dokładnie to samo, po prostu wziąłem kod źródłowy ViewPager i PagerAdapter z biblioteki kompatybilności i skompilowałem go w moim kodzie (musisz samodzielnie rozwiązać wszystkie błędy i zaimportować, ale na pewno da się to zrobić).

Następnie w CustomViewPager utwórz metodę o nazwie updateViewAt (pozycja int). Sam widok można uzyskać z ArrayList mItems zdefiniowanych w klasie ViewPager (musisz ustawić identyfikator dla widoków w natychmiastowym elemencie i porównać ten identyfikator z pozycją w metodzie updateViewAt ()). Następnie możesz zaktualizować widok w razie potrzeby.

Naveen LP
źródło
2

Chyba mam logikę ViewPager.

Jeśli muszę odświeżyć zestaw stron i wyświetlić je w oparciu o nowy zestaw danych, wywołuję zawiadomienieDataSetChanged () . Następnie ViewPager wykonuje szereg wywołań funkcji getItemPosition () , przekazując tam Fragment jako Obiekt. Ten fragment może pochodzić ze starego zestawu danych (który chcę odrzucić) lub z nowego (który chcę wyświetlić). Zastępuję więc getItemPosition () i tam muszę jakoś ustalić, czy mój Fragment pochodzi ze starego zestawu danych, czy z nowego.

W moim przypadku mam układ 2-panelowy z listą górnych elementów w lewym panelu i widokiem przeciągnięcia (ViewPager) po prawej. Tak więc przechowuję link do mojego bieżącego najwyższego elementu w moim PagerAdapter, a także wewnątrz każdego utworzonego fragmentu strony. Gdy wybrany najwyższy element na liście zmienia się, zapisuję nowy najwyższy element w PagerAdapter i wywołuję powiadomienieDataSetChanged () . A w przesłoniętej funkcji getItemPosition () porównuję najwyższy element z mojego adaptera z najwyższym elementem z mojego fragmentu. I tylko jeśli nie są sobie równe, zwracam POSITION_NONE. Następnie PagerAdapter przywraca wszystkie fragmenty, które zwróciły POSITION_NONE.

UWAGA. Lepszym pomysłem może być przechowywanie identyfikatora górnego elementu zamiast odwołania.

Poniższy fragment kodu jest nieco schematyczny, ale dostosowałem go z faktycznie działającego kodu.

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

Dzięki za wszystkich poprzednich badaczy!

Stanisław Karakhanov
źródło
2

Poniższy kod działał dla mnie.

Utwórz klasę, która rozszerza klasę FragmentPagerAdapter, jak poniżej.

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

Następnie w każdym utworzonym fragmencie utwórz metodę updateFragment. W tej metodzie zmieniasz rzeczy, które musisz zmienić we fragmencie. Na przykład w moim przypadku Fragment0 zawierał GLSurfaceView, który wyświetla obiekt 3d oparty na ścieżce do pliku .ply, więc w mojej metodzie updateFragment zmieniam ścieżkę do tego pliku warstwy.

następnie utwórz instancję ViewPager,

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

i instancja Adpatera,

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

to zrób to,

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

Następnie wewnątrz klasy, jeśli zainicjalizowałeś klasę Adapter powyżej i utworzyłeś viewPager, za każdym razem, gdy chcesz zaktualizować jeden ze swoich fragmentów (w naszym przypadku Fragment0), użyj następujących poleceń:

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.

To rozwiązanie zostało oparte na technice zaproponowanej przez Alvaro Luisa Bustamante.

Brainnovo
źródło
1

1. Najpierw musisz ustawić metodę getItemposition w swojej klasie Pageradapter 2. Musisz odczytać dokładną pozycję swojego View Pager 3. następnie wyślij tę pozycję jako lokalizację danych nowego 4. Zapisz przycisk aktualizacji po kliknięciu słuchacza wewnątrz setonPageChange słuchacz

ten kod programu jest nieco zmodyfikowany, aby ustawić tylko określony element pozycji

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) {
    }
}
}
Naveen Kumar
źródło
1

szło mi to, co zadziałało viewPager.getAdapter().notifyDataSetChanged();

oraz w adapterze umieszczając kod do aktualizacji widoku w ten getItemPositionsposób

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

może nie jest to najodpowiedniejszy sposób, ale zadziałało ( return POSITION_NONEsztuczka spowodowała u mnie awarię, więc nie było opcji)

Fonix
źródło
1

Możesz dynamicznie aktualizować wszystkie fragmenty, które możesz zobaczyć w trzech krokach.

W twoim adapterze:

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

Teraz w twojej aktywności:

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

}}

Wreszcie w twoim fragmencie coś takiego:

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

}}

Pełny kod możesz zobaczyć tutaj .

Dzięki Alvaro Luis Bustamante.

Cabezas
źródło
1

Zawsze zwracanie POSITION_NONEjest proste, ale mało wydajne, ponieważ wywołuje tworzenie instancji wszystkich stron, które już zostały utworzone.

Utworzyłem bibliotekę ArrayPagerAdapter, aby dynamicznie zmieniać elementy w PagerAdapters.

Wewnętrznie, adaptery Ta biblioteka jest powrót POSITION_NONEna getItemPosiition()tylko wtedy, gdy jest to konieczne.

Korzystając z tej biblioteki, możesz dynamicznie zmieniać elementy, takie jak śledzenie.

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

Biblioteka Thils obsługuje również strony utworzone przez Fragmenty.

T.Nakama
źródło
1

To jest dla wszystkich takich jak ja, którzy muszą zaktualizować Viewpager z usługi (lub innego wątku w tle) i żadna z propozycji nie zadziałała: po pewnym czasie logowania zauważyłem, że metoda powiadomienieDataSetChanged () nigdy nie zwraca. getItemPosition (obiekt Object) nazywa się tam wszystkie końce bez dalszego przetwarzania. Następnie znalazłem w dokumentach nadrzędnej klasy PagerAdapter (nie ma go w dokumentach podklas): „Zmiany zestawu danych muszą nastąpić w głównym wątku i muszą kończyć się wywołaniem powiadomieniaDataSetChanged ()”. Tak więc działającym rozwiązaniem w tym przypadku było (użycie FragmentStatePagerAdapter i getItemPosition (obiekt obiektu) ustawione na zwrócenie POSITION_NONE):

a następnie wywołanie powiadomieniaDataSetChanged ():

runOnUiThread(new Runnable() {
         @Override
         public void run() {
             pager.getAdapter().notifyDataSetChanged();
         }
     });
Helmwag
źródło
1

Możesz dodać transformację pagera do Viewpager w ten sposób

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

W poniższym kodzie zmieniłem kolor widoku w czasie wykonywania podczas przewijania pagera

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);
     }
   }
}
Akshay dashore
źródło
1

Wiem, że jestem późno, ale to może komuś pomóc. Po prostu rozszerzam akcentującą odpowiedź i dodałem również komentarz do niej.

dobrze,

sama odpowiedź mówi, że jest nieefektywna

więc aby to zrobić tylko raz, gdy jest to wymagane, możesz to zrobić

private boolean refresh;

public void refreshAdapter(){
    refresh = true;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    if(refresh){
        refresh = false;
        return POSITION_NONE;
    }else{
        return super.getItemPosition(object);
    }
}
przykład
źródło
1

ViewPager nie został zaprojektowany do obsługi dynamicznej zmiany widoku.

Miałem to potwierdzenie, szukając innego błędu związanego z tym https://issuetracker.google.com/issues/36956111, aw szczególności https://issuetracker.google.com/issues/36956111#comment56

To pytanie jest nieco stare, ale Google niedawno rozwiązało ten problem z ViewPager2 . Pozwoli to zastąpić ręcznie wykonane (nieobsługiwane i potencjalnie błędne) rozwiązania standardowymi. Zapobiega także niepotrzebnemu odtwarzaniu widoków, jak to robią niektóre odpowiedzi.

Przykłady ViewPager2 można sprawdzić https://github.com/googlesamples/android-viewpager2

Jeśli chcesz korzystać z ViewPager2, musisz dodać następującą zależność w pliku build.gradle:

  dependencies {
     implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
  }

Następnie możesz zastąpić ViewPager w pliku xml:

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

Następnie będziesz musiał zastąpić ViewPager przez ViewPager2 w swojej działalności

ViewPager2 potrzebuje RecyclerView.Adapter lub FragmentStateAdapter, w twoim przypadku może to być RecyclerView.Adapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
} 

W przypadku korzystania z TabLayout, możesz użyć TabLayoutMediator:

        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
            @Override
            public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                // configure your tab here
                tab.setText(tabs.get(position).getTitle());
            }
        });

        tabLayoutMediator.attach();

Następnie będziesz mógł odświeżyć swoje widoki, modyfikując dane adaptera i wywołując metodę replaceDataSetChanged

Yves Delerm
źródło
0

Myślę, że podjąłem prosty sposób powiadamiania o zmianach w zestawie danych:

Najpierw zmień nieco sposób działania funkcji 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;
    }

w przypadku „updateView” wypełnij widok wszystkimi danymi, które chcesz wypełnić (setText, setBitmapImage, ...).

sprawdź, czy destroyView działa w następujący sposób:

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

Załóżmy teraz, że musisz zmienić dane, zrób to, a następnie wywołaj następną funkcję na 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
}

Na przykład, jeśli chcesz powiadomić wszystkie widoki wyświetlane przez viewPager, że coś się zmieniło, możesz wywołać:

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

Otóż ​​to.

programista Androida
źródło
0

Jeśli chodzi o to, co jest warte, na KitKat + wydaje się, że adapter.notifyDataSetChanged()to wystarczy, aby wyświetlić nowe widoki, pod warunkiem, że masz setOffscreenPageLimitwystarczająco wysoki poziom. Jestem w stanie uzyskać pożądane zachowanie viewPager.setOffscreenPageLimit(2).

mszaro
źródło
0

I rzeczywiście wykorzystać notifyDataSetChanged()na ViewPageri CirclePageIndicator, a potem zadzwonię destroyDrawingCache()na ViewPageri to działa .. Żaden z innymi rozwiązaniami pracował dla mnie.

czaku
źródło
Oryginalne pytanie ma prawie 2 lata, jestem pewien, że istnieje wiele różnic w interfejsie API Androida, ponieważ odpowiedzi tutaj mogą nie być odpowiednie dla wszystkich wersji interfejsu API Androida.
C0deAttack