Cara menentukan kapan Fragmen menjadi terlihat di ViewPager


754

Masalah: Fragmen onResume()dalam ViewPagerdipecat sebelum fragmen menjadi benar-benar terlihat.

Sebagai contoh, saya punya 2 fragmen dengan ViewPagerdan FragmentPagerAdapter. Fragmen kedua hanya tersedia untuk pengguna yang berwenang dan saya harus meminta pengguna untuk masuk ketika fragmen menjadi terlihat (menggunakan dialog peringatan).

TETAPI ViewPagermenciptakan fragmen kedua ketika yang pertama terlihat untuk men-cache fragmen kedua dan membuatnya terlihat ketika pengguna mulai menggesek.

Jadi onResume()acara tersebut dipecat dalam fragmen kedua jauh sebelum terlihat. Itu sebabnya saya mencoba untuk menemukan suatu peristiwa yang menyala ketika fragmen kedua menjadi terlihat untuk menampilkan dialog pada saat yang tepat.

Bagaimana ini bisa dilakukan?


18
"Saya memiliki 2 fragmen dengan ViewPager dan FragmentPagerAdapter. Fragmen kedua hanya tersedia untuk pengguna yang berwenang dan saya harus meminta penggunaan untuk masuk ketika fragmen tersebut terlihat (dialog peringatan)." - IMHO, itu UX yang mengerikan. Memunculkan dialog karena pengguna menggesek secara horizontal akan menyebabkan saya memberi Anda peringkat bintang satu di Play Store.
CommonsWare

apakah lebih baik hanya menampilkan informasi di TextView dengan tombol "Login"? Apa solusi Anda untuk kasus itu?
4ntoine

5
"Tidak perlu memuat data jika itu tidak akan ditampilkan." - maka Anda tidak harus memasukkannya ke dalam ViewPager. Dalam pager dua halaman, kedua halaman akan segera dimuat, apakah Anda suka atau tidak. Pengalaman pengguna ViewPagerseharusnya adalah bahwa konten ada di sana segera setelah digesek, tidak beberapa waktu kemudian. Itulah sebabnya ViewPagermenginisialisasi halaman di depan apa yang terlihat, untuk membantu memastikan pengalaman pengguna.
CommonsWare

2
Tampaknya ViewPager tidak cukup fleksibel dan tidak memungkinkan untuk mematikan caching karena setOffscreenPageLimit minimum adalah 1: stackoverflow.com/questions/10073214/… . Jangan melihat alasan untuk ini dan perilaku yang diharapkan (dalam kasus caching wajib) adalah untuk membuat fragmen TAPI onResume fragmen api () ketika fragmen menjadi terlihat.
4ntoine

1
Agak terlambat, tetapi bagi siapa pun yang menghadapi masalah yang sama, Anda bisa mencoba pustaka FragmentViewPager (saya penulisnya), yang menangani masalah ini dan menyediakan beberapa fitur tambahan. Untuk sampel, periksa halaman GitHub proyek atau jawaban stackoverflow ini .
S. Brukhanda

Jawaban:


581

Cara menentukan kapan Fragmen menjadi terlihat di ViewPager

Anda dapat melakukan hal berikut dengan menimpa setUserVisibleHintdi Fragment:

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

7
Berkat pembaruan Perpustakaan Dukungan Android hari ini (rev 11), masalah petunjuk yang terlihat pengguna akhirnya diperbaiki. Sekarang aman untuk menggunakan petunjuk yang terlihat oleh pengguna untuk ViewPager.
Oasis Feng

58
Saya telah menemukan bahwa metode setUserVisibleHint dipanggil SEBELUM onCreateView dipanggil dan ini membuatnya sulit untuk melacak inisialisasi apa pun.
AndroidDev

11
@AndroidDev Jika Anda ingin menjalankan beberapa kode ketika petunjuk datang sebagai benar, tetapi yang membutuhkan pohon tampilan sudah diinisialisasi, cukup bungkus blok kode itu isResumed()untuk menghindari NPE. Bekerja dengan baik untukku.
Ravi Thapliyal

