ViewPager tidak menggambar ulang konten, tetap / kosong

86

Kami mengalami masalah yang sangat aneh dengan ViewPager di sini. Kami menyematkan daftar di setiap halaman ViewPager, dan memicu notifyDataSetChanged baik pada adaptor daftar maupun adaptor tampilan halaman saat memperbarui data daftar.

Apa yang kami amati adalah terkadang, halaman tidak memperbarui struktur tampilan, yaitu tetap kosong, atau terkadang bahkan menghilang saat berpindah halaman. Saat paging bolak-balik beberapa kali, konten tiba-tiba akan muncul kembali. Sepertinya Android kehilangan pembaruan tampilan di sini. Saya juga memperhatikan bahwa saat men-debug dengan penampil hierarki, memilih tampilan akan selalu membuatnya muncul kembali, tampaknya karena penampil hierarki memaksa tampilan yang dipilih untuk menggambar ulang sendiri.

Saya tidak bisa membuat ini bekerja secara terprogram; membatalkan tampilan daftar, atau bahkan seluruh halaman tampilan, tidak berpengaruh.

Ini dengan pustaka kompatibilitas-v4_r7. Saya juga mencoba menggunakan revisi terbaru, karena ia mengklaim dapat memperbaiki banyak masalah yang berkaitan dengan view pager, tetapi itu membuat masalah menjadi lebih buruk (misalnya, isyarat rusak sehingga tidak memungkinkan saya halaman melalui semua halaman lagi kadang-kadang.)

Apakah ada orang lain yang mengalami masalah ini juga, atau apakah Anda tahu apa yang menyebabkan ini?

Matthias
sumber

Jawaban:

44

Kami akhirnya berhasil menemukan solusi. Rupanya implementasi kami mengalami dua masalah:

  1. adaptor kami tidak menghapus tampilan di destroyItem().
  2. kami menyimpan tampilan dalam cache sehingga kami harus memekarkan tata letak kami hanya sekali, dan, karena kami tidak menghapus tampilan di destroyItem(), kami tidak menambahkannya instantiateItem()tetapi hanya mengembalikan tampilan yang disimpan dalam cache sesuai dengan posisi saat ini.

Saya belum melihat terlalu dalam di kode sumber ViewPager- dan tidak terlalu eksplisit bahwa Anda harus melakukan itu - tetapi dokumen mengatakan:

destroyItem ()
Hapus halaman untuk posisi tertentu. Adaptor bertanggung jawab untuk menghapus tampilan dari penampungnya, meskipun hanya harus memastikan ini dilakukan pada saat ia kembali dari finishUpdate (ViewGroup).

dan:

PagerAdapter yang sangat sederhana dapat memilih untuk menggunakan Tampilan laman itu sendiri sebagai objek utama, mengembalikannya dari instantiateItem (ViewGroup, int) setelah pembuatan dan menambahkannya ke ViewGroup induk. Implementasi destroyItem (ViewGroup, int, Object) yang cocok akan menghapus View dari ViewGroup induk dan isViewFromObject (View, Object) bisa diimplementasikan sebagai return view == object ;.

Jadi kesimpulan saya adalah itu ViewPagerbergantung pada adaptor yang mendasarinya untuk secara eksplisit menambahkan / menghapus turunannya di instantiateItem()/ destroyItem(). Artinya, jika adaptor Anda adalah subkelas dari PagerAdapter, subkelas Anda harus mengimplementasikan logika ini.

Catatan tambahan: perhatikan ini jika Anda menggunakan daftar di dalamnya ViewPager.

futtetennista
sumber
2
Saya mengalami masalah yang sama tetapi dengan FragmentPagerAdapter, yang menangani onDestroy untuk saya. Tidak ada solusi lain di sini yang berfungsi juga.
Greg Ennis
14
Apa yang juga bisa menjadi masalah, adalah seseorang menggunakan getFragmentManger daripada GetChildFragmentManager
Boy
@ Boy apa itu GetChildFragmentManager?
kebakaran di lubang
1
@ Boy Wooooow .... Saya telah menjambak rambut saya selama dua hari penuh mencoba mencari tahu mengapa saya tidak bisa mendapatkan apa pun untuk diperbarui. TERIMA KASIH! (mereka harus benar-benar memperhatikan dokumentasi Google untuk menggunakan childfragmentmanager, itu sama sekali bukan berarti Anda membutuhkan pengelola yang berbeda).
pengguna0721090601
@futtetennista Saya melakukan hal yang sama .. menghadapi masalah ini .. dapatkah Anda memeriksa .. stackoverflow.com/questions/61727835/…
AskQ
55

