Aktualizować ViewPager dynamicznie?

316

Nie mogę zaktualizować zawartości w ViewPager.

Jakie jest prawidłowe użycie metod instantiateItem () i getItem () w klasie FragmentPagerAdapter?

Używałem tylko metody getItem () do tworzenia instancji i zwracania moich fragmentów:

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

To działało dobrze. Tyle że nie mogę zmienić treści.

Więc znalazłem to: ViewPager PagerAdapter nie aktualizuje widoku

„Moje podejście polega na użyciu metody setTag () dla dowolnego utworzonego widoku w metodzie instantiateItem ()”

Teraz chcę zaimplementować instantiateItem (), aby to zrobić. Ale nie wiem, co muszę zwrócić (typ to Object) i jaka jest relacja z getItem (pozycja int)?

Przeczytałem referencję :

  • public abstract Fragment getItem (pozycja wewnętrzna)

    Zwraca Fragment powiązany z określoną pozycją.

  • public Object instantiateItem (kontener ViewGroup, pozycja int)

    Utwórz stronę dla danej pozycji. Adapter jest odpowiedzialny za dodanie widoku do podanego tutaj kontenera, ale musi tylko upewnić się, że zostanie to zrobione do czasu powrotu z finishUpdate (ViewGroup). Parametry

    kontener Zawierający widok, w którym strona będzie wyświetlana. pozycja Pozycja strony, która ma zostać utworzona.

    Zwroty

    Zwraca obiekt reprezentujący nową stronę. To nie musi być Widok, ale może to być inny kontener strony.

ale wciąż tego nie rozumiem.

Oto mój kod. Korzystam z pakietu wsparcia 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;
    }
}

Oto także ktoś z podobnym problemem, brak odpowiedzi http://www.mail-archive.com/[email protected]/msg200477.html

Ixx
źródło
Aby lepiej zrozumieć zaangażowane komponenty, pomocne może być przeczytanie mojego samouczka na temat ViewPager z kartami z osobną historią nawigacji wstecz. Zawiera działający przykład na GitHub i dodatkowe zasoby: medium.com/@nilan/…
marktani
dostałem problem stackoverflow.com/questions/40149039/...
albert
Możesz sprawdzić ten przykład w GitHub . Mam nadzieję, że ten przykład ci pomoże.
Cabezas
Dobre proste rozwiązanie: stackoverflow.com/a/35450575/2162226
Gene Bo
Google niedawno wprowadził ViewPager2, który upraszcza dynamiczne aktualizacje fragmentów / widoków. Możesz zobaczyć przykłady na github.com/googlesamples/android-viewpager2 lub tę odpowiedź na stackoverflow.com/a/57393414/1333448
Yves Delerm

Odpowiedzi:

605

Korzystając z FragmentPagerAdapter lub FragmentStatePagerAdapter najlepiej radzić sobie wyłącznie z getItem()dotykiem, a nie dotykać go instantiateItem()wcale. Interfejs instantiateItem()- destroyItem()- isViewFromObject()w PagerAdapter jest interfejsem niższego poziomu, którego FragmentPagerAdapter używa do implementacji znacznie prostszegogetItem() interfejsu.

Zanim przejdę do tego, powinienem to wyjaśnić

jeśli chcesz wyłączyć wyświetlane fragmenty, musisz unikać FragmentPagerAdapter i użyć FragmentStatePagerAdapter.

Wcześniejsza wersja tej odpowiedzi popełniła błąd przy użyciu FragmentPagerAdapter w swoim przykładzie - to nie zadziała, ponieważ FragmentPagerAdapter nigdy nie niszczy fragmentu po jego pierwszym wyświetleniu.

Nie polecam setTag()i findViewWithTag()Obejście zawarte w poście ty powiązane. Jak odkryłeś, używanie setTag()i findViewWithTag()nie działa z fragmentami, więc nie jest to dobre dopasowanie.