13
Ini hanya konyol bahwa ada begitu banyak peretasan yang berbeda untuk sesuatu yang seharusnya disediakan SDK secara default.
Mike6679

15
setUserVisibleHintsekarang sudah tidak digunakan lagi
SR

525

PEMBARUAN : Pustaka Dukungan Android (rev 11) akhirnya memperbaiki masalah petunjuk yang terlihat oleh pengguna , sekarang jika Anda menggunakan pustaka dukungan untuk fragmen, maka Anda dapat dengan aman menggunakan getUserVisibleHint()atau mengganti setUserVisibleHint()untuk menangkap perubahan seperti dijelaskan oleh jawaban gorn.

PEMBARUAN 1 Ini adalah satu masalah kecil dengan getUserVisibleHint(). Nilai ini secara default true.

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

Jadi mungkin ada masalah ketika Anda mencoba menggunakannya sebelum setUserVisibleHint()dipanggil. Sebagai solusinya Anda dapat menetapkan nilai dalam onCreatemetode seperti ini.

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

Jawaban yang kedaluwarsa:

Dalam kebanyakan kasus penggunaan, ViewPagerhanya menampilkan satu halaman pada satu waktu, tetapi fragmen pra-cache juga dihukum "terlihat" negara (sebenarnya tak terlihat) jika Anda menggunakan FragmentStatePagerAdapterdalam Android Support Library pre-r11.

Saya menimpa:

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

Untuk menangkap keadaan fokus fragmen, yang saya pikir adalah keadaan yang paling cocok dari "visibilitas" yang Anda maksud, karena hanya satu fragmen di ViewPager yang benar-benar dapat menempatkan item menu bersama dengan item aktivitas induk.


73
Berhati-hatilah menggunakan getActivity (), saat pertama kali dijalankan akan menjadi nol.
vovkab

10
Perhatikan bahwa setUserVisibleHint () selalu berfungsi dengan baik di FragmentPagerAdapter.
louielouie

8
Solusi ini (dan juga gorn) sedikit tertinggal. Dalam kasus di mana viewpager digesek dengan cepat dan beberapa kali, metode ini akan dipanggil dengan penundaan (ketika fragmen tidak lagi terlihat / tidak terlihat).
AsafK

3
Saya mendapatkan trueuntuk getUserVisibleHint () ketika onCreateOptionsMenudipanggil, dan ketika setUserVisibleHintdipanggil, menu belum dibuat sepertinya. Akhirnya saya mendapatkan dua opsi Menu yang ditambahkan ketika saya hanya ingin menu dari fragmen yang terlihat. Ada saran dalam hal ini?
cYrixmorten

13
setUserVisibleHintsekarang sudah tidak digunakan lagi
SR

143

Ini tampaknya mengembalikan onResume()perilaku normal yang Anda harapkan. Itu bermain dengan baik dengan menekan tombol home untuk meninggalkan aplikasi dan kemudian memasukkan kembali aplikasi. onResume()tidak dipanggil dua kali berturut-turut.

@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
Persis apa yang saya cari. Memecahkan masalah dengan setUserVisibleHintdipanggil sebelumnya onCreateViewdan itu setUserVisibleHinttidak dipanggil jika aplikasi berjalan latar dan kemudian latar depan. Luar biasa! Terima kasih!
Alex

11
Saya pikir menelepon padaResume sendiri adalah ide yang sangat buruk, tetapi jika tidak, semua yang Anda butuhkan untuk menjawab pertanyaan dari posting ini adalah dalam fungsi setUserVisibleHint!
Quentin G.

4
Saya lebih suka menerapkan onVisibleToUser()metode yang sederhana dan memanggil itu dari onResume()dan dari setUserVisibleHint(boolean)alih-alih memanggil onResume()diri saya dan mengganggu callback daur hidup. Kalau tidak, saya pikir pendekatan ini bekerja dengan baik, terima kasih!
Stephan Henningsen

1
Ya saya setuju dengan komentar di atas. Secara umum, cobalah untuk menghindari memanggil metode publik dari metode publik di kelas Anda. Buat metode pribadi dan panggil dari kedua metode publik.
Johan Franzén