Jika ViewPagerdiatur di dalam Fragment dengan FragmentPagerAdapter, menggunakan getChildFragmentManager()bukan getSupportFragmentManager()sebagai parameter untuk menginisialisasi Anda FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

Dari pada

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
eyelave
sumber
2
Tangkapan bagus, tampaknya menyelesaikan masalah saya saat menggunakan FragmentPagerAdapter
Tobliug
1
Terima kasih - ini berhasil. Aku mencoba untuk memaksa getItem()pada FragmentPagerAdapteryang bersarang di Fragment diciptakan.
kosiara - Bartosz Kosarzycki
wow ini bekerja seperti pesona. Saya kira masalahnya adalah karena membengkaknya viewpager dalam sebuah fragmen
Thecarisma
Saat-saat seperti ini saya berharap kami memiliki tepuk seperti Medium sehingga saya dapat memberi Anda 1000+ tepukan untuk ini. Kerja bagus, ini harus menjadi jawaban yang diterima
Codelicious
21

Saya memiliki masalah yang persis sama tetapi saya benar-benar menghancurkan tampilan di destroyItem (saya pikir). Masalahnya bagaimanapun adalah saya menghancurkannya menggunakan viewPager.removeViewAt(index);insted dariviewPager.removeView((View) object);

Salah:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

Baik:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}
Ringen
sumber
Terima kasih atas petunjuknya. Telah melihat masalah ini selama beberapa waktu.
Moritz
2
Ini berhasil untuk saya, tetapi apakah Anda tahu mengapa yang pertama menjadi masalah?
HannahMitt
@HannahMitt removeViewAt akan menghapus tampilan apa pun yang ada di posisi tertentu itu dalam daftar anak-anak ViewPager saat ini, dan halaman dapat ditambahkan ke ViewPager dalam urutan apa pun (jadi halaman 0 sebenarnya mungkin ada di ViewPager pada indeks 1 atau apa pun). removeView akan melakukan iterasi melalui daftar anak dan menghapus persis objek yang ditentukan.
2
Tidak berfungsi untuk saya: Tidak dapat mentransmisikan tampilan ke fragmen. objek di sini adalah sebuah fragmen.
FRK
viewpager harus statis?
Kanagalingam
10

ViewPager mencoba melakukan hal-hal cerdas seputar penggunaan kembali item, tetapi mengharuskan Anda untuk mengembalikan posisi item baru ketika ada hal yang berubah. Coba tambahkan ini ke PagerAdapter Anda:

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

Ini pada dasarnya memberi tahu ViewPager bahwa semuanya telah berubah (dan memaksanya untuk membuat ulang semuanya). Itulah satu-satunya hal yang dapat saya pikirkan di luar kepala saya.

Chris Banes
sumber
1
Ya, saya telah membaca tentang opsi ini di utas ini: stackoverflow.com/questions/7263291/… - namun, ini sepertinya pendekatan palu godam. Pasti ada cara yang lebih elegan? Saya ingin tahu apakah ini terkait dengan penggunaan daftar sebagai halaman pager?
Matthias
Ini sedikit pendekatan palu godam, tetapi Anda dapat mengatasinya. yaitu mengembalikan nilai yang benar dari metode tersebut.
Chris Banes
@ChrisBanes Seperti yang Anda katakan memanggil return POSITION_NONE;, forces it to re-instantiate everythingtetapi dalam kasus saya, saya tidak ingin instantiate ulang semuanya , jadi apakah mungkin untuk menghapus viewtanpa membersihkan barang yang ada? Tolong beri tahu saya
Ritesh Adulkar
Anda adalah
penikmat
2

Mencoba terlalu banyak solusi tetapi ternyata viewPager.post()berhasil

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });
Zeero0
sumber
1
Tuhanku. mengapa tidak ada yang memilih ini? Saya mendapat perilaku aneh ketika saya memasukkan kembali sebuah fragmen dan viewpager di dalamnya tidak merender sendiri, dan menundanya sedikit seperti ini sebenarnya menyelesaikan masalah!
Fugogugo
0

Pustaka Dukungan Android memiliki Aktivitas demo yang menyertakan ViewPager dengan ListView di setiap halaman. Anda mungkin harus melihat dan melihat apa fungsinya.

Di Eclipse (dengan Android Dev Tools r20):

  1. Pilih New > Android Sample Project
  2. Pilih level API target Anda (saya sarankan yang terbaru tersedia)
  3. Pilih Support4Demos
  4. Klik kanan proyek dan pilih Android Tools > Add Support Library
  5. Jalankan aplikasi dan pilih FragmentlaluPager

Kode untuk ini ada di src/com.example.android.supportv4.app/FragmentPagerSupport.java. Semoga berhasil!