Właściwym rozwiązaniem jest zastąpienie getItemPosition(). Po notifyDataSetChanged()wywołaniu ViewPager wywołuje getItemPosition()wszystkie elementy w adapterze, aby sprawdzić, czy należy je przenieść w inne miejsce, czy usunąć.

Domyślnie getItemPosition()zwraca POSITION_UNCHANGED, co oznacza: „Ten obiekt jest w porządku tam, gdzie jest, nie niszcz go ani nie usuwaj”. Zwrócenie POSITION_NONErozwiązuje problem, mówiąc: „Ten obiekt nie jest już elementem, który wyświetlam, usuń go”. Dzięki temu usuwa i odtwarza każdy element w adapterze.

Jest to całkowicie uzasadniona poprawka! Ta poprawka sprawia, że ​​replaceDataSetChanged zachowuje się jak zwykły adapter bez recyklingu widoku. Jeśli zastosujesz tę poprawkę, a wydajność będzie satysfakcjonująca, możesz wziąć udział w wyścigach. Zadanie wykonane.

Jeśli potrzebujesz lepszej wydajności, możesz skorzystać z bardziej zaawansowanej getItemPosition()implementacji. Oto przykład pagera tworzącego fragmenty z listy ciągów:

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

Dzięki tej implementacji będą wyświetlane tylko fragmenty zawierające nowe tytuły. Fragmenty zawierające tytuły, które wciąż znajdują się na liście, zostaną zamiast tego przeniesione do nowej pozycji na liście, a fragmenty z tytułami, których już nie ma na liście, zostaną zniszczone.

Co jeśli fragment nie został ponownie utworzony, ale i tak wymaga aktualizacji? Aktualizacje żywego fragmentu najlepiej jest obsługiwać przez sam fragment. W końcu to zaleta posiadania fragmentu - jest to jego własny kontroler. Fragment może dodać detektor lub obserwator do innego obiektu w onCreate(), a następnie usunąć go onDestroy(), tym samym zarządzając samymi aktualizacjami. Nie musisz umieszczać całego kodu aktualizacji, getItem()tak jak w adapterze dla ListView lub innych typów AdapterView.

Ostatnia rzecz - tylko dlatego, że FragmentPagerAdapter nie niszczy fragmentu, nie oznacza, że ​​getItemPosition jest całkowicie bezużyteczny w FragmentPagerAdapter. Nadal możesz użyć tego wywołania zwrotnego, aby zmienić kolejność swoich fragmentów w ViewPager. Jednak nigdy nie usunie ich całkowicie z narzędzia FragmentManager.

Bill Phillips
źródło
4
Nie działa, wciąż niczego nie aktualizuje ... opublikował mój kod.
Ixx,
64
Ok, odkryłem problem - musisz użyć FragmentStatePagerAdapter zamiast FragmentPagerAdapter. FragmentPagerAdapter nigdy nie usuwa fragmentów z FragmentManager, jedynie je odłącza i dołącza. Sprawdź dokumentację, aby uzyskać więcej informacji - ogólnie rzecz biorąc, chcesz używać FragmentPagerAdapter tylko dla fragmentów, które są trwałe. Zredagowałem mój przykład z poprawką.
Bill Phillips
1
Sprawdzę to w pewnym momencie później ... dzięki za odpowiedź. A następnie akceptuję twoją odpowiedź, jeśli zadziała. Przed ostatnim komentarzem zaimplementowałem usuwanie i dodawanie nowego ViewPager za każdym razem i to działa. Niezbyt dobre rozwiązanie, ale nie mam teraz czasu, aby to zmienić.
Ixx
12
Masz rację, zmiana klasy adaptera na FragmentStatePagerAdapter rozwiązała wszystkie moje problemy. Dzięki!
Ixx
2
Nawet gdy zwracam POSITION_NONE, podczas używania FragmentStatePagerAdapterwidzę, że usuwa mój fragment i tworzy nowe wystąpienie. Ale nowa instancja otrzymuje pakiet saveInstanceState ze stanem starego fragmentu. Jak mogę całkowicie zniszczyć fragment, aby pakiet był pusty po ponownym utworzeniu?
Singed
142