4
setUserVisibleHint sudah usang!
Hamid Reza

70

Berikut cara lain menggunakan 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
Solusi ini berfungsi dengan baik, terima kasih. Ini harus menjadi solusi yang disarankan bagi mereka yang membangun untuk platform lama menggunakan paket dukungan fragmen (v4)
Frank Yin

7
Perlu dicatat bahwa ini tidak bisa memberi Anda hasil yang diharapkan jika Anda menggunakan FragmentStatePagerAdapter dan membuat instance instance Fragmen baru di getItem (), karena Anda hanya akan mengatur keadaan pada fragmen baru dan bukan yang didapatkan oleh viewpager.
Ben Pearson

1
Solusi ini bagus jika Anda ingin melakukan sesuatu ketika fragmen menjadi terlihat. Itu tidak menjaga keadaan fragmen yang terlihat (artinya, ia tidak tahu kapan fragmen itu tidak terlihat).
AsafK

Saya jenis masalah yang sama. tolong bantu saya untuk menyelesaikan masalah. Posting Saya: stackoverflow.com/questions/23115283/…
Jeeten Parmar

ini memberikan pengecualian null pointer untuk adaptor yang saya gunakan dalam metode iamVisible dari fragmen. Saya mencoba mengatur data yang diterima dari jaringan hanya ketika fragmen terlihat.
zaphod100.10

58

setUserVisibleHint()dipanggil kadang-kadang sebelum onCreateView() dan kadang-kadang setelah yang menyebabkan masalah.

Untuk mengatasinya Anda perlu memeriksa isResumed()juga setUserVisibleHint()metode di dalam . Tetapi dalam hal ini saya menyadari setUserVisibleHint()dipanggil hanya jika Fragmen dilanjutkan dan terlihat, BUKAN saat Dibuat.

Jadi, jika Anda ingin memperbarui sesuatu ketika Fragment berada visible, masukkan fungsi pembaruan Anda di onCreate()dan setUserVisibleHint():

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

UPDATE: Masih saya menyadari myUIUpdate()dipanggil dua kali kadang-kadang, alasannya adalah, jika Anda memiliki 3 tab dan kode ini ada di tab ke-2, ketika Anda pertama kali membuka tab ke-1, tab ke-2 juga dibuat meskipun tidak terlihat dan myUIUpdate()dipanggil. Kemudian ketika Anda menggeser ke tab ke-2, myUIUpdate()dari if (visible && isResumed())dipanggil dan sebagai hasilnya, myUIUpdate()dapat dipanggil dua kali dalam satu detik.

Yang lain masalah adalah !visibledi setUserVisibleHintdipanggil kedua 1) ketika Anda pergi keluar dari layar fragmen dan 2) sebelum diciptakan, ketika Anda beralih ke layar fragmen pertama kalinya.

Larutan:

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

Penjelasan:

fragmentResume, fragmentVisible: Memastikan myUIUpdate()di onCreateView()disebut hanya ketika fragmen dibuat dan terlihat, tidak di resume. Ini juga memecahkan masalah ketika Anda berada di tab ke-1, tab ke-2 dibuat bahkan jika itu tidak terlihat. Ini menyelesaikan itu dan memeriksa apakah layar fragmen terlihat saat onCreate.

fragmentOnCreated: Memastikan fragmen tidak terlihat, dan tidak dipanggil saat Anda membuat fragmen pertama kali. Jadi sekarang ini jika klausa hanya dipanggil ketika Anda menggesek fragmen.

Pembaruan Anda dapat memasukkan semua kode ini dalam BaseFragmentkode seperti ini dan mengganti metode.


1
Solusi Anda berfungsi dengan baik kecuali dengan satu skenario, ketika fragmen dibuka secara normal dan kemudian Anda mengklik beberapa tombol untuk menggantinya dengan fragmen lain dan kemudian klik kembali untuk memunculkan fragmen baru dari backstack, jika case setUserVisibleHinttidak mendapat panggilan .. ! dan di dalam onCreateViewmetode fragmentVisibleitu false!? jadi fragmen itu muncul kosong ..! ada pemikiran.?
Alaa AbuZarifa

