So bestimmen Sie, wann Fragment in ViewPager sichtbar wird

754

Problem: Fragment onResume()in ViewPagerwird ausgelöst, bevor das Fragment tatsächlich sichtbar wird.

Zum Beispiel habe ich 2 Fragmente mit ViewPagerund FragmentPagerAdapter. Das zweite Fragment ist nur für autorisierte Benutzer verfügbar, und ich muss den Benutzer bitten, sich anzumelden, wenn das Fragment sichtbar wird (mithilfe eines Warndialogs).

ABER das ViewPagererstellt das zweite Fragment, wenn das erste sichtbar ist, um das zweite Fragment zwischenzuspeichern, und macht es sichtbar, wenn der Benutzer mit dem Wischen beginnt.

Das onResume()Ereignis wird also im zweiten Fragment ausgelöst, lange bevor es sichtbar wird. Aus diesem Grund versuche ich, ein Ereignis zu finden, das ausgelöst wird, wenn das zweite Fragment sichtbar wird, um zum richtigen Zeitpunkt einen Dialog anzuzeigen.

Wie kann das gemacht werden?

4ntoine
quelle
18
"Ich habe 2 Fragmente mit ViewPager und FragmentPagerAdapter. Das zweite Fragment kann nur für autorisierte Benutzer verfügbar sein und ich sollte nach der Anmeldung fragen, wenn das Fragment sichtbar wird (Warndialog)." - IMHO, das ist schrecklich UX. Das Aufrufen eines Dialogfelds, weil der Benutzer horizontal gewischt hat, würde mich veranlassen, Ihnen im Play Store eine Ein-Stern-Bewertung zu geben.
CommonsWare
Ist es besser, Informationen nur in TextView mit der Schaltfläche "Anmelden" anzuzeigen? Was ist Ihre Lösung für diesen Fall?
4.
5
"Es müssen keine Daten geladen werden, wenn sie nicht angezeigt werden." - dann sollten Sie es nicht in eine setzen ViewPager. In einem zweiseitigen Pager werden beide Seiten sofort geladen, ob Sie möchten oder nicht. Die Benutzererfahrung von ViewPagersoll sein, dass der Inhalt sofort nach dem Wischen da ist, nicht einige Zeit später. Aus diesem Grund wird ViewPagereine Seite vor dem Sichtbaren initialisiert, um die Benutzererfahrung zu gewährleisten.
CommonsWare
2
Es scheint, dass ViewPager nicht flexibel genug ist und das Caching nicht deaktivieren kann, da setOffscreenPageLimit mindestens 1 beträgt: stackoverflow.com/questions/10073214/… . Sie sehen keinen Grund dafür und das erwartete Verhalten (im Falle eines obligatorischen Caching) besteht darin, ein Fragment zu erstellen, ABER das Fragment onResume () des Feuers, wenn das Fragment sichtbar wird.
4.
1
Etwas spät, aber für alle, die mit demselben Problem konfrontiert sind, können Sie die FragmentViewPager- Bibliothek (ich bin der Autor) ausprobieren , die sich mit diesem Problem befasst und einige zusätzliche Funktionen bietet. Ein Beispiel finden Sie auf der GitHub-Seite des Projekts oder in dieser Stackoverflow-Antwort .
S. Brukhanda

Antworten:

582

So bestimmen Sie, wann Fragment in ViewPager sichtbar wird

Sie können durch Überschreiben der folgenden Aktionen setUserVisibleHintin Ihrer Fragment:

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}
gorn
quelle
7
Dank des heutigen Updates der Android Support Library (Version 11) ist das Problem mit den sichtbaren Hinweisen des Benutzers endlich behoben. Es ist jetzt sicher, vom Benutzer sichtbare Hinweise für ViewPager zu verwenden.
Oasis Feng
58
Ich habe festgestellt, dass die setUserVisibleHint-Methode aufgerufen wird, BEVOR die onCreateView aufgerufen wird, und dies macht es schwierig, eine Initialisierung zu verfolgen.
AndroidDev
11
@AndroidDev Wenn Sie Code ausführen möchten, wenn der Hinweis als wahr eingeht, der Ansichtsbaum jedoch bereits initialisiert werden muss, wickeln Sie diesen Codeblock einfach ein isResumed(), um eine NPE zu vermeiden. Ich habe gut für mich gearbeitet.
Ravi Thapliyal
13
Es ist einfach lächerlich, dass es so viele verschiedene Hacks für etwas gibt, das das SDK standardmäßig bereitstellen sollte.
Mike6679
15
setUserVisibleHintist jetzt veraltet
SR
525

