การใช้ forceLayout (), requestLayout () และ invalidate ()


185

ฉันบิตสับสนเกี่ยวกับบทบาทของforceLayout(), requestLayout()และinvalidate()วิธีการของViewชั้น

พวกเขาจะถูกเรียกเมื่อใด

คำตอบ:


357

เพื่อให้เข้าใจคำตอบได้ดียิ่งขึ้นโดยFrançois BOURLIEUXและDalvikฉันขอแนะนำให้คุณดูแผนภาพวงจรการดูที่ยอดเยี่ยมนี้โดยArpit Mathur : ป้อนคำอธิบายรูปภาพที่นี่


28
ฉันมักจะเห็นคำร้องขอการเรียกใช้โดยตรงหลังจากการเรียกใช้ไม่ถูกต้องฉันยังเห็นว่าเกิดขึ้นในซอร์สโค้ด Android สำหรับสิ่งต่าง ๆ เช่น TextView แต่ตามแผนภาพนี้การทำซ้ำซ้อนใช่ไหม? ดังนั้นมีจุดประสงค์ในการทำเช่นนั้น?
tcox

5
TextViewดีว่าเป็นคำถามที่น่าสนใจและจะซื่อสัตย์ผมไม่ทราบจริงๆว่าทำไมพวกเขาเรียกวิธีการทั้งในเช่น ผมคิดว่าบางทีพวกเขาต้องการที่จะวาดViewเป็นครั้งสุดท้ายก่อนที่พวกเขาเปลี่ยนพารามิเตอร์รูปแบบที่เกี่ยวข้องกับมัน แต่มันไม่ได้โดดทำให้รู้สึกใด ๆ ถ้าเราคิดเกี่ยวกับสายที่ถูกเรียกในลำดับที่แตกต่างกัน (และพวกเขาเรียกinvalidate()ขวาหลังจากที่requestLayout()ในTextViewเช่นกัน) อาจจะสมควรได้รับคำถามอื่นใน StackOverflow
Bartek Lipinski

14
(1/2) : ฉันไม่คิดว่าคุณเข้าใจสองวิธี ( invalidate()และrequestLayout()) อย่างถูกต้อง วัตถุประสงค์ของวิธีการเหล่านั้นคือการบอกViewสิ่งที่ประเภทของการไม่ได้ (ตามที่คุณเรียกมันว่า) ได้เกิดขึ้น มันไม่เหมือนกับการViewตัดสินใจว่าจะติดตามเส้นทางใดหลังจากคุณเรียกใช้หนึ่งในวิธีการเหล่านั้น ตรรกะที่อยู่เบื้องหลังการเลือกView-lifecycle-path คือการเลือกวิธีการที่เหมาะสมในการเรียกตัวเอง หากมีบางสิ่งบางอย่างที่เกี่ยวข้องกับการเปลี่ยนขนาด - requestLayout()ควรจะเรียกว่าถ้ามีเพียงการเปลี่ยนแปลงภาพโดยไม่ต้องขนาดการเปลี่ยนแปลง - invalidate()คุณควรจะเรียก
Bartek Lipinski

11
(2/2):หากคุณเปลี่ยนขนาดของคุณViewด้วยวิธีใดวิธีหนึ่งเช่นคุณได้รับLayoutParams a Viewและคุณแก้ไขได้ แต่ไม่เรียกอย่างใดอย่างหนึ่งrequestLayoutหรือsetLayoutParams(ซึ่งเรียกrequestLayoutภายใน) คุณสามารถโทรได้invalidate()มากเท่าที่คุณต้องการและViewจะไม่ไปผ่านขั้นตอนการวัดแบบการจัดวางจึงจะไม่เปลี่ยนขนาดของมัน ถ้าคุณไม่บอกคุณViewว่าขนาดของมันเปลี่ยนไป (ด้วยrequestLayoutการเรียกเมธอด) Viewมันจะถือว่ามันไม่ได้และonMeasureและonLayoutจะไม่ถูกเรียก
Bartek Lipinski

2
@tcox เพิ่มเติมที่นี่ -> stackoverflow.com/questions/35279374/…
Sotti

125

invalidate()

การโทรinvalidate()เสร็จสิ้นเมื่อคุณต้องการกำหนดเวลาการวาดมุมมองใหม่ มันจะส่งผลให้onDrawถูกเรียกในที่สุด (เร็ว ๆ นี้ แต่ไม่ทันที) ตัวอย่างเมื่อมุมมองที่กำหนดเองจะเรียกว่าเป็นเมื่อคุณสมบัติข้อความหรือสีพื้นหลังมีการเปลี่ยนแปลง

