ViewPager'de Parçanın ne zaman görüneceğini belirleme


754

Sorun: Fragment onResume()içinde ViewPagerparçası aslında görünür hale gelmeden önce ateşlenir.

Örneğin, ViewPagerve ile 2 parça var FragmentPagerAdapter. İkinci parça yalnızca yetkili kullanıcılar tarafından kullanılabilir ve parçanın görünür hale geldiğinde kullanıcıdan oturum açmasını istemem gerekir (bir uyarı iletişim kutusu kullanarak).

AMA ViewPagerikinci parçayı önbelleğe almak için birincisi göründüğünde ikinci parçayı oluşturur ve kullanıcı kaydırmaya başladığında görünmesini sağlar.

Böylece onResume()olay, görünür hale gelmeden çok önce ikinci parçada tetiklenir. Bu yüzden, ikinci parça uygun anda bir diyalog göstermek için görünür hale geldiğinde patlayan bir olay bulmaya çalışıyorum.

Bu nasıl yapılabilir?


18
"ViewPager ve FragmentPagerAdapter ile 2 parça var. İkinci parça sadece yetkili kullanıcılar için kullanılabilir ve parça görünür hale geldiğinde (uyarı iletişim kutusu) oturum açmak için kullanım sormak gerekir." - IMHO, bu korkunç UX. Kullanıcı yatay olarak kaydırdığı için bir iletişim kutusu açmak Play Store'da size bir yıldızlı puan vermeme neden olur.
CommonsWare

"Oturum Aç" düğmesiyle TextView'da bilgi görüntülemek daha mı iyidir? Bu dava için çözümünüz nedir?
4ntoine

5
"Görüntülenmeyecekse veri yüklemeye gerek yok." - o zaman a koymamalısınız ViewPager. İki sayfalık bir çağrı cihazında, ister beğensin ister beğenmesin, her iki sayfa da hemen yüklenir. Kullanıcı deneyiminin ViewPageriçeriğin, kaydırıldıktan hemen sonra orada olması, bir süre sonra olmaması gerekiyordu. Bu nedenle ViewPager, kullanıcı deneyiminin sağlanmasına yardımcı olmak için görünür olandan önce bir sayfa başlatır.
CommonsWare

2
ViewPager yeterince esnek değil ve minimum setOffscreenPageLimit 1 olduğundan önbelleği kapatmaya izin vermiyor gibi görünüyor: stackoverflow.com/questions/10073214/… . Bunun için herhangi bir neden görmeyin ve beklenen davranış (zorunlu önbellekleme durumunda), parça görünür hale geldiğinde parça AMA yangın parçasının onResume () parçasını oluşturmaktır.
4ntoine

1
Biraz geç, ancak aynı sorunla karşı karşıya olan herkes için , bu sorunla ilgilenen ve birkaç ekstra özellik sağlayan FragmentViewPager kütüphanesini (yazarım) deneyebilirsiniz . Bir örnek için projenin GitHub sayfasını veya bu yığın akışı yanıtını kontrol edin .
S. Brukhanda

Yanıtlar:


582

ViewPager'de Parçanın ne zaman görüneceğini belirleme

Sen geçersiz kılarak aşağıdakileri yapabilirsiniz setUserVisibleHintGözlerinde farklı Fragment:

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}

7
Bugünün Android Destek Kitaplığı güncellemesi (rev 11) sayesinde, kullanıcının görünür ipucu sorunu sonunda düzeltildi. ViewPager için kullanıcının görünür ipucunu kullanmak artık güvenlidir.
Oasis Feng

58
SetUserVisibleHint yöntemi onCreateView çağrılmadan ÖNCE çağrıldığını buldum ve bu herhangi bir başlatma izlemeyi zorlaştırıyor.
AndroidDev