UPDATE : Die Android Support Library (Version 11) hat das Problem mit den sichtbaren Hinweisen des Benutzers endgültig behoben. Wenn Sie nun die Support Library für Fragmente verwenden, können Sie die Änderungen sicher verwenden getUserVisibleHint()oder überschreiben setUserVisibleHint(), wie in Gorns Antwort beschrieben.

UPDATE 1 Hier ist ein kleines Problem mit getUserVisibleHint(). Dieser Wert ist standardmäßig true.

// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;

Es kann also ein Problem auftreten, wenn Sie versuchen, es zu verwenden, bevor setUserVisibleHint()es aufgerufen wurde. Um onCreatedieses Problem zu umgehen, können Sie in einer solchen Methode einen Wert festlegen .

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);

Die veraltete Antwort:

In den meisten Anwendungsfällen wird jeweils ViewPagernur eine Seite angezeigt. Die vorab zwischengespeicherten Fragmente werden jedoch auch in den Status "sichtbar" (tatsächlich unsichtbar) versetzt, wenn Sie FragmentStatePagerAdapterin verwenden Android Support Library pre-r11.

Ich überschreibe:

public class MyFragment extends Fragment {
    @Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {
            // ...
        }
    }
   // ...
}

Um den Fokusstatus des Fragments zu erfassen, den ich für den am besten geeigneten Status der "Sichtbarkeit" halte, meinen Sie, da nur ein Fragment in ViewPager seine Menüelemente zusammen mit den Elementen der übergeordneten Aktivität platzieren kann.

Oase Feng
quelle
73
Seien Sie vorsichtig, wenn Sie getActivity () verwenden. Beim ersten Start ist es null.
Vovkab
10
Beachten Sie, dass setUserVisibleHint () in FragmentPagerAdapter immer ordnungsgemäß funktioniert hat.
Louielouie
8
Diese Lösung (und auch die von Gorn) bleibt etwas zurück. In Fällen, in denen der Viewpager schnell und mehrfach gewischt wird, wird diese Methode mit einer Verzögerung aufgerufen (wenn das Fragment nicht mehr sichtbar / unsichtbar ist).
AsafK
3
Ich trueerhalte für getUserVisibleHint (), wenn onCreateOptionsMenues aufgerufen wird, und wenn setUserVisibleHintes aufgerufen wird, wurde das Menü anscheinend noch nicht erstellt. Letztendlich bekomme ich zwei Optionen Menüs hinzugefügt, wenn ich nur das Menü aus dem sichtbaren Fragment haben möchte. Anregungen dazu?
cYrixmorten
13
setUserVisibleHintist jetzt veraltet
SR
143

Dies scheint das normale onResume()Verhalten wiederherzustellen, das Sie erwarten würden. Es funktioniert gut, wenn Sie die Home-Taste drücken, um die App zu verlassen und dann die App erneut aufzurufen. onResume()wird nicht zweimal hintereinander aufgerufen.

@Override
public void setUserVisibleHint(boolean visible)
{
    super.setUserVisibleHint(visible);
    if (visible && isResumed())
    {
        //Only manually call onResume if fragment is already visible
        //Otherwise allow natural fragment lifecycle to call onResume
        onResume();
    }
}