มุมมองจะถูกวาดใหม่ แต่ขนาดจะไม่เปลี่ยนแปลง

requestLayout()

requestLayout()หากสิ่งที่เกี่ยวกับมุมมองของคุณมีการเปลี่ยนแปลงที่จะมีผลต่อขนาดแล้วคุณควรจะเรียก สิ่งนี้จะทริกเกอร์onMeasureและonLayoutไม่เพียง แต่สำหรับมุมมองนี้เท่านั้น

โทรrequestLayout()จะไม่รับประกันว่าจะมีผลในการonDraw (ตรงกันข้ามกับสิ่งที่แผนภาพในคำตอบที่ได้รับการยอมรับนัย) invalidate()ดังนั้นจึงมักจะถูกรวมกับ

invalidate();
requestLayout();

ตัวอย่างนี้คือเมื่อฉลากที่กำหนดเองมีการเปลี่ยนแปลงคุณสมบัติข้อความ ฉลากจะเปลี่ยนขนาดและจำเป็นต้องปรับใหม่และวาดใหม่

forceLayout()

เมื่อมีการrequestLayout()เรียกใช้กลุ่มมุมมองพาเรนต์คุณไม่จำเป็นต้องจำใหม่และถ่ายทอดมุมมองชายด์ อย่างไรก็ตามหากเด็กควรรวมอยู่ใน remeasure และ relayout คุณสามารถโทรหาforceLayout()เด็กได้ forceLayout()ใช้งานได้เฉพาะกับเด็กถ้าเกิดขึ้นร่วมกับrequestLayout()บนพาเรนต์โดยตรง การเรียกforceLayout()ด้วยตัวเองจะไม่มีผลเนื่องจากไม่ทริกเกอร์ทrequestLayout()รีมุมมอง

อ่านคำถาม & คำตอบนี้โดยละเอียดforceLayout()สำหรับรายละเอียดเพิ่มเติม

การศึกษาเพิ่มเติม


1
แต่จะไม่เหมาะสมกว่าหรือไม่หากการร้องขอแรกที่เรียกว่าเลย์เอาต์ () แล้วทำให้ใช้งานไม่ได้ ()
scholt

2
@ scholt เท่าที่ฉันรู้ลำดับไม่สำคัญดังนั้นคุณสามารถโทรrequestLayout()มาก่อนinvalidate()ถ้าคุณต้องการ ไม่มีรูปแบบใดหรือวาดรูปทันที ค่อนข้างพวกเขาตั้งค่าสถานะที่ในที่สุดจะส่งผลให้การถ่ายทอดและวาดใหม่
Suragch

27

ที่นี่คุณสามารถค้นหาคำตอบบางอย่าง: http://developer.android.com/guide/topics/ui/how-android-draws.html

สำหรับฉันการโทรเพื่อinvalidate()รีเฟรชมุมมองและการโทรเพื่อrequestLayout()รีเฟรชมุมมองและคำนวณขนาดของมุมมองบนหน้าจอ


3
แล้ว forceLayout () ล่ะ?
sdabet

@fiddler วิธีนี้จะตั้งค่าสองสถานะ: PFLAG_FORCE_LAYOUT และ PFLAG_INVALIDATED
suitianshi

7
@suitianshi อะไรคือผลที่ตามมาของการตั้งค่าสถานะแล้ว?
Sergey

1
@Sergey ผลที่ตามมาดูเหมือนว่าธงนี้จะแทนที่การวัดแคช (มุมมองใช้แคชเพื่อวัดได้เร็วขึ้นในอนาคตสำหรับ MeasureSpec เดียวกัน) ดูบรรทัด 18783 ของ View.java ที่นี่: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim

3