11
@AndroidDev İpucu doğru olarak geldiğinde bazı kodları çalıştırmak istiyorsanız, ancak görünüm ağacının zaten başlatılmasını gerektiriyorsa, isResumed()bir NPE'yi önlemek için bu kod bloğunu sarmanız yeterlidir. Benim için iyi çalışıyor.
Ravi Thapliyal

13
SDK'nın varsayılan olarak sağlaması gereken bir şey için çok fazla farklı saldırı olması çok saçma.
Mike6679

15
setUserVisibleHintşimdi kullanımdan kaldırıldı
SR

525

GÜNCELLEME : Android Destek Kitaplığı (rev 11) nihayet kullanıcı görünür ipucu sorununu düzeltti , şimdi parçalar için destek kitaplığı kullanıyorsanız , gorn'un cevabında açıklandığı gibi değişiklikleri yakalamak için güvenle kullanabilir getUserVisibleHint()veya geçersiz kılabilirsiniz setUserVisibleHint().

GÜNCELLEME 1 İşte küçük bir sorun getUserVisibleHint(). Bu değer varsayılan değerdir true.

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

Bu yüzden daha önce setUserVisibleHint()çağrıldı kullanmaya çalıştığınızda bir sorun olabilir . Geçici bir çözüm olarak, böyle bir onCreateyöntemde değer ayarlayabilirsiniz .

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

Güncel olmayan cevap:

Kullanılması durumda, ViewPagersadece bir kerede bir sayfa gösteriliyor ancak kullanıyorsanız önceden önbelleğe fragmanları da (aslında görünmeyen) "görünür" durumuna konulur FragmentStatePagerAdapteriçinde Android Support Library pre-r11.

Geçersiz kıldım:

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

ViewPager'de yalnızca bir parça aslında menü öğelerini üst etkinliğin öğeleriyle birlikte yerleştirebildiğinden, "görünürlük" ün en uygun durumu olduğunu düşündüğüm parçanın odak durumunu yakalamak için.


73
GetActivity () işlevini kullanmaya dikkat edin, ilk başlangıçta boş olacaktır.
vovkab

10
SetUserVisibleHint () işlevinin FragmentPagerAdapter öğesinde her zaman düzgün çalıştığını unutmayın.
louielouie

8
Bu çözüm (ve ayrıca gorn'lar) biraz gecikiyor. Vizörün hızlı ve birden fazla kez kaydırıldığı durumlarda, bu yöntem bir gecikmeyle çağrılır (parça artık görünür / görünmez olduğunda).
AsafK

3
Ben çağrıldığında truegetUserVisibleHint () alıyorum onCreateOptionsMenuve setUserVisibleHintçağrıldığında, menü henüz oluşturulmamış gibi görünüyor. Sonuçta sadece menüden görünen parçayı istediğimde iki seçenek eklendi. Bu konuda herhangi bir öneriniz var mı?
cYrixmorten

13
setUserVisibleHintşimdi kullanımdan kaldırıldı
SR

143

Bu onResume()beklediğiniz normal davranışı geri yüklüyor gibi görünüyor . Uygulamadan çıkmak için ana ekran tuşuna basarak ve daha sonra uygulamaya tekrar girerek iyi oynar. onResume()arka arkaya iki kez çağrılmaz.

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

4
Tam aradığım şey. Çözer sorunu setUserVisibleHintönce çağrılan onCreateViewve o setUserVisibleHintuygulama arka planı gider ve daha sonra ön plan eğer adlı değil. Müthiş! Teşekkür ederim!
Alex

11
Bence kendini çağırmak oldukça kötü bir fikir, ama aksi takdirde, bu yazının sorusunu cevaplamak için ihtiyacınız olan her şey setUserVisibleHint işlevinde!
Quentin

4
Daha doğrusu bir düz uygulamak istiyorum onVisibleToUser()yöntem ve o çağrıda onResume()ve gelen setUserVisibleHint(boolean)aramak yerine onResume()kendim ve yaşam döngüsü geri aramaları müdahale. Aksi takdirde bu yaklaşımın iyi işlediğini düşünüyorum, teşekkürler!
Stephan Henningsen

1
Evet, yukarıdaki yorumlara katılıyorum. Genel olarak, genel yöntemleri sınıfınızdaki genel yöntemlerden çağırmaktan kaçının. Özel bir yöntem oluşturun ve her iki genel yöntemden de çağırın.
Johan Franzén

4
setUserVisibleHint kullanımdan kaldırıldı!
Hamid Reza

70

İşte kullanarak başka bir yol 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

  }
});