@Override
public void onResume()
{
    super.onResume();
    if (!getUserVisibleHint())
    {
        return;
    }

    //INSERT CUSTOM CODE HERE
}
craigrs84
quelle
4
Genau das, wonach ich gesucht habe. Behebt das Problem mit setUserVisibleHintdem vorherigen Aufruf onCreateViewund das setUserVisibleHintwird nicht aufgerufen, wenn die App in den Hintergrund und dann in den Vordergrund geht. Genial! Vielen Dank!
Alex
11
Ich denke, onResume selbst aufzurufen ist eine ziemlich schlechte Idee, aber ansonsten befindet sich alles, was Sie zur Beantwortung der Frage dieses Beitrags benötigen, in der Funktion setUserVisibleHint!
Quentin G.
4
Ich würde lieber eine einfache onVisibleToUser()Methode implementieren und diese von onResume()und nach setUserVisibleHint(boolean)aufrufen onResume()lassen, anstatt mich selbst anzurufen und Rückrufe im Lebenszyklus zu stören. Ansonsten denke ich, dass dieser Ansatz gut funktioniert, danke!
Stephan Henningsen
1
Ja, ich stimme den obigen Kommentaren zu. Vermeiden Sie im Allgemeinen das Aufrufen öffentlicher Methoden von öffentlichen Methoden in Ihrer Klasse. Erstellen Sie eine private Methode und rufen Sie sie von beiden öffentlichen Methoden aus auf.
Johan Franzén
4
setUserVisibleHint veraltet!
Hamid Reza
70

Hier ist eine andere Möglichkeit onPageChangeListener:

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager);
  FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager);
  pager.setAdapter(adapter);
  pager.setOnPageChangeListener(new OnPageChangeListener() {

  public void onPageSelected(int pageNumber) {
    // Just define a callback method in your fragment and call it like this! 
    adapter.getItem(pageNumber).imVisible();

  }

  public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub

  }

  public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub

  }
});
Ostkontentitan
quelle
1
Diese Lösung funktioniert gut, danke. Dies sollte die empfohlene Lösung für diejenigen sein, die für ältere Plattformen mit den (v4) Fragment-Support-Paketen bauen
Frank Yin
7
Es ist erwähnenswert, dass dies nicht das erwartete Ergebnis liefern kann, wenn Sie einen FragmentStatePagerAdapter verwenden und eine neue Fragmentinstanz auf getItem () instanziieren, da Sie nur den Status für das neue Fragment festlegen und nicht den, den der Viewpager erhalten hat
Ben Pearson
1
Diese Lösung ist gut, wenn Sie etwas ausführen möchten, wenn das Fragment sichtbar wird. Der sichtbare Zustand eines Fragments bleibt nicht erhalten (dh es weiß nicht, wann das Fragment unsichtbar ist).
AsafK
Ich bin die gleiche Art von Problem. Bitte helfen Sie mir, das Problem zu lösen. Mein Beitrag: stackoverflow.com/questions/23115283/…
Jeeten Parmar
Dies gibt eine Nullzeiger-Ausnahme für den Adapter, den ich in der iamVisible-Methode des Fragments verwende. Ich versuche, vom Netzwerk empfangene Daten nur dann festzulegen, wenn das Fragment sichtbar ist.
zaphod100.10
58

setUserVisibleHint()wird manchmal vorher onCreateView() und manchmal danach angerufen, was zu Problemen führt.

Um dies zu überwinden, müssen Sie isResumed()auch die setUserVisibleHint()Methode überprüfen . Aber in diesem Fall wurde mir klar setUserVisibleHint(), dass er nur aufgerufen wird , wenn das Fragment wieder aufgenommen und sichtbar ist, NICHT wenn es erstellt wurde.

Wenn Sie also etwas aktualisieren möchten, während Fragment ist visible, setzen Sie Ihre Aktualisierungsfunktion sowohl in onCreate()als auch in setUserVisibleHint():

@Override
public View onCreateView(...){
    ...
    myUIUpdate();
    ...        
}
  ....
@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){
        myUIUpdate();
    }
}

UPDATE: Trotzdem myUIUpdate()wurde mir klar , dass manchmal zweimal aufgerufen wird. Der Grund ist, wenn Sie 3 Registerkarten haben und dieser Code sich auf der zweiten Registerkarte befindet. Wenn Sie die erste Registerkarte zum ersten Mal öffnen, wird auch die zweite Registerkarte erstellt, auch wenn sie nicht sichtbar ist und myUIUpdate()aufgerufen wird. Wenn Sie dann zur zweiten Registerkarte wischen, wird myUIUpdate()from if (visible && isResumed())aufgerufen und wird daher myUIUpdate()möglicherweise zweimal in einer Sekunde aufgerufen.