Zamiast powrocie POSITION_NONEz getItemPosition()powodując pełny widok rekreacji, to zrobić:

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

Twoje fragmenty powinny implementować UpdateableFragmentinterfejs:

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

i interfejs:

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

Twoja klasa danych:

public class UpdateData {
    //whatever you want here
}
M-WaJeEh
źródło
1
Czy to oznacza, że ​​Twoja MainActivity również musi zaimplementować interfejs? Proszę pomóż mi tutaj. W mojej obecnej implementacji używam mojej klasy MainActivity jako komunikatora między fragmentami, więc byłem zdezorientowany co do tego rozwiązania.
Rakeeb Rajbhandari,
1
Ok, odpowiedziałem w innym miejscu z pełną implementacją, gdzie aktualizuję Fragmentdynamicznie, spójrz: stackoverflow.com/questions/19891828/...
M-WaJeEh
@ M-WaJeEh może jakiś przykład !!! Mam widok listy z listą miast i po wybraniu miasta otwiera viewpager (3 strony) z informacjami o tym mieście. to działa ok. ale po wybraniu innego podglądu miasta tylko strona 3 d page on refreshes the 1-st page only after swiping pages? thw 2d jest zawsze pusta. dzięki
SERG
4
pośród wszystkich rzeczy, które widziałem przez ostatnie 2,5 dnia mojego życia zawodowego .. to jest jedyny, który pracował dla mnie .. wszelkiego rodzaju arigato, dzięki, spaciba, sukran .. n.
Orkun Ozen
2
Mother of Dragons !, to działa jak urok, jedyne rozwiązanie działało dla mnie ... bardzo dużo !!
Sebastián Guerrero,
24

dla tych, którzy wciąż napotykają ten sam problem, z którym miałem do czynienia, kiedy mam ViewPager7 fragmentów. domyślnie dla tych fragmentów ładowana jest treść angielska z APIusługi, ale tutaj problem polega na tym, że chcę zmienić język z działania ustawień i po zakończeniu działania ustawień chcę ViewPagerw działaniu głównym odświeżyć fragmenty, aby dopasować wybór języka od użytkownika i załadować treść w języku arabskim, jeśli użytkownik wybierze tutaj język arabski, co zrobiłem po raz pierwszy

1- Musisz użyć FragmentStatePagerAdapter, jak wspomniano powyżej.

2- na mainActivity i przesłaniam onResume i wykonałem następujące czynności

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

3-i zastąpiło getItemPosition()w mPagerAdapter i sprawiło, że zwróciło POSITION_NONE.

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

działa jak urok

Ziad Gholmish
źródło
1
Działa z wyjątkiem jednego miejsca. Po usunięciu ostatniego elementu w viewPager. To nie usuwa się z listy.
Vlado Pandžić,
Dynamicznie dodawałem fragmenty do lewej i prawej strony bieżącego fragmentu w przeglądarce. Ten zadziałał! Dzięki.
h8pathak,
15