1
Bu çözüm iyi çalışıyor, teşekkürler. Bu, (v4) parça destek paketlerini kullanan eski platformlar için inşa edilenler için önerilen çözüm olmalıdır
Frank Yin

7
FragmentStatePagerAdapter kullanıyorsanız ve getItem () üzerinde yeni bir Fragment örneği başlatırsanız bunun beklenen sonucu veremeyeceğini belirtmek gerekir, çünkü durumu yeni görüntüleyicide alırsınız;
Ben Pearson

1
Parça görünür hale geldiğinde bir şey yapmak istiyorsanız bu çözüm iyidir. Bir parçanın görünür durumunu korumaz (yani parçanın ne zaman görünmez olduğunu bilmez).
AsafK

Ben aynı tür bir problemim. lütfen sorunu çözmeme yardım et. Benim
mesajım

Bu, parçanın iamVisible yönteminde kullandığım bağdaştırıcı için boş işaretçi özel durumu veriyor. Sadece parça görünür olduğunda ağdan alınan verileri ayarlamaya çalışıyorum.
zaphod100.10

58

setUserVisibleHint()bazen önce onCreateView() ve bazen sonra sorun olur.

Bunun üstesinden gelmek için isResumed()setUserVisibleHint()yöntemi de kontrol etmeniz gerekir . Ancak bu durumda setUserVisibleHint(), sadece Parçanın devam ettirildiği ve görülebildiği durumlarda yaratıldığını farkettim , yaratıldığında DEĞİL.

Eğer Fragment olan güncelleme şey istiyorsanız visible, hem sizin güncelleme fonksiyonunu koydu onCreate()ve setUserVisibleHint():

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

GÜNCELLEME: Hala farkettim ki myUIUpdate()bazen iki kez çağrılır, nedeni, 3 sekmeniz varsa ve bu kod 2. sekmede ise, ilk sekmeyi açtığınızda, 2. sekme de görünmez ve myUIUpdate()çağrıldığında bile oluşturulur . Eğer 2 sekmesine tokatlamak Ardından, myUIUpdate()gelen if (visible && isResumed())denir ve bunun sonucu olarak, myUIUpdate()bir saniye içinde iki kez adlı olabilir.

Diğer sorun ise !visiblede setUserVisibleHintçağrılan hem 1) parçası ekran ilk kez geçtiğinizde, oluşturulmadan önce parça ekrana ve 2), dışarı giderken.

Çözüm:

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

Açıklama:

fragmentResume, fragmentVisible: Emin yapar myUIUpdate()içinde onCreateView()parçası değil özgeçmiş, oluşturulan ve görünür olduğunda çağrılır. Ayrıca 1. sekmede olduğunuzda da sorunu çözer, görünür olmasa bile 2. sekme oluşturulur. Bu, bunu çözer ve parça ekranının ne zaman görünür olup olmadığını kontrol eder onCreate.

fragmentOnCreated: Parçanın görünür olmadığından ve ilk kez parça oluşturduğunuzda çağrılmadığından emin olur. Şimdi bu madde sadece parçayı hızlıca kaydırdığınızda çağrılırsa.

Güncelle Tüm bu BaseFragmentkodu böyle bir koda koyabilir ve yöntemi geçersiz kılabilirsiniz.