Das andere Problem besteht !visibledarin setUserVisibleHint, sowohl 1) beim Verlassen des Fragmentbildschirms als auch 2) vor dem Erstellen aufgerufen zu werden, wenn Sie zum ersten Mal zum Fragmentbildschirm wechseln.

Lösung:

private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...

@Override
public View onCreateView(...){
    ...
    //Initialize variables
    if (!fragmentResume && fragmentVisible){   //only when first time fragment is created
        myUIUpdate();
    }
    ...        
}

@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){   // only at fragment screen is resumed
        fragmentResume=true;
        fragmentVisible=false;
        fragmentOnCreated=true;
        myUIUpdate();
    }else  if (visible){        // only at fragment onCreated
        fragmentResume=false;
        fragmentVisible=true;
        fragmentOnCreated=true;
    }
    else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
        fragmentVisible=false;
        fragmentResume=false;
    }
}

Erläuterung:

fragmentResume, fragmentVisible: Stellt sicher , dass myUIUpdate()in onCreateView()nur aufgerufen wird, wenn ein Fragment erstellt und sichtbar ist, nicht beim Fortsetzen. Es löst auch das Problem, wenn Sie sich auf der ersten Registerkarte befinden. Die zweite Registerkarte wird erstellt, auch wenn sie nicht sichtbar ist. Dies löst das und prüft, ob der Fragmentbildschirm wann sichtbar ist onCreate.

fragmentOnCreated: Stellt sicher, dass das Fragment nicht sichtbar ist und beim ersten Erstellen des Fragments nicht aufgerufen wird. Diese if-Klausel wird jetzt nur aufgerufen, wenn Sie aus dem Fragment wischen.

Aktualisieren Sie setzen können alle diesen Code in BaseFragmentCode wie folgt aus und überschreiben Methode.

Jemshit Iskenderov
quelle
1
Ihre Lösung funktioniert einwandfrei, außer in einem Szenario, in dem das Fragment normal geöffnet wird und Sie dann auf eine Schaltfläche klicken, um es durch ein anderes Fragment zu ersetzen, und dann auf Zurück klicken, um das neue Fragment aus dem Backstack zu entfernen. In diesem Fall setUserVisibleHintwurde kein Aufruf abgerufen . ! und innerhalb der onCreateViewMethode fragmentVisiblewar das false!? so zeigte sich das Fragment leer ..! irgendwelche Gedanken.
Alaa AbuZarifa
28