Zetknąłem się z tym problemem i ostatecznie go rozwiązałem dzisiaj, więc zapisuję to, czego się nauczyłem i mam nadzieję, że będzie on pomocny dla kogoś, kto jest nowy w Androidzie ViewPageri będzie aktualizował się w tym momencie. Używam FragmentStatePagerAdapterna poziomie API 17 i obecnie mam tylko 2 fragmenty. Myślę, że musi być coś nie tak, popraw mnie, dziękuję. wprowadź opis zdjęcia tutaj

  1. Dane szeregowe należy załadować do pamięci. Można to zrobić za pomocą CursorLoader/ AsyncTask/ Thread. To, czy zostanie automatycznie załadowane, zależy od Twojego kodu. Jeśli używasz CursorLoader, jest ładowany automatycznie, ponieważ istnieje zarejestrowany obserwator danych.

  2. Po wywołaniu viewpager.setAdapter(pageradapter)adapter getCount()jest ciągle wywoływany w celu budowania fragmentów. Więc jeśli dane są ładowane, getCount()może zwrócić 0, więc nie trzeba tworzyć fałszywych fragmentów, aby nie były wyświetlane żadne dane.

  3. Po załadowaniu danych adapter nie będzie budował fragmentów automatycznie, ponieważ getCount()wciąż ma wartość 0, więc możemy ustawić rzeczywiście załadowany numer danych, który ma zostać zwrócony getCount(), a następnie wywołać numer adaptera notifyDataSetChanged(). ViewPagerzacznij tworzyć fragmenty (tylko pierwsze 2 fragmenty) według danych w pamięci. Jest to zrobione przed notifyDataSetChanged()zwrotem. Następnie ViewPagerma odpowiednie fragmenty, których potrzebujesz.

  4. Jeśli dane w bazie danych i pamięci są zarówno aktualizowane (zapis), lub tylko dane w pamięci są aktualizowane (zapisywanie), lub aktualizowane są tylko dane w bazie danych. W dwóch ostatnich przypadkach, jeśli dane nie są automatycznie ładowane z bazy danych do pamięci (jak wspomniano powyżej). ViewPagerI adapter pager właśnie do czynienia z danymi w pamięci.

  5. Więc kiedy dane w pamięci są aktualizowane, wystarczy wywołać adapter notifyDataSetChanged(). Ponieważ fragment jest już utworzony, adapter onItemPosition()zostanie wywołany przed notifyDataSetChanged()zwróceniem. Nic nie trzeba robić getItemPosition(). Następnie dane są aktualizowane.

oscarthecat
źródło
nie znalazłem nic, co muszę zrobić po aktualizacji danych (w pamięci), wystarczy zadzwonić, notifyDataSetChanged()a następnie zaktualizować automatycznie, ponieważ fragment (z widokiem mapy), którego używam, odświeży się. jeśli nie jest, jak fragment statyczny, będę musiał zadzwonić onItemPositon()i POSITION_NONE przepraszam za to
oscarthecat
Czy ktoś ma pojęcie o tym, jakiego narzędzia użyto do stworzenia tego schematu?
JuiCe,
12

Wypróbuj destroyDrawingCache()ViewPager po notifyDataSetChanged()w swoim kodzie.

czaku
źródło
3
To samo tutaj. Działa świetnie, dziękuję. Miałem szczególnie trudny czas, ponieważ nie korzystam Fragmentsz pagera widokowego. Więc dziękuję!
Seth
11

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) Utwórz metodę, aby ponownie ustawićadapter danych 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
czy możesz mi pomóc w moim problemie stackoverflow.com/questions/40149039/…
albert
czy możesz mi pomóc z tym problemem stackoverflow.com/questions/41547995/…
viper
10

Z jakiegoś powodu żadna z odpowiedzi nie działała dla mnie, więc musiałem zastąpić metodę restoreState bez wywoływania super w moim fragmentStatePagerAdapter. Kod:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

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

}
hordurh
źródło
1
Po wypróbowaniu wszystkiego innego w tej SO odpowiedzi, to ta, która działała dla mnie. Kończę () działanie, które powraca do działania, które obsługuje dany PagerAdapter. Chcę, aby wszystkie moje fragmenty zostały ponownie utworzone w tym przypadku, ponieważ Aktywność, którą właśnie zakończyłem, mogła zmienić stan używany do rysowania fragmentów.
JohnnyLambada
OMG .. to działa .. to działa .. kudos: D W moim przypadku muszę usunąć obiekt bieżącego obiektu przeglądarki. ale bieżący fragment nie aktualizuje się po wypróbowaniu wszystkich powyższych metod / kroków.
Rahul Khurana
3

