Penggunaan forceLayout (), requestLayout () dan invalidate ()

185

Saya agak bingung tentang peran forceLayout(), requestLayout()dan invalidate()metode Viewkelas.

Kapan mereka akan dipanggil?

sdabet
sumber

Jawaban:

357

Untuk lebih memahami jawaban yang diberikan oleh François BOURLIEUX dan Dalvik, saya sarankan Anda melihat diagram siklus hidup tampilan yang mengagumkan ini oleh Arpit Mathur : masukkan deskripsi gambar di sini

Bartek Lipinski
sumber
28
Saya sering melihat requestLayout dipanggil langsung setelah invalidate dipanggil, saya bahkan melihat bahwa terjadi pada kode sumber Android untuk hal-hal seperti TextView, tetapi menurut diagram ini, melakukan hal itu berlebihan, bukan? Jadi, apakah ada tujuan untuk melakukan itu?
tcox
5
Nah itu pertanyaan yang menarik, dan sejujurnya saya tidak tahu mengapa mereka menyebut kedua metode tersebut misalnya TextView. Saya pikir mungkin mereka ingin menggambar Viewuntuk yang terakhir kali sebelum mereka mengubah parameter yang berhubungan dengan tata letak, tetapi tidak benar-benar masuk akal jika kita berpikir tentang panggilan yang dipanggil dalam urutan yang berbeda (dan mereka memanggil invalidate()tepat setelah requestLayout()di TextViewdemikian juga). Mungkin layak menerima pertanyaan lain tentang StackOverflow :)?
Bartek Lipinski
14
(1/2) : Saya pikir Anda tidak memahami kedua metode itu ( invalidate()dan requestLayout()) dengan benar. Tujuan dari metode-metode tersebut adalah untuk mengetahui Viewjenis pembatalan seperti apa (sebagaimana Anda menyebutnya) telah terjadi. Ini tidak seperti Viewmemutuskan jalan apa yang harus diikuti setelah Anda memanggil salah satu metode itu. Logika di balik memilih View-lifecycle-path adalah memilih metode yang tepat untuk memanggil dirinya sendiri. Jika ada sesuatu yang berkaitan dengan perubahan ukuran - requestLayout()harus dipanggil, jika hanya ada perubahan visual tanpa ukuran diubah - Anda harus menelepon invalidate().
Bartek Lipinski
11
(2/2): Jika Anda mengubah ukuran Anda Viewdalam beberapa cara, misalnya Anda mendapatkan arusLayoutParams dari Viewdan Anda mengubah mereka, tetapi tidak menyebut baik requestLayoutatau setLayoutParams(yang panggilan requestLayoutinternal), maka Anda dapat menghubungi invalidate()sebanyak yang Anda inginkan, dan yang Viewtidak akan melalui proses ukuran-layout, karena itu tidak akan mengubah ukurannya. Jika Anda tidak memberi tahu Anda Viewbahwa ukurannya telah berubah (dengan requestLayoutpemanggilan metode), maka Viewakan menganggap itu tidak, dan onMeasuredan onLayouttidak akan dipanggil.
Bartek Lipinski
2
@tcox lebih lanjut di sini -> stackoverflow.com/questions/35279374/...
Sotti
125

invalidate()

Panggilan invalidate()dilakukan saat Anda ingin menjadwalkan gambar ulang tampilan. Itu akan menghasilkan onDrawdipanggil pada akhirnya (segera, tetapi tidak segera). Contoh kapan tampilan kustom menyebutnya adalah ketika properti teks atau warna latar belakang telah berubah.

Tampilan akan digambar ulang tetapi ukurannya tidak akan berubah.

requestLayout()

Jika ada sesuatu tentang perubahan tampilan Anda yang akan mempengaruhi ukuran, maka Anda harus menelepon requestLayout(). Ini akan memicu onMeasuredan onLayouttidak hanya untuk tampilan ini tetapi juga sepanjang garis untuk tampilan induk.

Memanggil requestLayout()ini tidak dijamin hasil dalamonDraw (bertentangan dengan apa yang diagram dalam jawaban diterima menyiratkan), sehingga biasanya dikombinasikan dengan invalidate().

invalidate();
requestLayout();

Contoh dari ini adalah ketika label kustom memiliki properti teksnya diubah. Label akan berubah ukuran dan karenanya perlu diukur ulang dan digambar ulang.

forceLayout()

Ketika ada requestLayout()yang dipanggil pada kelompok tampilan induk, itu tidak perlu untuk mengukur kembali dan menyampaikan pandangan anaknya. Namun, jika seorang anak harus dimasukkan dalam remeasure dan relayout, maka Anda dapat memanggilnya forceLayout(). forceLayout()hanya bekerja pada anak jika itu terjadi bersamaan dengan requestLayout()pada orangtua langsungnya. Memanggil forceLayout()dengan sendirinya tidak akan berpengaruh karena tidak memicu requestLayout()pohon tampilan.

Baca Tanya Jawab ini untuk deskripsi yang lebih terperinci tentang forceLayout().

Pelajaran lanjutan