Um festzustellen , Fragmentin ViewPagersichtbar, ich bin ziemlich sicher , dass nur mit setUserVisibleHint nicht genug.
Hier ist meine Lösung, um zu überprüfen, ob ein Fragment sichtbar oder unsichtbar ist. Wechseln Sie beim Starten von Viewpager zunächst zwischen den Seiten und wechseln Sie zu einer anderen Aktivität / Fragment / Hintergrund / Vordergrund`

public class BaseFragmentHelpLoadDataWhenVisible extends Fragment {
    protected boolean mIsVisibleToUser; // you can see this variable may absolutely <=> getUserVisibleHint() but it not. Currently, after many test I find that

    /**
     * This method will be called when viewpager creates fragment and when we go to this fragment background or another activity or fragment
     * NOT called when we switch between each page in ViewPager
     */
    @Override
    public void onStart() {
        super.onStart();
        if (mIsVisibleToUser) {
            onVisible();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mIsVisibleToUser) {
            onInVisible();
        }
    }

    /**
     * This method will called at first time viewpager created and when we switch between each page
     * NOT called when we go to background or another activity (fragment) when we go back
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        mIsVisibleToUser = isVisibleToUser;
        if (isResumed()) { // fragment have created
            if (mIsVisibleToUser) {
                onVisible();
            } else {
                onInVisible();
            }
        }
    }

    public void onVisible() {
        Toast.makeText(getActivity(), TAG + "visible", Toast.LENGTH_SHORT).show();
    }

    public void onInVisible() {
        Toast.makeText(getActivity(), TAG + "invisible", Toast.LENGTH_SHORT).show();
    }
}

ERLÄUTERUNG Sie können den Logcat unten sorgfältig prüfen, dann wissen Sie vielleicht, warum diese Lösung funktioniert

Erster Start

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment3: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment1: setUserVisibleHint: isVisibleToUser=true isResumed=false // AT THIS TIME isVisibleToUser=true but fragment still not created. If you do something with View here, you will receive exception
Fragment1: onCreateView
Fragment1: onStart mIsVisibleToUser=true
Fragment2: onCreateView
Fragment3: onCreateView
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=false

Gehen Sie zu Seite 2

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment2: setUserVisibleHint: isVisibleToUser=true isResumed=true

Gehen Sie zu Seite 3

Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment3: setUserVisibleHint: isVisibleToUser=true isResumed=true

Zum Hintergrund gehen:

Fragment1: onStop mIsVisibleToUser=false
Fragment2: onStop mIsVisibleToUser=false
Fragment3: onStop mIsVisibleToUser=true

Gehe in den Vordergrund

Fragment1: onStart mIsVisibleToUser=false
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=true

DEMO-Projekt hier

Hoffe es hilft

Phan Van Linh
quelle
1
Es funktioniert nicht, wenn ein Fragment einen anderen Ansichtspager hat, der ebenfalls aus einem Fragment besteht.
Shihab Uddin
Entschuldigung, ich kann die Antwort derzeit nicht posten, da ich nicht genug Zeit habe. Wenn möglich, verweisen Sie bitte meinen Git hier auf Ihr Problem. github.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master . SubChildContainerFragmentwird zum Erkennen verwendet a fragment has another view pager which also consists fragment. Sie können SubChildContainerFragmentund ChildContainerFragmentzu 1 Klasse mischen . Hoffe es hilft. Ich werde die vollständige Antwort später veröffentlichen
Phan Van Linh
26
package com.example.com.ui.fragment;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.com.R;

public class SubscribeFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_subscribe, container, false);
        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            // called here
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}
w3hacker
quelle
2
Dies sollte die akzeptierte Antwort sein. Funktioniert auch mit android.app.Fragment, was ein riesiger Bonus ist.
RajV
setUserVisibleHintVeraltet
ekashking
23

In ViewPager2und ViewPagerab Version können androidx.fragment:fragment:1.1.0Sie einfach onPauseund onResumeRückrufe verwenden, um zu bestimmen, welches Fragment derzeit für den Benutzer sichtbar ist. onResumeRückruf wird aufgerufen, wenn das Fragment sichtbar wurde und onPausewenn es nicht mehr sichtbar ist.

Im Fall von ViewPager2 ist dies das Standardverhalten, aber das gleiche Verhalten kann für alte Güter ViewPagerleicht aktiviert werden.

Um dieses Verhalten im ersten ViewPager zu aktivieren, müssen Sie den FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTParameter als zweites Argument des FragmentPagerAdapterKonstruktors übergeben.

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

Hinweis: setUserVisibleHint()Methode und FragmentPagerAdapterKonstruktor mit einem Parameter sind in der neuen Version von Fragment von Android Jetpack jetzt veraltet.

lukjar
quelle
1
Danke für die schöne Lösung. Ich habe lange danach gesucht. Großartige Arbeit
Anurag Srivastava
1
Für ViewPager2 ist die Option BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT nicht das Standardverhalten. Sie müssen sie manuell festlegen. Beim Einstellen dieser Option hat Ihre Lösung für mich funktioniert. Vielen Dank.
Am
1
Ab APR 2020 ist dies die aktualisierte Lösung und funktioniert wie ein Zauber.
Prakash
1
@ 4ntoine Bitte akzeptieren Sie diese Antwort als die richtige Antwort, da dies ab sofort korrekt ist und die meisten Antworten die veraltete setUserVisibleHint-Methode verwenden
Jorn Rigter
16

setPrimaryItem()In der FragmentPagerAdapterUnterklasse überschreiben . Ich benutze diese Methode und sie funktioniert gut.

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    // This is what calls setMenuVisibility() on the fragments
    super.setPrimaryItem(container, position, object);

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;
        fragment.doTheThingYouNeedToDoOnBecomingVisible();
    }
}
kris larson
quelle
1
Dies funktionierte fast, hatte aber Probleme mit NPE und wurde mehrmals aufgerufen. Habe unten eine Alternative gepostet!
Gober
@Gober Speichern des Objekts beim ersten Mal und Überprüfen und Ignorieren des Folgeaufrufs mit demselben Objektobjekt wie FragmentStateAdapter in der setPrimaryItem () -Methode
Wanglugao
12

Überschreiben Sie Fragment.onHiddenChanged()dafür.

public void onHiddenChanged(boolean hidden)

Wird aufgerufen, wenn sich der verborgene Status (wie von zurückgegeben isHidden()) des Fragments geändert hat. Fragmente beginnen nicht versteckt; Dies wird immer dann aufgerufen, wenn das Fragment seinen Status ändert.

Parameter
hidden- boolean: True, wenn das Fragment jetzt ausgeblendet ist, false, wenn es nicht sichtbar ist.

Dan
quelle
3
Diese Methode ist mittlerweile veraltet und kann nicht mehr verwendet werden
Bluewhile
4
@bluewhile wo siehst du es ist veraltet? developer.android.com/reference/android/app/…
Cel
1
Ich habe dies gerade mit Erfolg verwendet, und die Dokumentation scheint nicht darauf hinzudeuten, dass es veraltet ist.
Trevor
1
@bluewhile, in 4.1 (API 16) ist es noch nicht veraltet.
Dan
8
Ich verwende API Level 19 und kann bestätigen, dass dies zwar nicht veraltet ist, aber nicht wie angekündigt funktioniert. onHiddenChanged wird nicht aufgerufen, wenn das Fragment beispielsweise von einem anderen Fragment ausgeblendet wird.
4

Ich habe das herausgefunden onCreateOptionsMenuund onPrepareOptionsMenuMethoden nur im Fall des wirklich sichtbaren Fragments aufgerufen . Ich konnte keine Methode finden, die sich so verhält. Ich habe OnPageChangeListeneres auch versucht, aber es hat in den Situationen nicht funktioniert. Ich benötige beispielsweise eine in der onCreateMethode initialisierte Variable .

Diese beiden Methoden können daher für dieses Problem als Problemumgehung verwendet werden, insbesondere für kleine und kurze Jobs.

Ich denke, das ist die bessere Lösung, aber nicht die beste. Ich werde dies verwenden, aber gleichzeitig auf eine bessere Lösung warten.

Grüße.

ismailarilik
quelle
2

Eine andere hier veröffentlichte Lösung, die setPrimaryItem im Pageradapter von kris larson überschreibt, hat fast für mich funktioniert. Diese Methode wird jedoch für jedes Setup mehrmals aufgerufen. Außerdem habe ich NPE aus Ansichten usw. im Fragment erhalten, da dies beim ersten Aufruf dieser Methode noch nicht fertig ist. Mit den folgenden Änderungen hat dies bei mir funktioniert:

private int mCurrentPosition = -1;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    super.setPrimaryItem(container, position, object);

    if (position == mCurrentPosition) {
        return;
    }

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;

        if (fragment.isResumed()) {
            mCurrentPosition = position;
            fragment.doTheThingYouNeedToDoOnBecomingVisible();
        }
    }
}
Gober
quelle
2

Fügen Sie den folgenden Code in das Fragment ein

@Override
public void setMenuVisibility(final boolean visible) 
 {
    super.setMenuVisibility(visible);
    if (visible && isResumed()) 
     {

     }
}
Afzaal Iftikhar
quelle
Dies funktioniert nur für ViewPagerFragments. Nicht für Fragmente in normaler Aktivität.
AndroidGuy
2

Ich habe das gleiche Problem beim Arbeiten mit FragmentStatePagerAdaptersund 3 Registerkarten festgestellt. Ich musste einen Dilaog anzeigen, wenn auf die erste Registerkarte geklickt wurde, und ihn beim Klicken auf andere Registerkarten ausblenden.

Das Überschreiben setUserVisibleHint()allein half nicht, das aktuell sichtbare Fragment zu finden.

Beim Klicken von der 3. Registerkarte -----> 1. Registerkarte. Es wurde zweimal für das 2. Fragment und für das 1. Fragment ausgelöst. Ich habe es mit der Methode isResumed () kombiniert.

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    isVisible = isVisibleToUser;

    // Make sure that fragment is currently visible
    if (!isVisible && isResumed()) {
        // Call code when Fragment not visible
    } else if (isVisible && isResumed()) {
       // Call code when Fragment becomes visible.
    }

}
Aman Arora
quelle
2

Wir haben einen Sonderfall mit MVP, in dem das Fragment den Präsentator benachrichtigen muss, dass die Ansicht sichtbar geworden ist, und der Präsentator von Dagger in injiziert wird fragment.onAttach().

setUserVisibleHint()ist nicht genug, wir haben 3 verschiedene Fälle entdeckt, die behoben werden mussten ( onAttach()wird erwähnt, damit Sie wissen, wann der Moderator verfügbar ist):

  1. Das Fragment wurde gerade erstellt. Das System führt folgende Aufrufe durch:

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. Fragment bereits erstellt und Home-Taste gedrückt. Wenn Sie die App im Vordergrund wiederherstellen, wird dies wie folgt aufgerufen:

    onResume()
  3. Orientierungsänderung:

    onAttach() // presenter available
    onResume()
    setUserVisibleHint()

Wir möchten, dass der Sichtbarkeitshinweis nur einmal beim Präsentator eingeht. So machen wir das:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_list, container, false);
    setHasOptionsMenu(true);

    if (savedInstanceState != null) {
        lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION,
              getResources().getConfiguration().orientation);
    } else {
        lastOrientation = getResources().getConfiguration().orientation;
    }

    return root;
}

@Override
public void onResume() {
    super.onResume();
    presenter.onResume();

    int orientation = getResources().getConfiguration().orientation;
    if (orientation == lastOrientation) {
        if (getUserVisibleHint()) {
            presenter.onViewBecomesVisible();
        }
    }
    lastOrientation = orientation;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (presenter != null && isResumed() && isVisibleToUser) {
        presenter.onViewBecomesVisible();
    }
}

@Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_LAST_ORIENTATION, lastOrientation);
}
Stift
quelle
2

Erkennen durch focused view!

Das funktioniert bei mir

public static boolean isFragmentVisible(Fragment fragment) {
    Activity activity = fragment.getActivity();
    View focusedView = fragment.getView().findFocus();
    return activity != null
            && focusedView != null
            && focusedView == activity.getWindow().getDecorView().findFocus();
}
Mohammad Reza Norouzi
quelle
1

Dieses Problem trat auf, als ich versuchte, einen Timer zum Auslösen zu bringen, wenn das Fragment im Viewpager auf dem Bildschirm angezeigt wurde, damit der Benutzer es sehen konnte.

Der Timer wurde immer gestartet, bevor das Fragment vom Benutzer gesehen wurde. Dies liegt daran, dass die onResume()Methode im Fragment aufgerufen wird, bevor wir das Fragment sehen können.

Meine Lösung bestand darin, die onResume()Methode zu überprüfen . Ich wollte eine bestimmte Methode 'foo ()' aufrufen, wenn Fragment 8 das aktuelle Fragment des View Pagers war.

@Override
public void onResume() {
    super.onResume();
    if(viewPager.getCurrentItem() == 8){
        foo();
        //Your code here. Executed when fragment is seen by user.
    }
}

Hoffe das hilft. Ich habe dieses Problem oft gesehen. Dies scheint die einfachste Lösung zu sein, die ich je gesehen habe. Viele andere sind nicht mit niedrigeren APIs usw. kompatibel.

Malone
quelle
1

Ich hatte das gleiche Problem. ViewPagerführt andere Fragmentlebenszyklusereignisse aus und ich konnte dieses Verhalten nicht ändern. Ich habe einen einfachen Pager mit Fragmenten und verfügbaren Animationen geschrieben. SimplePager

Rukmal Dias
quelle
1

Ich habe das benutzt und es hat funktioniert!

mContext.getWindow().getDecorView().isShown() //boolean
Ali Akram
quelle
1

Ich unterstütze SectionsPagerAdapter mit untergeordneten Fragmenten, so dass ich nach vielen Kopfschmerzen endlich eine Arbeitsversion bekam, die auf Lösungen aus diesem Thema basiert:

public abstract class BaseFragment extends Fragment {

    private boolean visible;
    private boolean visibilityHintChanged;

    /**
     * Called when the visibility of the fragment changed
     */
    protected void onVisibilityChanged(View view, boolean visible) {

    }

    private void triggerVisibilityChangedIfNeeded(boolean visible) {
        if (this.visible == visible || getActivity() == null || getView() == null) {
            return;
        }
        this.visible = visible;
        onVisibilityChanged(getView(), visible);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!visibilityHintChanged) {
            setUserVisibleHint(false);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        triggerVisibilityChangedIfNeeded(!hidden);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        visibilityHintChanged = true;
        if (isVisibleToUser && isResumed() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        } else if (!isVisibleToUser) {
            triggerVisibilityChangedIfNeeded(false);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        triggerVisibilityChangedIfNeeded(false);
    }

    @Override
    public void onStop() {
        super.onStop();
        triggerVisibilityChangedIfNeeded(false);
    }

    protected boolean isReallyVisible() {
        return visible;
    }
}
Andoctorey
quelle
Ich habe vergessen zu erwähnen, dass nach dem Hinzufügen eines untergeordneten Fragments zum übergeordneten Fragment set fragment.setUserVisibleHint (true);
Andoctorey
Und vergessen Sie nicht, getChildFragmentManager () anstelle von getFragmentManager () zum Hinzufügen untergeordneter Fragmente zu verwenden.
Andoctorey
0

Beachten Sie, dass setUserVisibleHint(false)dies bei Aktivität / Fragmentstopp nicht aufgerufen wird. Sie müssen noch Start / Stopp überprüfen, um register/unregisteralle Listener / etc.

Außerdem erhalten Sie, setUserVisibleHint(false)wenn Ihr Fragment in einem nicht sichtbaren Zustand beginnt. Sie möchten unregisterdort nicht, da Sie sich in diesem Fall noch nie registriert haben.

@Override
public void onStart() {
    super.onStart();

    if (getUserVisibleHint()) {
        // register
    }
}

@Override
public void onStop() {
    if (getUserVisibleHint()) {
        // unregister
    }

    super.onStop();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser && isResumed()) {
        // register

        if (!mHasBeenVisible) {
            mHasBeenVisible = true;
        }
    } else if (mHasBeenVisible){
        // unregister
    }
}
elffünf
quelle
0

Eine einfache Methode zur Implementierung besteht darin, zu überprüfen, ob der Benutzer angemeldet ist, bevor Sie zum Fragment wechseln.

In Ihrer MainActivity können Sie so etwas in der onNavigationItemSelected- Methode ausführen .

 case R.id.nav_profile_side:


                if (User_is_logged_in) {

                    fragmentManager.beginTransaction()
                            .replace(R.id.content_frame
                                    , new FragmentProfile())
                            .commit();
                }else {

                    ShowLoginOrRegisterDialog(fragmentManager);

                }

                break;

Wenn Sie jedoch eine Navigationsleiste verwenden, wurde die Auswahl in der Schublade in Profil geändert, obwohl wir nicht zum Profilformat gegangen sind.

Um die Auswahl auf die aktuelle Auswahl zurückzusetzen, führen Sie den folgenden Code aus

        navigationView.getMenu().getItem(0).setChecked(true);
Martin Mbae
quelle
-4

Ich habe die Count-Methode des zugeordneten FragmentStatePagerAdapter überschrieben und die Gesamtanzahl abzüglich der Anzahl der auszublendenden Seiten zurückgegeben:

 public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
 {   
     private List<Fragment> _fragments;

     public int TrimmedPages { get; set; }

     public MyAdapter(Android.App.FragmentManager fm) : base(fm) { }

     public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm)
     {
         _fragments = fragments;

         TrimmedPages = 0;
     }

     public override int Count
     {
         //get { return _fragments.Count; }
         get { return _fragments.Count - TrimmedPages; }
     }
 }

Wenn dem ViewPager zunächst 3 Fragmente hinzugefügt wurden und nur die ersten 2 angezeigt werden sollen, bis eine bestimmte Bedingung erfüllt ist, überschreiben Sie die Seitenzahl, indem Sie TrimmedPages auf 1 setzen, und es sollten nur die ersten beiden Seiten angezeigt werden.

Dies funktioniert gut für Seiten am Ende, hilft aber nicht wirklich für Seiten am Anfang oder in der Mitte (obwohl es viele Möglichkeiten gibt, dies zu tun).

Sam ist
quelle