28

Untuk mendeteksi Fragmentdalam ViewPagerterlihat, aku cukup yakin bahwa hanya menggunakan setUserVisibleHint tidak cukup.
Inilah solusi saya untuk memeriksa apakah sebuah fragmen terlihat atau tidak terlihat. Pertama saat meluncurkan viewpager, beralih di antara halaman, pergi ke aktivitas / fragmen / latar belakang / latar depan lain`

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

PENJELASAN Anda dapat memeriksa logcat di bawah ini dengan hati-hati maka saya pikir Anda mungkin tahu mengapa solusi ini akan berhasil

Peluncuran pertama

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

Pergi ke halaman2

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

Pergi ke halaman3

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

Pergi ke latar belakang:

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

Pergi ke latar depan

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

Proyek DEMO di sini

Semoga ini bisa membantu


1
Ini tidak berfungsi ketika sebuah fragmen memiliki pager tampilan lain yang juga terdiri dari fragmen.
Shihab Uddin

maaf, saya tidak dapat memposting jawaban saat ini karena saya tidak punya cukup waktu, jika memungkinkan silakan merujuk git saya tentang masalah Anda di sini github.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master . SubChildContainerFragmentdigunakan untuk mendeteksi a fragment has another view pager which also consists fragment. Anda dapat mencampur SubChildContainerFragmentdan ChildContainerFragmentke 1 kelas. Semoga ini bisa membantu. Saya akan mengirimkan jawaban lengkapnya nanti
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
Ini harus menjadi jawaban yang diterima. Bekerja dengan android.app.Fragment juga, yang merupakan bonus besar.
RajV

setUserVisibleHintTidak digunakan lagi
ekashking

23

Di ViewPager2dan ViewPagerdari versi, androidx.fragment:fragment:1.1.0Anda bisa menggunakan onPausedan onResumemenelepon balik untuk menentukan fragmen mana yang saat ini terlihat oleh pengguna. onResumepanggilan balik dipanggil saat fragmen menjadi terlihat dan onPauseketika berhenti untuk terlihat.

Dalam kasus ViewPager2 itu adalah perilaku default tetapi perilaku yang sama dapat diaktifkan untuk barang lama ViewPagerdengan mudah.

Untuk mengaktifkan perilaku ini di ViewPager pertama Anda harus melewati FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTparameter sebagai argumen kedua FragmentPagerAdapterkonstruktor.

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

Catatan: setUserVisibleHint()metode dan FragmentPagerAdapterkonstruktor dengan satu parameter sekarang tidak digunakan lagi dalam versi baru Fragment dari android jetpack.


1
Terima kasih atas solusinya. Saya mencari ini sejak lama. Great Work
Anurag Srivastava

1
Untuk ViewPager2 opsi BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT bukan perilaku default, Anda perlu mengaturnya secara manual. Saat mengatur opsi ini, solusi Anda bekerja untuk saya. Terima kasih.
driAn

1
Pada April 2020 ini adalah solusi yang diperbarui dan bekerja seperti pesona.
Prakash

1
@ 4ntoine tolong pertimbangkan menerima jawaban ini sebagai jawaban yang benar, karena sampai sekarang ini sudah benar dan sebagian besar jawaban menggunakan metode setUserVisibleHint yang sudah tidak digunakan lagi
Jorn Rigter

16

Override setPrimaryItem()dalam FragmentPagerAdaptersubclass. Saya menggunakan metode ini, dan itu bekerja dengan baik.

@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
Ini hampir berhasil tetapi memiliki masalah dengan NPE dan dipanggil beberapa kali. Diposting alternatif di bawah ini!
Gober


@ Robert menyimpan objek Objek pada saat pertama, dan memeriksa dan mengabaikan panggilan tindak lanjut dengan objek Obyek yang sama seperti yang dilakukan oleh metode FragmentStateAdapter dalam metode setPrimaryItem ()
wanglugao

12

Ganti Fragment.onHiddenChanged()untuk itu.

public void onHiddenChanged(boolean hidden)

Disebut ketika keadaan tersembunyi (dikembalikan oleh isHidden()) fragmen telah berubah. Fragmen mulai tidak disembunyikan; ini akan dipanggil setiap kali fragmen berubah dari situ.

Parameter
hidden- boolean: Benar jika fragmen sekarang disembunyikan, salah jika tidak terlihat.


3
metode ini sudah usang sekarang dan tidak dapat digunakan lagi
bluewhile

4
@bluewhile di mana Anda melihat itu sudah usang? developer.android.com/reference/android/app/…
Cel

1
Saya baru saja menggunakan ini dengan sukses, dan dokumentasi sepertinya tidak mengindikasikan itu sudah usang.
Trevor

1
@bluewhile, dalam 4.1 (api 16) itu belum ditinggalkan.
dan

8
Saya menggunakan API level 19 dan dapat mengonfirmasi bahwa sementara ini tidak ditinggalkan, itu tidak berfungsi seperti yang diiklankan. onHiddenChanged tidak dipanggil ketika fragmen disembunyikan oleh fragmen lain, misalnya.

4

Saya menemukan itu onCreateOptionsMenudan onPrepareOptionsMenumetode yang disebut hanya dalam kasus fragmen benar-benar terlihat. Saya tidak dapat menemukan metode yang berperilaku seperti ini, saya juga mencoba OnPageChangeListenertetapi tidak berfungsi untuk situasi, misalnya, saya perlu variabel yang diinisialisasi dalam onCreatemetode.

Jadi kedua metode ini dapat digunakan untuk masalah ini sebagai solusi, khususnya untuk pekerjaan kecil dan pendek.

Saya pikir, ini adalah solusi yang lebih baik tetapi bukan yang terbaik. Saya akan menggunakan ini tetapi menunggu solusi yang lebih baik pada saat yang sama.

Salam.


2

Solusi lain yang diposting di sini mengesampingkan setPrimaryItem di pageradapter oleh kris larson hampir berhasil untuk saya. Tetapi metode ini disebut beberapa kali untuk setiap pengaturan. Saya juga mendapatkan NPE dari pandangan, dll. Dalam fragmen karena ini tidak siap beberapa kali pertama metode ini disebut. Dengan perubahan berikut ini berhasil bagi saya:

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

Tambahkan Kode berikut di dalam fragmen

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

     }
}

Ini hanya berfungsi untuk ViewPagerFragments. Bukan untuk fragmen dalam aktivitas normal.
AndroidGuy

2

Saya mengalami masalah yang sama saat bekerja dengan FragmentStatePagerAdaptersdan 3 tab. Saya harus menunjukkan Dilaog setiap kali tab pertama diklik dan menyembunyikannya saat mengklik tab lain.

Mengesampingkan setUserVisibleHint()sendiri tidak membantu menemukan fragmen yang terlihat saat ini.

Saat mengklik dari tab ke-3 -----> tab ke-1. Ini memicu dua kali untuk fragmen ke-2 dan untuk fragmen ke-1. Saya menggabungkannya dengan metode isResumed ().

    @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

Kami memiliki kasus khusus dengan MVP di mana fragmen perlu memberi tahu presenter bahwa pandangan telah menjadi terlihat, dan presenter disuntikkan oleh Dagger pada fragment.onAttach().

setUserVisibleHint()tidak cukup, kami telah mendeteksi 3 kasus berbeda yang perlu ditangani ( onAttach()disebutkan agar Anda tahu kapan presenter tersedia):

  1. Fragmen baru saja dibuat. Sistem membuat panggilan berikut:

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. Fragmen sudah dibuat dan tombol beranda ditekan. Saat mengembalikan aplikasi ke latar depan, ini disebut:

    onResume()
  3. Perubahan orientasi:

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

Kami hanya ingin petunjuk visibilitas sampai ke presenter satu kali, jadi ini adalah bagaimana kami melakukannya:

@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

Mendeteksi oleh focused view!

Ini bekerja untuk saya

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

Saya mengalami masalah ini ketika saya mencoba untuk menyalakan timer ketika fragmen di viewpager berada di layar untuk dilihat oleh pengguna.

Penghitung waktu selalu dimulai tepat sebelum fragmen dilihat oleh pengguna. Ini karena onResume()metode dalam fragmen dipanggil sebelum kita dapat melihat fragmen.

Solusi saya adalah melakukan pemeriksaan dalam onResume()metode ini. Saya ingin memanggil metode tertentu 'foo ()' ketika fragmen 8 adalah pager tampilan saat ini.

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

Semoga ini membantu. Saya telah melihat masalah ini muncul banyak. Ini tampaknya menjadi solusi paling sederhana yang pernah saya lihat. Banyak yang lain tidak kompatibel dengan API yang lebih rendah dll.


1

Saya memiliki masalah yang sama. ViewPagermengeksekusi peristiwa siklus hidup fragmen lainnya dan saya tidak bisa mengubah perilaku itu. Saya menulis pager sederhana menggunakan fragmen dan animasi yang tersedia. SimplePager


1

Saya menggunakan ini dan itu berhasil!

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

1

Saya mendukung SectionsPagerAdapter dengan fragmen anak jadi setelah banyak sakit kepala saya akhirnya mendapatkan versi yang berfungsi berdasarkan solusi dari topik ini:

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

Lupa menyebutkan, setelah Anda menambahkan fragmen anak ke fragmen induk, atur fragment.setUserVisibleHint (true);
Andoctorey

Dan jangan lupa untuk menggunakan getChildFragmentManager () alih-alih getFragmentManager () untuk menambahkan fragmen anak.
Andoctorey

0

Catatan yang setUserVisibleHint(false)tidak dipanggil pada penghentian aktivitas / fragmen. Anda masih perlu memeriksa mulai / hentikan untuk register/unregistermendengarkan dengan benar pendengar / dll.

Juga, Anda akan mendapatkan setUserVisibleHint(false)jika fragmen Anda mulai dalam keadaan tidak terlihat; Anda tidak ingin ke unregistersana karena Anda belum pernah mendaftar sebelumnya dalam kasus itu.

@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

Cara sederhana untuk mengimplementasikannya adalah memeriksa apakah pengguna masuk sebelumnya masuk ke fragmen.

Di MainActivity Anda, Anda dapat melakukan sesuatu seperti ini di dalam metode onNavigationItemSelected .

 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;

Namun, jika Anda menggunakan laci navigasi, pilihan dalam laci akan berubah menjadi Profil meskipun kami belum pergi ke ProfileFragment.

Untuk mengatur ulang pilihan ke pilihan saat ini jalankan kode di bawah ini

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

0

setUserVisibleHint (boolean terlihat) sekarang sudah usang Jadi ini adalah solusi yang benar

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

Di ViewPager2 dan ViewPager dari versi androidx.fragment:fragment:1.1.0Anda bisa menggunakan onPause()dan onResume()untuk menentukan fragmen mana yang saat ini terlihat oleh pengguna. onResume()disebut ketika fragmen menjadi terlihat dan onPauseketika berhenti untuk terlihat.

Untuk mengaktifkan perilaku ini di ViewPager pertama Anda harus melewati FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTparameter sebagai argumen kedua FragmentPagerAdapterkonstruktor.


-4

Saya mengesampingkan metode Hitung dari FragmentStatePagerAdapter yang terkait dan mengembalikannya jumlah total dikurangi jumlah halaman yang disembunyikan:

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

Jadi, jika ada 3 fragmen yang pada awalnya ditambahkan ke ViewPager, dan hanya 2 fragmen pertama yang akan ditampilkan sampai beberapa kondisi terpenuhi, ganti jumlah halaman dengan mengatur TrimmedPages ke 1 dan seharusnya hanya menampilkan dua halaman pertama.

Ini berfungsi baik untuk halaman di bagian akhir, tetapi tidak akan sangat membantu untuk yang di awal atau di tengah (meskipun ada banyak cara untuk melakukan ini).

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.