Sử dụng ForceLayout (), requestLayout () và không hợp lệ ()


185

Tôi là một chút nhầm lẫn về vai trò của forceLayout(), requestLayout()invalidate()phương pháp của Viewlớp.

Khi nào họ sẽ được gọi?

Câu trả lời:


357

Để hiểu rõ hơn về câu trả lời được cung cấp bởi François BOURLIEUXDalvik, tôi khuyên bạn nên xem sơ đồ vòng đời xem tuyệt vời này của Arpit Mathur : nhập mô tả hình ảnh ở đây


28
Tôi thường thấy requestLayout được gọi trực tiếp sau khi không hợp lệ được gọi, tôi thậm chí còn thấy điều đó xảy ra trong mã nguồn Android cho những thứ như TextView, nhưng theo sơ đồ này thì làm như vậy là thừa, phải không? Vì vậy, có bất kỳ mục đích để làm điều đó?
tcox

5
Đó là một câu hỏi thú vị, và thành thật mà nói tôi không thực sự biết tại sao họ gọi cả hai phương pháp trong ví dụ TextView. Tôi nghĩ rằng có lẽ họ muốn vẽ Viewcho 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 TextViewcũng). Có lẽ nó xứng đáng với một câu hỏi khác trên StackOverflow :)?
Bartek Lipinski

14
(1/2) : Tôi không nghĩ bạn hiểu đúng hai phương pháp đó ( invalidate()requestLayout()). Mục đích của các phương thức đó là để cho biết Viewloại vô hiệu (như bạn gọi nó) đã xảy ra. Nó không giống như Viewquyế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().
Bartek Lipinski

11
(2/2): Nếu bạn thay đổi kích thước của mình Viewtheo một cách nào đó, ví dụ: bạn nhận được dòng điệnLayoutParams của a Viewvà bạn sửa đổi chúng, nhưng KHÔNG gọi một trong hai requestLayouthoặc setLayoutParams(gọi requestLayouttrong nội bộ), thì bạn có thể gọi invalidate()nhiều như bạn muốn, và những Viewsẽ 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 Viewrằng kích thước của nó đã thay đổi (với một requestLayoutcuộc gọi phương thức), thì ý Viewchí sẽ cho rằng nó đã không xảy ra, onMeasureonLayoutsẽ không được gọi.
Bartek Lipinski

2
@tcox nhiều hơn tại đây -> stackoverflow.com/questions353279374/ trên
Sotti

125

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 onDrawviệ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 onMeasureonLayoutkhông chỉ cho chế độ xem này mà còn cả đường lên cho các chế độ xem chính.

Gọi requestLayout()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().

Học cao hơn


1
nhưng sau đó sẽ có ý nghĩa hơn khi gọi requestLayout () và sau đó vô hiệu hóa ()?
scholt

2
@scholt, Theo như tôi biết thì thứ tự không thành vấn đề, vì vậy bạn có thể gọi requestLayout()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.
Suragch

27

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.


3
Làm thế nào về lực lượngLayout () sau đó?
sd.us

@fiddler, phương pháp này chỉ đặt hai cờ: PFLAG_FORCE_LAYOUT và PFLAG_INVALIDATED
suitianshi

7
@suitianshi Hậu quả của việc đặt cờ là gì?
Serge

1
@Sergey Hậu quả có vẻ là cờ này ghi đè lên bộ đệm (các khung nhìn sử dụng bộ đệm để đo nhanh hơn trong tương lai cho cùng một biện pháp đo lường); xem dòng 18783 của View.java tại đây: github.com/android/pl
RhetoricalRuvim

3

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)


3

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()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.


1
Chào! Làm thế nào bạn đến với câu thần chú đó ? Đó là tài liệu ở đâu đó?
azizbekian

1
Thật không may, nó không được ghi lại và thậm chí có thể không có ý định. Tôi đã tìm ra điều đó bằng cách tự mình điều tra mã Viewvà bằng cách tự mình xử lý vấn đề & gỡ lỗi.
chất lỏng

Sau đó, tôi tò mò về mục đích của 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()?
azizbekian

@azizbekian không, bạn không cần phải làm vậy. requestLayout()làm mọi thứ mà forceLayout()cũng làm.
chất lỏng

1
@Suragch đã bỏ lỡ cái đó, cảm ơn! Phân tích rất thú vị. Mục đích sử dụng forceLayoutcó ý nghĩa. Vì vậy, cuối cùng nó chỉ được đặt tên và ghi chép rất tệ.
chất lỏng

0

invalidate()---> onDraw()từ luồng UI

postInvalidate()---> onDraw()từ chủ đề nền

requestLayout()---> onMeasure()onLayout()KHÔNG CẦN THIẾT onDraw()

  • QUAN TRỌNG : Gọi phương thức này không ảnh hưởng đến con của lớp được gọi.

forceLayout()---> onMeasure()onLayout() CHỈ NẾU cha mẹ trực tiếp gọi requestLayout().

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.