ฉันบิตสับสนเกี่ยวกับบทบาทของforceLayout()
, requestLayout()
และinvalidate()
วิธีการของView
ชั้น
พวกเขาจะถูกเรียกเมื่อใด
ฉันบิตสับสนเกี่ยวกับบทบาทของforceLayout()
, requestLayout()
และinvalidate()
วิธีการของView
ชั้น
พวกเขาจะถูกเรียกเมื่อใด
คำตอบ:
เพื่อให้เข้าใจคำตอบได้ดียิ่งขึ้นโดยFrançois BOURLIEUXและDalvikฉันขอแนะนำให้คุณดูแผนภาพวงจรการดูที่ยอดเยี่ยมนี้โดยArpit Mathur :
TextView
ดีว่าเป็นคำถามที่น่าสนใจและจะซื่อสัตย์ผมไม่ทราบจริงๆว่าทำไมพวกเขาเรียกวิธีการทั้งในเช่น ผมคิดว่าบางทีพวกเขาต้องการที่จะวาดView
เป็นครั้งสุดท้ายก่อนที่พวกเขาเปลี่ยนพารามิเตอร์รูปแบบที่เกี่ยวข้องกับมัน แต่มันไม่ได้โดดทำให้รู้สึกใด ๆ ถ้าเราคิดเกี่ยวกับสายที่ถูกเรียกในลำดับที่แตกต่างกัน (และพวกเขาเรียกinvalidate()
ขวาหลังจากที่requestLayout()
ในTextView
เช่นกัน) อาจจะสมควรได้รับคำถามอื่นใน StackOverflow
invalidate()
และrequestLayout()
) อย่างถูกต้อง วัตถุประสงค์ของวิธีการเหล่านั้นคือการบอกView
สิ่งที่ประเภทของการไม่ได้ (ตามที่คุณเรียกมันว่า) ได้เกิดขึ้น มันไม่เหมือนกับการView
ตัดสินใจว่าจะติดตามเส้นทางใดหลังจากคุณเรียกใช้หนึ่งในวิธีการเหล่านั้น ตรรกะที่อยู่เบื้องหลังการเลือกView
-lifecycle-path คือการเลือกวิธีการที่เหมาะสมในการเรียกตัวเอง หากมีบางสิ่งบางอย่างที่เกี่ยวข้องกับการเปลี่ยนขนาด - requestLayout()
ควรจะเรียกว่าถ้ามีเพียงการเปลี่ยนแปลงภาพโดยไม่ต้องขนาดการเปลี่ยนแปลง - invalidate()
คุณควรจะเรียก
View
ด้วยวิธีใดวิธีหนึ่งเช่นคุณได้รับLayoutParams
a View
และคุณแก้ไขได้ แต่ไม่เรียกอย่างใดอย่างหนึ่งrequestLayout
หรือsetLayoutParams
(ซึ่งเรียกrequestLayout
ภายใน) คุณสามารถโทรได้invalidate()
มากเท่าที่คุณต้องการและView
จะไม่ไปผ่านขั้นตอนการวัดแบบการจัดวางจึงจะไม่เปลี่ยนขนาดของมัน ถ้าคุณไม่บอกคุณView
ว่าขนาดของมันเปลี่ยนไป (ด้วยrequestLayout
การเรียกเมธอด) View
มันจะถือว่ามันไม่ได้และonMeasure
และonLayout
จะไม่ถูกเรียก
invalidate()
การโทรinvalidate()
เสร็จสิ้นเมื่อคุณต้องการกำหนดเวลาการวาดมุมมองใหม่ มันจะส่งผลให้onDraw
ถูกเรียกในที่สุด (เร็ว ๆ นี้ แต่ไม่ทันที) ตัวอย่างเมื่อมุมมองที่กำหนดเองจะเรียกว่าเป็นเมื่อคุณสมบัติข้อความหรือสีพื้นหลังมีการเปลี่ยนแปลง
มุมมองจะถูกวาดใหม่ แต่ขนาดจะไม่เปลี่ยนแปลง
requestLayout()
requestLayout()
หากสิ่งที่เกี่ยวกับมุมมองของคุณมีการเปลี่ยนแปลงที่จะมีผลต่อขนาดแล้วคุณควรจะเรียก สิ่งนี้จะทริกเกอร์onMeasure
และonLayout
ไม่เพียง แต่สำหรับมุมมองนี้เท่านั้น
โทรrequestLayout()
จะไม่รับประกันว่าจะมีผลในการonDraw
(ตรงกันข้ามกับสิ่งที่แผนภาพในคำตอบที่ได้รับการยอมรับนัย) invalidate()
ดังนั้นจึงมักจะถูกรวมกับ
invalidate();
requestLayout();
ตัวอย่างนี้คือเมื่อฉลากที่กำหนดเองมีการเปลี่ยนแปลงคุณสมบัติข้อความ ฉลากจะเปลี่ยนขนาดและจำเป็นต้องปรับใหม่และวาดใหม่
forceLayout()
เมื่อมีการrequestLayout()
เรียกใช้กลุ่มมุมมองพาเรนต์คุณไม่จำเป็นต้องจำใหม่และถ่ายทอดมุมมองชายด์ อย่างไรก็ตามหากเด็กควรรวมอยู่ใน remeasure และ relayout คุณสามารถโทรหาforceLayout()
เด็กได้ forceLayout()
ใช้งานได้เฉพาะกับเด็กถ้าเกิดขึ้นร่วมกับrequestLayout()
บนพาเรนต์โดยตรง การเรียกforceLayout()
ด้วยตัวเองจะไม่มีผลเนื่องจากไม่ทริกเกอร์ทrequestLayout()
รีมุมมอง
อ่านคำถาม & คำตอบนี้โดยละเอียดforceLayout()
สำหรับรายละเอียดเพิ่มเติม
View
รหัสแหล่งที่มาrequestLayout()
มาก่อนinvalidate()
ถ้าคุณต้องการ ไม่มีรูปแบบใดหรือวาดรูปทันที ค่อนข้างพวกเขาตั้งค่าสถานะที่ในที่สุดจะส่งผลให้การถ่ายทอดและวาดใหม่
ที่นี่คุณสามารถค้นหาคำตอบบางอย่าง: http://developer.android.com/guide/topics/ui/how-android-draws.html
สำหรับฉันการโทรเพื่อinvalidate()
รีเฟรชมุมมองและการโทรเพื่อrequestLayout()
รีเฟรชมุมมองและคำนวณขนาดของมุมมองบนหน้าจอ
คุณใช้โมฆะ () ในมุมมองที่คุณต้องการวาดใหม่มันจะทำให้ onDraw (Canvas c) ถูกเรียกใช้และ requestLayout () จะทำให้การเรนเดอร์เค้าโครงทั้งหมด (เฟสการวัดและเฟสการวางตำแหน่ง) ทำงานอีกครั้ง คุณควรใช้มันหากคุณเปลี่ยนขนาดของมุมมองเด็กในรันไทม์ แต่เฉพาะในบางกรณีเช่นข้อ จำกัด จากมุมมองพาเรนต์ (โดยที่ฉันหมายความว่าความสูงหรือความกว้างของพาเรนต์เป็น WRAP_CONTENT และจับคู่เด็ก ๆ
คำตอบนี้forceLayout()
ไม่ถูกต้องเกี่ยวกับ
อย่างที่คุณเห็นในรหัสของforceLayout()
มันแค่ทำเครื่องหมายว่า "ต้องการการถ่ายทอด" แต่มันไม่ได้กำหนดเวลาหรือทริกเกอร์การถ่ายทอดนั้น การถ่ายทอดผลจะไม่เกิดขึ้นจนกว่าจะถึงจุดหนึ่งในอนาคตที่ผู้ปกครองของมุมมองถูกวางไว้ด้วยเหตุผลอื่น
นอกจากนี้ยังมีปัญหาที่ใหญ่กว่าเมื่อใช้forceLayout()
และrequestLayout()
:
สมมติว่าคุณโทรforceLayout()
มาดู ตอนนี้เมื่อเรียกrequestLayout()
ใช้การสืบทอดของมุมมองนั้น Android จะโทรหาrequestLayout()
บรรพบุรุษของผู้สืบทอดนั้นซ้ำ forceLayout()
ปัญหาคือว่ามันจะหยุดการเรียกซ้ำในมุมมองที่คุณได้เรียกว่า ดังนั้นการrequestLayout()
โทรจะไม่ไปถึงรูทการดูและดังนั้นจึงไม่เคยกำหนดเวลาการส่งผ่านเลย์เอาต์ ทรีย่อยทั้งหมดของลำดับชั้นการดูกำลังรอเลย์เอาต์และการเรียกrequestLayout()
ใช้มุมมองของทรีย่อยนั้นจะไม่ทำให้เกิดเลย์เอาต์ เฉพาะการเรียกrequestLayout()
ดูมุมมองใด ๆ นอกทรีย่อยนั้นจะทำให้เวทมนต์แตก
ฉันจะพิจารณาการใช้งานforceLayout()
(และวิธีที่มันมีผลต่อrequestLayout()
การแตกและคุณไม่ควรใช้ฟังก์ชันนั้นในโค้ดของคุณ
View
และโดยพบปัญหาตัวเอง & แก้จุดบกพร่องมัน
forceLayout()
API: มันไม่จริงบังคับผ่านรูปแบบที่ค่อนข้างจะเป็นเพียงแค่การเปลี่ยนแปลงธงที่มีการตั้งข้อสังเกตในonMeasure()
แต่onMeasure()
จะไม่ถูกเรียกเว้นแต่requestLayout()
หรืออย่างชัดเจนView#measure()
เรียกว่า นั่นหมายถึงว่าควรจะจับคู่กับforceLayout()
requestLayout()
บนมืออื่น ๆ แล้วทำไมจะดำเนินการforceLayout()
ถ้าฉันยังคงต้องดำเนินการrequestLayout()
?
requestLayout()
ทำทุกอย่างที่forceLayout()
ทำด้วย
forceLayout
ทำให้สมเหตุสมผล ดังนั้นในที่สุดมันก็มีชื่อและเอกสารไม่ดีมาก
invalidate()
---> onDraw()
จากเธรด UI
postInvalidate()
---> onDraw()
จากด้ายพื้นหลัง
requestLayout()
---> onMeasure()
และonLayout()
และไม่จำเป็น onDraw()
forceLayout()
---> onMeasure()
และonLayout()
เพียงถ้าrequestLayout()
ผู้ปกครองโดยตรงเรียกว่า