Suragch
sumber
1
tetapi bukankah lebih masuk akal untuk memanggil requestLayout () terlebih dahulu dan kemudian membatalkan ()?
scholt
2
@ Scholt, Sejauh yang saya tahu pesanannya tidak masalah, jadi Anda bisa menelepon requestLayout()sebelumnya invalidate()jika mau. Tidak ada yang melakukan tata letak atau menggambar segera. Sebaliknya, mereka menetapkan bendera yang pada akhirnya akan menghasilkan relayout dan redraw.
Suragch
27

Di sini Anda dapat menemukan beberapa respons: http://developer.android.com/guide/topics/ui/how-android-draws.html

Bagi saya panggilan untuk invalidate()hanya menyegarkan tampilan dan panggilan untuk requestLayout()menyegarkan tampilan dan menghitung ukuran tampilan di layar.

François BOURLIEUX
sumber
3
lalu bagaimana dengan forceLayout ()?
sdabet
@fiddler, metode ini hanya menetapkan dua flag: PFLAG_FORCE_LAYOUT dan PFLAG_INVALIDATED
suitianshi
7
@ suuitianshi Apa konsekuensi dari pengaturan bendera itu?
Sergey
1
@Sergey Konsekuensinya adalah bahwa flag ini mengabaikan cache pengukuran (tampilan menggunakan cache untuk mengukur lebih cepat di masa depan untuk MeasureSpec yang sama); lihat baris 18783 dari View.java di sini: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim
3

Anda menggunakan invalidate () pada tampilan yang ingin Anda redraw, itu akan membuat onDraw (Canvas c) untuk dipanggil, dan requestLayout () akan membuat rendering seluruh layout (fase pengukuran dan fase positioning) berjalan kembali. Anda harus menggunakannya jika Anda mengubah ukuran tampilan anak saat runtime tetapi hanya dalam kasus-kasus tertentu seperti batasan dari tampilan induk (maksud saya bahwa tinggi atau lebar orangtua adalah WRAP_CONTENT dan karenanya cocok mengukur anak-anak sebelum mereka dapat membungkusnya lagi)

Nativ
sumber
3

Jawaban ini tidak benar tentang forceLayout().

Seperti yang dapat Anda lihat dalam kodeforceLayout() itu hanya menandai pandangan sebagai "membutuhkan relayout" tetapi tidak menjadwalkan atau memicu relayout itu. Relai tidak akan terjadi sampai pada suatu titik di masa depan orang tua tampilan diletakkan karena alasan lain.

Ada juga masalah yang jauh lebih besar saat menggunakan forceLayout()dan requestLayout():

Katakanlah Anda telah meminta forceLayout()tampilan. Sekarang ketika memanggil requestLayout()keturunan dari pandangan itu, Android akan secara rekursif memanggil requestLayout()leluhur keturunan itu. Masalahnya adalah itu akan menghentikan rekursi pada tampilan yang Anda panggil forceLayout(). Jadi requestLayout()panggilan tidak akan pernah mencapai view root dan dengan demikian tidak pernah menjadwalkan lulus tata letak. Seluruh subtree dari hierarki tampilan sedang menunggu tata letak dan memanggil requestLayout()semua tampilan subtree tersebut tidak akan menyebabkan tata letak. Hanya memanggil requestLayout()sembarang tampilan di luar subtree yang akan memecah mantra.

Saya akan mempertimbangkan implementasi forceLayout()(dan bagaimana pengaruhnya requestLayout()rusak dan Anda seharusnya tidak pernah menggunakan fungsi itu dalam kode Anda.

fluidsonic
sumber
1
Hai! Bagaimana kamu menemukan mantra itu ? Apakah itu didokumentasikan di suatu tempat?
azizbekian
1
Sayangnya itu tidak didokumentasikan dan kemungkinan bahkan tidak dimaksudkan. Saya mengetahuinya dengan menyelidiki kode Viewdan dengan menjalankan sendiri masalah tersebut & men-debug-nya.
fluidsonic
Lalu saya ingin tahu tentang tujuan forceLayout()API: itu tidak benar-benar memaksa pass layout, melainkan hanya mengubah flag yang sedang diamationMeasure() , tetapi onMeasure()tidak akan dipanggil kecuali requestLayout()atau secara eksplisit View#measure()dipanggil. Itu berarti, itu forceLayout()harus dipasangkan requestLayout(). Di sisi lain, mengapa kemudian melakukan forceLayout()jika saya masih perlu melakukan requestLayout()?
azizbekian
@azizbekian tidak, Anda tidak harus melakukannya. requestLayout()melakukan segala sesuatu yang forceLayout()juga dilakukannya.
fluidsonic
1
@ Suragch melewatkan yang itu, terima kasih! Analisis yang sangat menarik. Penggunaan yang dimaksudkan forceLayoutmasuk akal. Jadi pada akhirnya nama dan dokumentasinya sangat buruk.
fluidsonic
0

invalidate()---> onDraw()dari utas UI

postInvalidate()---> onDraw()dari utas latar belakang

requestLayout()---> onMeasure()dan onLayout()DAN TIDAK onDraw()

  • PENTING : Memanggil metode ini tidak memengaruhi anak kelas yang dipanggil.

forceLayout()---> onMeasure()dan onLayout() HANYA JIKA induk langsung dipanggil requestLayout().

Ehsan Mashhadi
sumber