Lekko zmodyfikowałem rozwiązanie dostarczone przez Billa Phillipsa, aby dostosować je do moich potrzeb

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

aby getItemPosition()zwrotyPOSITION_NONE tylko do tych fragmentów, które są obecnie w FragmentManagerprzypadku getItemPositionnazywa. (Zauważ, że to FragmentStatePageri ViewPagerpowiązane z nim są zawarte we fragmencie nie będącym działaniem)

czekaj
źródło
2

Miałem podobny problem, ale nie chcę ufać istniejącym rozwiązaniom (na stałe zakodowane nazwy znaczników itp.) I nie mogłem sprawić, by rozwiązanie M-WaJeEh działało dla mnie. Oto moje rozwiązanie:

Przechowuję odwołania do fragmentów utworzonych w getItem w tablicy. Działa to dobrze, dopóki aktywność nie zostanie zniszczona z powodu zmiany konfiguracji lub braku pamięci lub czegokolwiek (-> po powrocie do aktywności fragmenty wracają do ostatniego stanu bez ponownego wywołania „getItem”, a zatem bez aktualizacji tablicy ).

Aby uniknąć tego problemu, zaimplementowałem instantiateItem (ViewGroup, int) i zaktualizowałem tam moją tablicę, jak poniżej:

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

Tak więc z jednej strony cieszę się, że znalazłem rozwiązanie, które działa dla mnie i chciałem się z tobą podzielić, ale chciałem również zapytać, czy ktoś inny próbował czegoś podobnego i czy jest jakiś powód, dla którego nie powinienem był tego robić czy to tak? Jak dotąd działa dla mnie bardzo dobrze ...

jpm
źródło
Zrobiłem coś związanego w stackoverflow.com/a/23427215/954643 , chociaż powinieneś usunąć element z myFragmentsw public void destroyItem(ViewGroup container, int position, Object object)jak dobrze więc nie chwycić nieświeży odniesienie fragement.
qix
1

Korzystam z biblioteki EventBus do aktualizacji Fragmenttreści ViewPager. Logika jest prosta, podobnie jak dokument EventBus, jak to zrobić . Nie ma potrzeby kontrolowania FragmentPagerAdapterinstancji. Kod jest tutaj:

1: Zdefiniuj zdarzenia

Określ, który komunikat jest potrzebny do aktualizacji.

public class UpdateCountEvent {
    public final int count;

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

2. Przygotuj subskrybentów

Napisz poniższy kod we fragmencie, który wymaga aktualizacji.

@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 wydarzenia

Napisz poniższy kod w innym Activitylub innym, Fragmentktóry musi zaktualizować parametr

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));
Fantasy Fang
źródło
1

Jeśli chcesz korzystać z FragmentStatePagerAdapter, spójrz na 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 . Występują problemy z FragmentStatePagerAdapter, które mogą, ale nie muszą, powodować kłopotu z twoim przypadkiem użycia.

Link ma również kilka rozwiązań. Niewiele może pasować do twoich wymagań.

cgr
źródło
Z tego adresu URL znalazłem rozwiązanie. Użyłem zmodyfikowanego FragmentStatePagerAdapter z tego gist.github.com/ypresto/8c13cb88a0973d071a64 w moim kodzie i zaczął on działać tak, jak powinien.
Rafael
Fajne. Tak. Link ma też kilka rozwiązań.
cgr
1

Przeżyłem ten sam problem i szukałem zbyt wiele razy. Jakakolwiek odpowiedź podana w stackoverflow lub przez google nie była rozwiązaniem mojego problemu. Mój problem był łatwy. Mam listę, pokazuję ją w przeglądarce. Po dodaniu nowego elementu do nagłówka listy i odświeżeniu podglądu nic się nie zmieniło. Moje ostateczne rozwiązanie było bardzo łatwe dla każdego. Gdy nowy element zostanie dodany do listy i chcesz odświeżyć listę. Najpierw ustaw adapter podglądu na null, a następnie ponownie utwórz adapter i ustaw i dla niego na viewpager.

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