1
Çözümünüz, bir senaryo dışında mükemmel bir şekilde çalışır, parça normal olarak açıldığında ve daha sonra başka bir parçayla değiştirmek için bazı düğmelere tıklarsınız ve ardından yeni parçayı backstack'ten açmak için geri tıklarsanız, bu durumda setUserVisibleHintçağrı almadı .. ! ve iç onCreateViewyöntem fragmentVisibleoldu false!? böylece parça boş çıktı ..! Düşüncesi olan var mı.?
Alaa AbuZarifa

28

Tespit etmek için Fragmentde ViewPagergörünür, ben çok emin değilim sadece kullanılarak setUserVisibleHint yeterli değildir.
İşte bir parçanın görünür veya görünmez olup olmadığını kontrol etmek için çözümüm. İlk önce viewpager'ı başlatırken sayfa arasında geçiş yapın, başka bir etkinliğe / parçaya / arka plan / ön plana gidin`

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

AÇIKLAMA Aşağıdaki logcat'i dikkatlice kontrol edebilirsiniz, sonra bu çözümün neden işe yarayacağını bildiğinizi düşünüyorum

İlk lansman

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

2. sayfaya git

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

Sayfaya git3

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

Arka plana git:

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

Ön plana git

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

DEMO projesi burada

Umarım yardımcı olur


1
Bir parçanın, parçayı da içeren başka bir görüntüleme çağrı cihazına sahip olması durumunda çalışmaz.
Shihab Uddin

üzgünüm, ben şu anda cevap gönderemiyorum çünkü yeterli zamanım yok, mümkünse lütfen sorunumla ilgili buraya git github.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master . SubChildContainerFragmentalgılamak için kullanılır a fragment has another view pager which also consists fragment. Sen karıştırıp SubChildContainerFragmentve ChildContainerFragment1 sınıfa. Umarım yardımcı olur. Tam cevabı daha sonra göndereceğim
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);
    }
}

2
Bu kabul edilen cevap olmalı. Ayrıca büyük bir bonus olan android.app.Fragment ile çalışır.
RajV

setUserVisibleHintKullanımdan kaldırıldı
ekashking

23

Sürümde ViewPager2ve ViewPagersürümden , kullanıcı için hangi parçanın görünür olduğunu belirlemek androidx.fragment:fragment:1.1.0için onPauseve onResumegeri çağrıları kullanabilirsiniz . onResumefragman görünür onPauseolduğunda ve görünür hale gelmediğinde geri arama yapılır .

ViewPager2 durumunda bu varsayılan davranıştır, ancak aynı davranış eski mal için ViewPagerkolayca etkinleştirilebilir .

İlk ViewPager'de bu davranışı etkinleştirmek için yapıcıyı FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTikinci bağımsız değişken olarak parametre geçirmeniz gerekir FragmentPagerAdapter.

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

Not: Bir parametreli setUserVisibleHint()yöntem ve FragmentPagerAdapteryapıcı artık android jetpack'ten Fragment'ın yeni sürümünde kullanımdan kaldırılmıştır.


1
Güzel çözüm için teşekkürler. Bunu uzun zamandır arıyordum. Büyük İş
Anurag Srivastava

1
ViewPager2 için BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT seçeneği varsayılan davranış değildir, manuel olarak ayarlamanız gerekir. Bu seçeneği belirlerken çözümünüz benim için çalıştı. Teşekkürler.
Drian

1
NİSAN 2020'den itibaren bu güncellenmiş bir çözümdür ve bir cazibe gibi çalışır.
Prakash

1
@ 4ntoine lütfen bu yanıtı doğru cevap olarak kabul etmeyi düşünün, çünkü şu andan itibaren bu doğru ve çoğu cevap kullanımdan kaldırılan setUserVisibleHint yöntemini kullanıyor
Jorn Rigter

16

Geçersiz Kıl setPrimaryItem()içinde FragmentPagerAdapteralt sınıf. Bu yöntemi kullanıyorum ve iyi çalışıyor.

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

