Câu trả lời:
Để hiểu rõ hơn về câu trả lời được cung cấp bởi François BOURLIEUX và Dalvik, tôi khuyên bạn nên xem sơ đồ vòng đời xem tuyệt vời này của Arpit Mathur :
TextView
. Tôi nghĩ rằng có lẽ họ muốn vẽ View
cho lần cuối cùng trước khi họ thay đổi các thông số bố trí liên quan đến nó, nhưng nó không thực sự có ý nghĩa gì nếu chúng ta suy nghĩ về những cuộc gọi được gọi theo thứ tự khác nhau (và họ gọi invalidate()
ngay sau khi requestLayout()
trong TextView
cũng). Có lẽ nó xứng đáng với một câu hỏi khác trên StackOverflow :)?
invalidate()
và requestLayout()
). Mục đích của các phương thức đó là để cho biết View
loại vô hiệu (như bạn gọi nó) đã xảy ra. Nó không giống như View
quyết định đi theo con đường nào sau khi bạn gọi một trong những phương thức đó. Logic đằng sau việc chọn View
đường dẫn -lifecycle là việc chọn phương thức thích hợp để gọi chính nó. Nếu có gì đó liên quan đến thay đổi kích thước - requestLayout()
nên được gọi, nếu chỉ có thay đổi trực quan mà không thay đổi kích thước - bạn nên gọi invalidate()
.
View
theo một cách nào đó, ví dụ: bạn nhận được dòng điệnLayoutParams
của a View
và bạn sửa đổi chúng, nhưng KHÔNG gọi một trong hai requestLayout
hoặc setLayoutParams
(gọi requestLayout
trong nội bộ), thì bạn có thể gọi invalidate()
nhiều như bạn muốn, và những View
sẽ không đi qua quá trình biện pháp bố trí, do đó sẽ không thay đổi kích thước của nó. Nếu bạn không nói với bạn View
rằng kích thước của nó đã thay đổi (với một requestLayout
cuộc gọi phương thức), thì ý View
chí sẽ cho rằng nó đã không xảy ra, onMeasure
và onLayout
sẽ không được gọi.
invalidate()
Gọi invalidate()
được thực hiện khi bạn muốn lên lịch vẽ lại khung nhìn. Nó sẽ dẫn đến onDraw
việc được gọi cuối cùng (sớm, nhưng không phải ngay lập tức). Một ví dụ về thời điểm chế độ xem tùy chỉnh sẽ gọi nó là khi thuộc tính văn bản hoặc màu nền đã thay đổi.
Chế độ xem sẽ được vẽ lại nhưng kích thước sẽ không thay đổi.
requestLayout()
Nếu một cái gì đó về quan điểm của bạn thay đổi sẽ ảnh hưởng đến kích thước, thì bạn nên gọi requestLayout()
. Điều này sẽ kích hoạt onMeasure
và onLayout
không chỉ cho chế độ xem này mà còn cả đường lên cho các chế độ xem chính.
Gọi requestLayout()
là không đảm bảo kết quả trong mộtonDraw
(trái với những gì các sơ đồ trong câu trả lời được chấp nhận ngụ ý), vì vậy nó thường được kết hợp với invalidate()
.
invalidate();
requestLayout();
Một ví dụ về điều này là khi nhãn tùy chỉnh có thuộc tính văn bản thay đổi. Nhãn sẽ thay đổi kích thước và do đó cần phải được sửa chữa và vẽ lại.
forceLayout()
Khi có một requestLayout()
cái được gọi trong nhóm xem cha mẹ, không cần thiết phải hài lòng và chuyển tiếp các quan điểm con của nó. Tuy nhiên, nếu một đứa trẻ nên được đưa vào sự hài lòng và chuyển tiếp, thì bạn có thể gọi forceLayout()
cho đứa trẻ. forceLayout()
chỉ hoạt động trên một đứa trẻ nếu nó xảy ra kết hợp với mộtrequestLayout()
cha mẹ trực tiếp của nó. Gọi forceLayout()
bằng chính nó sẽ không có hiệu lực vì nó không kích hoạt requestLayout()
cây xem.
Đọc Q & A này để biết mô tả chi tiết hơn forceLayout()
.
View
mã nguồnrequestLayout()
trước invalidate()
nếu muốn. Không ai làm một bố cục hoặc vẽ ngay lập tức. Thay vào đó, họ đặt cờ cuối cùng sẽ dẫn đến chuyển tiếp và vẽ lại.
Tại đây bạn có thể tìm thấy một số phản hồi: http://developer.android.com/guide/topics/ui/how-android-draws.html
Đối với tôi, một cuộc gọi invalidate()
chỉ làm mới chế độ xem và cuộc gọi để requestLayout()
làm mới chế độ xem và tính kích thước của chế độ xem trên màn hình.
bạn sử dụng không hợp lệ () trên chế độ xem mà bạn muốn vẽ lại, nó sẽ làm cho onDraw (Canvas c) của nó được gọi và requestLayout () sẽ làm cho toàn bộ kết xuất bố cục (pha đo và pha định vị) chạy lại. Bạn nên sử dụng nó nếu bạn đang thay đổi kích thước của khung nhìn con trong thời gian chạy nhưng chỉ trong các trường hợp cụ thể như các ràng buộc từ chế độ xem cha mẹ (điều đó có nghĩa là chiều cao hoặc chiều rộng của cha mẹ là WRAP_CONTENT và do đó, đo lường các con trước khi chúng có thể quấn chúng lại)
Câu trả lời này không đúng vềforceLayout()
.
Như bạn có thể thấy trong mã củaforceLayout()
nó chỉ đánh dấu khung nhìn là "cần chuyển tiếp" nhưng nó không lên lịch cũng như không kích hoạt chuyển tiếp đó. Việc chuyển tiếp sẽ không xảy ra cho đến một lúc nào đó trong tương lai cha mẹ của quan điểm được đặt ra vì một số lý do khác.
Ngoài ra còn có một vấn đề lớn hơn nhiều khi sử dụng forceLayout()
và requestLayout()
:
Hãy nói rằng bạn đã gọi forceLayout()
trên một quan điểm. Bây giờ khi kêu gọi requestLayout()
một hậu duệ của quan điểm đó, Android sẽ gọi đệ quy requestLayout()
về tổ tiên của hậu duệ đó. Vấn đề là nó sẽ dừng đệ quy ở chế độ xem mà bạn đã gọi forceLayout()
. Vì vậy, requestLayout()
cuộc gọi sẽ không bao giờ đạt đến root view và do đó không bao giờ lên lịch vượt qua bố cục. Toàn bộ cây con của hệ thống phân cấp chế độ xem đang chờ bố cục và gọi requestLayout()
bất kỳ chế độ xem nào của cây con đó sẽ không gây ra bố cục. Chỉ gọi requestLayout()
bất kỳ chế độ xem bên ngoài cây con đó sẽ phá vỡ chính tả.
Tôi sẽ xem xét việc triển khai forceLayout()
(và ảnh hưởng của nó requestLayout()
bị phá vỡ và bạn không bao giờ nên sử dụng chức năng đó trong mã của mình.
View
và bằng cách tự mình xử lý vấn đề & gỡ lỗi.
forceLayout()
API: nó không thực sự buộc một bố cục vượt qua, thay vào đó nó chỉ thay đổi một cờ đang được quan sátonMeasure()
, nhưng onMeasure()
sẽ không được gọi trừ khi requestLayout()
hoặc một cuộc View#measure()
gọi rõ ràng được gọi. Điều đó có nghĩa là, điều đó forceLayout()
nên được kết hợp với requestLayout()
. Mặt khác, tại sao sau đó để thực hiện forceLayout()
nếu tôi vẫn cần phải thực hiện requestLayout()
?
requestLayout()
làm mọi thứ mà forceLayout()
cũng làm.
forceLayout
có ý nghĩa. Vì vậy, cuối cùng nó chỉ được đặt tên và ghi chép rất tệ.
invalidate()
---> onDraw()
từ luồng UI
postInvalidate()
---> onDraw()
từ chủ đề nền
requestLayout()
---> onMeasure()
vàonLayout()
VÀ KHÔNG CẦN THIẾT onDraw()
forceLayout()
---> onMeasure()
và onLayout()
CHỈ NẾU cha mẹ trực tiếp gọi requestLayout()
.