Upewnij się, że adapter musi rozszerzyć FragmentStatePagerAdapter

Nebyan
źródło
1

Oto moja implementacja, która zawiera informacje z @Bill Phillips One przez większość czasu jest buforowana fragmentami, z wyjątkiem sytuacji, gdy dane uległy zmianie. Prosty i wydaje się działać dobrze.

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
źródło
0

Próbowałem tylu różnych podejść, ale żadne z nich tak naprawdę nie rozwiało mojego problemu. Poniżej przedstawiam, w jaki sposób rozwiązuję to za pomocą kombinacji rozwiązań dostarczonych przez was wszystkich. Dziękuję wszystkim.

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

Chcę tylko odświeżyć stronę 0 po onResume ().

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

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

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

W mojej FragmentsMain znajduje się publiczna „strona” całkowita, która może mi powiedzieć, czy jest to strona, którą chcę odświeżyć.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;
Ted Hsieh
źródło
0

Wiem, że spóźniłem się na przyjęcie. Rozwiązałem problem, dzwoniąc TabLayout#setupWithViewPager(myViewPager);zaraz poFragmentPagerAdapter#notifyDataSetChanged();

theapache64
źródło
0

To rozwiązanie nie będzie działać dla wszystkich, ale w moim przypadku każdy fragment w moim ViewPager ma inną klasę i tylko jedna z nich istnieje na raz.

Przy takim ograniczeniu to rozwiązanie jest bezpieczne i powinno być bezpieczne do użycia w produkcji.

private void updateFragment(Item item) {

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

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

    }
}

Jeśli masz wiele wersji tego samego fragmentu, możesz użyć tej samej strategii do wywołania metod na tych fragmentach, aby ustalić, czy jest to fragment, który chcesz zaktualizować.

Kevin Grant
źródło
0

Może to komuś pomóc - w moim przypadku przy wstawianiu nowej strony pager podglądu pytał dwukrotnie o pozycję istniejącego fragmentu, ale nie pytał o pozycję nowego elementu, powodując nieprawidłowe zachowanie i brak wyświetlania danych.

  1. Skopiuj źródło dla FragmentStatePagerAdapter (wydaje się, że nie był aktualizowany od wieków).

  2. Przesłoń powiadomienieDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Dodaj kontrolę poczytalności, aby destroyItem (), aby uniknąć awarii:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }
Meanman
źródło
0

Przejrzałem wszystkie powyższe odpowiedzi i wiele innych postów, ale wciąż nie mogłem znaleźć czegoś, co zadziałałoby dla mnie (z różnymi typami fragmentów wraz z dynamicznym dodawaniem i usuwaniem zakładek). Poniższe podejście FWIW działało dla mnie (na wypadek, gdyby ktoś miał takie same problemy).

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);
    }
}
John O'Reilly
źródło
0

Użyj FragmentStatePagerAdapter zamiast FragmentPagerAdapter, jeśli chcesz odtworzyć lub ponownie załadować fragment na podstawie indeksu Na przykład, jeśli chcesz ponownie załadować fragment inny niż FirstFragment, możesz sprawdzić instancję i zwrócić pozycję w ten sposób

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }
HemangNirmal
źródło
0

Musisz zmienić element mFragments getItemPosition w instantiateItem.

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

Oparty na AndroidX FragmentStatePagerAdapter.java, ponieważ mFragments pozycja elementów nie zmienia się podczas wywoływania powiadomieniaDataSetChanged ().

Źródło: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Przykład: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

Możesz uruchomić ten projekt, aby potwierdzić sposób działania. https://github.com/cuichanghao/infivt

Changhao Cui
źródło