Sparky
sumber
terima kasih - saya akan melihat itu! Mungkin saya akan melihat ada yang salah.
Matthias
0

Saya mengalami ini dan memiliki masalah yang sangat mirip. Saya bahkan menanyakannya pada stack overflow.

Bagi saya, sebagai orang tua dari pandangan saya, seseorang menjadi subclass LinearLayoutdan mengesampingkan requestLayout()tanpa menelepon super.requestLayout(). Ini dicegah onMeasuredan onLayoutdipanggil di ViewPager saya (meskipun hierarchyviewer memanggil ini secara manual). Tanpa diukur, mereka akan muncul sebagai kosong di ViewPager.

Jadi periksa pandangan Anda yang berisi. Pastikan mereka subclass dari View dan tidak secara membabi buta menimpa requestLayout atau yang serupa.

Tim O'Brien
sumber
0

Punya masalah yang sama, yang ada hubungannya dengan ListView(karena tampilan kosong saya muncul dengan baik jika daftar kosong). Saya baru saja menyebut requestLayout()yang bermasalah ListView. Sekarang menggambar dengan baik!

Oleg Vaskevich
sumber
0

Saya mengalami masalah yang sama ini saat menggunakan ViewPager dan FragmentStatePagerAdapter. Saya mencoba menggunakan penangan dengan penundaan 3 detik untuk memanggil invalidate () dan requestLayout () tetapi tidak berhasil. Apa yang berhasil adalah menyetel ulang warna latar belakang viewPager sebagai berikut:

MyFragment.java

    private Handler mHandler;
    private Runnable mBugUpdater;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = new ViewPager(getActivity());
        //...Create your adapter and set it here...

        mHandler = new Handler();
        mBugUpdater = new Runnable(){
            @Override
            public void run() {
                mVp.setBackgroundColor(mItem.getBackgroundColor());
                mHandler = null;
                mBugUpdater = null;
            }           
        };
        mHandler.postDelayed(mBugUpdater,50);

        return rootView;
    }

    @Override
    public void onPause() {
        if(mHandler != null){
            //Remove the callback if it hasn't triggered yet
            mHandler.removeCallbacks(mBugUpdater);
            mHandler = null;
            mBugUpdater = null;
        }
        super.onPause();
     }
Chris Sprague
sumber
0

Saya memiliki masalah dengan gejala yang sama, tetapi penyebab berbeda yang ternyata adalah kesalahan konyol saya. Kupikir aku akan menambahkannya di sini kalau-kalau itu membantu siapa pun.

Saya memiliki ViewPager menggunakan FragmentStatePagerAdapter yang dulu memiliki dua fragmen, tetapi saya kemudian menambahkan yang ketiga. Namun, saya lupa bahwa batas halaman layar mati default adalah 1 - jadi, ketika saya beralih ke fragmen ketiga yang baru, yang pertama akan dihancurkan, kemudian dibuat ulang setelah beralih kembali. Masalahnya adalah bahwa aktivitas saya bertugas memberi tahu fragmen ini untuk menginisialisasi status UI-nya. Ini terjadi saat aktivitas dan siklus hidup fragmen sama, tetapi untuk memperbaikinya saya harus mengubah fragmen untuk menginisialisasi UI mereka sendiri selama siklus hidup startup mereka. Pada akhirnya saya juga akhirnya mengubah setOffscreenPageLimit menjadi 2 sehingga ketiga fragmen tetap hidup setiap saat (aman dalam hal ini karena tidak terlalu intensif memori).

dfinn
sumber
-1

Saya memiliki masalah serupa. Saya menyimpan cache tampilan karena saya hanya perlu 3 tampilan ViewPager. Ketika saya geser ke depan semuanya baik-baik saja tetapi ketika saya mulai meluncur ke belakang terjadi kesalahan, dikatakan bahwa "pandangan saya sudah memiliki induk". Solusinya adalah menghapus item yang tidak dibutuhkan secara manual.

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];
Roman Nazarevych
sumber
-1

Bagi saya masalahnya adalah kembali ke aktivitas setelah proses aplikasi dihentikan. Saya menggunakan adaptor halaman tampilan kustom yang dimodifikasi dari sumber Android. Halaman tampilan disematkan langsung di aktivitas.

Panggilan viewPager.setCurrentItem(position, true);

(dengan animasi) setelah menyetel data dan notifyDataSetChanged () tampaknya berfungsi, tetapi jika parameter disetel ke false, ia tidak dan fragmennya kosong. Ini adalah kasus tepi yang mungkin bisa membantu seseorang.

Jahat
sumber
-1

Untuk pengguna Kotlin:

Dalam fragmen Anda; Gunakan childFragmentManagersebagai gantiviewPagerAdapter

Batuhan Köklü
sumber