คุณใช้โมฆะ () ในมุมมองที่คุณต้องการวาดใหม่มันจะทำให้ onDraw (Canvas c) ถูกเรียกใช้และ requestLayout () จะทำให้การเรนเดอร์เค้าโครงทั้งหมด (เฟสการวัดและเฟสการวางตำแหน่ง) ทำงานอีกครั้ง คุณควรใช้มันหากคุณเปลี่ยนขนาดของมุมมองเด็กในรันไทม์ แต่เฉพาะในบางกรณีเช่นข้อ จำกัด จากมุมมองพาเรนต์ (โดยที่ฉันหมายความว่าความสูงหรือความกว้างของพาเรนต์เป็น WRAP_CONTENT และจับคู่เด็ก ๆ


3

คำตอบนี้forceLayout()ไม่ถูกต้องเกี่ยวกับ

อย่างที่คุณเห็นในรหัสของforceLayout()มันแค่ทำเครื่องหมายว่า "ต้องการการถ่ายทอด" แต่มันไม่ได้กำหนดเวลาหรือทริกเกอร์การถ่ายทอดนั้น การถ่ายทอดผลจะไม่เกิดขึ้นจนกว่าจะถึงจุดหนึ่งในอนาคตที่ผู้ปกครองของมุมมองถูกวางไว้ด้วยเหตุผลอื่น

นอกจากนี้ยังมีปัญหาที่ใหญ่กว่าเมื่อใช้forceLayout()และrequestLayout():

สมมติว่าคุณโทรforceLayout()มาดู ตอนนี้เมื่อเรียกrequestLayout()ใช้การสืบทอดของมุมมองนั้น Android จะโทรหาrequestLayout()บรรพบุรุษของผู้สืบทอดนั้นซ้ำ forceLayout()ปัญหาคือว่ามันจะหยุดการเรียกซ้ำในมุมมองที่คุณได้เรียกว่า ดังนั้นการrequestLayout()โทรจะไม่ไปถึงรูทการดูและดังนั้นจึงไม่เคยกำหนดเวลาการส่งผ่านเลย์เอาต์ ทรีย่อยทั้งหมดของลำดับชั้นการดูกำลังรอเลย์เอาต์และการเรียกrequestLayout()ใช้มุมมองของทรีย่อยนั้นจะไม่ทำให้เกิดเลย์เอาต์ เฉพาะการเรียกrequestLayout()ดูมุมมองใด ๆ นอกทรีย่อยนั้นจะทำให้เวทมนต์แตก

ฉันจะพิจารณาการใช้งานforceLayout()(และวิธีที่มันมีผลต่อrequestLayout()การแตกและคุณไม่ควรใช้ฟังก์ชันนั้นในโค้ดของคุณ


1
Hi! คุณคิดคาถานั้นได้อย่างไร? เอกสารนั้นอยู่ที่ไหนสักแห่ง?
azizbekian

1
น่าเสียดายที่มันไม่ได้บันทึกและมีแนวโน้มที่จะไม่ได้ตั้งใจ ฉันคิดว่าออกโดยตรวจสอบรหัสViewและโดยพบปัญหาตัวเอง & แก้จุดบกพร่องมัน
fluidsonic

แล้วผมอยากรู้เกี่ยวกับวัตถุประสงค์ของforceLayout()API: มันไม่จริงบังคับผ่านรูปแบบที่ค่อนข้างจะเป็นเพียงแค่การเปลี่ยนแปลงธงที่มีการตั้งข้อสังเกตในonMeasure()แต่onMeasure()จะไม่ถูกเรียกเว้นแต่requestLayout()หรืออย่างชัดเจนView#measure()เรียกว่า นั่นหมายถึงว่าควรจะจับคู่กับforceLayout() requestLayout()บนมืออื่น ๆ แล้วทำไมจะดำเนินการforceLayout()ถ้าฉันยังคงต้องดำเนินการrequestLayout()?
azizbekian

@azizbekian ไม่คุณไม่จำเป็นต้องทำ requestLayout()ทำทุกอย่างที่forceLayout()ทำด้วย
ของไหล

1
@Suragch พลาดนั่นขอบคุณมาก! การวิเคราะห์ที่น่าสนใจมาก การใช้ที่ตั้งใจforceLayoutทำให้สมเหตุสมผล ดังนั้นในที่สุดมันก็มีชื่อและเอกสารไม่ดีมาก
fluidsonic

0

invalidate()---> onDraw()จากเธรด UI

postInvalidate()---> onDraw()จากด้ายพื้นหลัง

requestLayout()---> onMeasure()และonLayout()และไม่จำเป็น onDraw()

  • สำคัญ : การเรียกใช้วิธีนี้ไม่ส่งผลกระทบต่อลูกของคลาสที่เรียกว่า

forceLayout()---> onMeasure()และonLayout() เพียงถ้าrequestLayout()ผู้ปกครองโดยตรงเรียกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.