1
Bu neredeyse işe yaradı, ancak NPE ve birkaç kez çağrılma ile ilgili sorunları vardı. Aşağıda bir alternatif gönderdi!
Gober


@Gober Object nesnesini ilk kez kaydetme ve aynı FragmentStateAdapter setPrimaryItem () yönteminde yaptığı gibi aynı Object nesnesiyle takip çağrısını kontrol edip yok sayın
wanglugao

12

Bunun için geçersiz kıl Fragment.onHiddenChanged().

public void onHiddenChanged(boolean hidden)

isHidden()Parçanın gizli durumu (tarafından döndürüldüğü gibi ) değiştiğinde çağrılır . Parçalar gizli değil başlar; fragman durum değiştirdiğinde bu çağrılacaktır.

Parametreler
hidden- boolean: Parça şimdi gizlenmişse true, görünür değilse false.


3
bu yöntem şu ana kadar kullanımdan kaldırılmıştır ve artık kullanılamaz
bluewhile

4
@bluewhile, kullanımdan kaldırıldığını görüyorsunuz? developer.android.com/reference/android/app/…
Cel

1
Bunu sadece başarıyla kullandım ve belgeler kullanımdan kaldırıldığını göstermiyor gibi görünüyor.
Trevor

1
@bluewhile, 4.1 (api 16) 'da henüz onaylanmamıştır.
dan

8
API düzey 19 kullanıyorum ve bu kullanımdan kaldırılmamış olsa da, reklamı olarak çalışmadığını onaylayabilirsiniz. Parça, başka bir parça tarafından gizlendiğinde onHiddenChanged çağrılmaz.

4

Bunu anladım onCreateOptionsMenuve onPrepareOptionsMenuyöntemlerin sadece parçada gerçekten görünür olması durumunda çağırdım . Ben böyle davranan herhangi bir yöntem bulamadı, ben de denedim OnPageChangeListenerama durumları için işe yaramadı, örneğin, onCreateyöntemde başlatılmış bir değişkene ihtiyacım var .

Bu nedenle, bu iki yöntem bu sorun için, özellikle küçük ve kısa işler için geçici bir çözüm olarak kullanılabilir.

Bence bu daha iyi bir çözüm ama en iyisi değil. Bunu kullanacağım ama aynı zamanda daha iyi bir çözüm bekleyeceğim.

Saygılarımızla.


2

Burada kris larson tarafından pageradapter setPrimaryItem geçersiz kılan başka bir çözüm neredeyse benim için çalıştı. Ancak bu yöntem her kurulum için birden çok kez çağrılır. Ayrıca bu yöntem denir ilk birkaç kez hazır olmadığı için parça, vb NPE var . Aşağıdaki değişikliklerle bu benim için çalıştı:

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

2

Parçanın içine aşağıdaki kodu ekleyin

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

     }
}

Bu yalnızca ViewPagerFragments için geçerlidir. Normal aktivitedeki fragmanlar için değil.
AndroidGuy

2

FragmentStatePagerAdapters3 sekme ile çalışırken aynı sorunla karşılaştım . 1. sekme tıklandığında bir Dilaog göstermek ve diğer sekmeleri tıklamak gizlemek zorunda kaldı.

setUserVisibleHint()Tek başına geçersiz kılma , mevcut görünür parçayı bulmaya yardımcı olmadı.

3. sekmeden -----> 1. sekmeden tıklarken. 2. parça ve 1. parça için iki kez tetiklendi. Ben isResumed () yöntemi ile birleştirdim.

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

}

2

MVP ile, parçanın sunum yapan kişiye görüşün görünür hale geldiğini ve sunum yapan kişinin Dagger tarafından enjekte edildiğini bildirmesi gereken özel bir vakamız var fragment.onAttach().

setUserVisibleHint()yeterli değil, ele alınması gereken 3 farklı durum tespit ettik ( onAttach()sunucunun ne zaman müsait olduğunu bilmeniz için bahsedildi):

  1. Parça yeni oluşturuldu. Sistem aşağıdaki çağrıları yapar:

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. Parça zaten oluşturuldu ve ana sayfa düğmesine basıldı. Uygulamayı ön plana geri yüklerken buna denir:

    onResume()
  3. Yön değişikliği:

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

Görünürlük ipucunun sunucuya yalnızca bir kez ulaşmasını istiyoruz, bu yüzden böyle yapıyoruz:

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

2

Tarafından tespit focused view!

Bu benim için çalışıyor

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

1

Viewpager parçasını kullanıcı görmek için ekranda iken bir zamanlayıcı ateş etmeye çalışırken bu sorunla karşılaştım.

Zamanlayıcı her zaman parça kullanıcı tarafından görülmeden hemen önce başlar. Bunun nedeni, onResume()parçayı görmeden önce parçadaki yöntemin çağrılmasıdır.

Benim çözüm onResume()yöntemde bir kontrol yapmak oldu . 8 numaralı parça mevcut parçacığı görüntülediğinde belirli bir yöntemi 'foo ()' olarak adlandırmak istedim.

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

Bu yardımcı olur umarım. Bu sorunun çok açıldığını gördüm. Bu gördüğüm en basit çözüm gibi görünüyor. Diğer birçoğu daha düşük API'lerle vb. Uyumlu değildir.


1

Aynı sorunu yaşadım. ViewPagerdiğer parça yaşam döngüsü olaylarını yürütür ve bu davranışı değiştiremedim. Parçalar ve mevcut animasyonlar kullanarak basit bir çağrı cihazı yazdım. SimplePager


1

Bunu kullandım ve işe yaradı!

mContext.getWindow().getDecorView().isShown() //boolean

1

SectionsPagerAdapter'ı çocuk parçalarıyla destekliyorum, bu nedenle baş ağrısının çoğundan sonra nihayet bu konunun çözümlerine dayanan çalışma sürümü aldım:

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

Üst parça kümesine alt parça ekledikten sonra, fragment.setUserVisibleHint (true);
Andoctorey

Alt parçalar eklemek için getFragmentManager () yerine getChildFragmentManager () yöntemini kullanmayı unutmayın.
Andoctorey

0

setUserVisibleHint(false)Etkinlik / parça durağında çağrılmadığını unutmayın . Hala düzgün başlangıç / durdurma kontrol etmek gerekir register/unregistervb herhangi dinleyici /.

Ayrıca, setUserVisibleHint(false)parçanız görünür olmayan bir durumda başlarsa elde edersiniz ; unregisterbu durumda daha önce hiç kayıt olmamış olduğunuz için orada olmak istemezsiniz .

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

0

Uygulamanın basit bir yolu , parçaya gitmeden önce kullanıcının oturum açıp açmadığını kontrol etmektir .

MainActivity'nizde onNavigationItemSelected yönteminde böyle bir şey yapabilirsiniz .

 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;

Ancak, gezinme çekmecesini kullanıyorsanız, ProfileFragment'e gitmese de çekmecedeki seçim Profil olarak değişmiş olacaktır.

Seçimi geçerli seçime sıfırlamak için aşağıdaki kodu çalıştırın

        navigationView.getMenu().getItem(0).setChecked(true);

-4

Ilişkili FragmentStatePagerAdapter Count yöntemini geçersiz kılındı ​​ve toplam sayısını eksi saklanacak sayfa sayısını döndürün:

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

Dolayısıyla, ViewPager'a başlangıçta 3 parça eklenmişse ve bir koşul karşılanıncaya kadar yalnızca ilk 2 parça gösterilecekse, TrimmedPages değerini 1 olarak ayarlayarak sayfa sayısını geçersiz kılın ve yalnızca ilk iki sayfayı göstermelidir.

Bu, sondaki sayfalar için iyi çalışır, ancak başlangıçta veya ortada olanlara gerçekten yardımcı olmaz (bunu yapmanın birçok yolu olsa da).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.