WebView quản lý việc căn chỉnh nội dung bằng hai khung nhìn: khung nhìn bố cục (kích thước trang) và khung nhìn trực quan (phần trang mà người dùng thực sự nhìn thấy). Mặc dù khung nhìn bố cục thường ở trạng thái tĩnh, nhưng khung nhìn hình ảnh sẽ thay đổi linh hoạt khi người dùng thu phóng, cuộn hoặc khi các phần tử trên giao diện người dùng hệ thống (chẳng hạn như bàn phím phần mềm) xuất hiện.
Khả năng tương thích của tính năng
Khả năng hỗ trợ của WebView cho phần lồng ghép cửa sổ đã phát triển theo thời gian để điều chỉnh hành vi của nội dung trên web cho phù hợp với kỳ vọng của ứng dụng Android gốc:
| Mốc quan trọng | Đã thêm tính năng | Phạm vi |
|---|---|---|
| M136 | displayCutout() và systemBars() hỗ trợ thông qua các phần lồng ghép vùng an toàn CSS. |
Chỉ WebView toàn màn hình. |
| M139 | Hỗ trợ ime() (trình chỉnh sửa phương thức nhập, tức là bàn phím) thông qua việc đổi kích thước khung nhìn hình ảnh. |
Tất cả WebView. |
| M144 | Hỗ trợ displayCutout() và systemBars(). |
Tất cả WebView (bất kể trạng thái toàn màn hình). |
Để biết thêm thông tin, hãy xem WindowInsetsCompat.
Cơ chế cốt lõi
WebView xử lý các phần lồng ghép thông qua hai cơ chế chính:
Khu vực an toàn (
displayCutout,systemBars): WebView chuyển tiếp các kích thước này đến nội dung trên web thông qua các biến CSS safe-area-inset-*. Điều này cho phép nhà phát triển ngăn các thành phần tương tác của riêng họ (như thanh điều hướng) bị che khuất bởi các vết cắt hoặc thanh trạng thái.Đổi kích thước khung nhìn trực quan bằng trình chỉnh sửa phương thức nhập (IME): Kể từ phiên bản M139, trình chỉnh sửa phương thức nhập (IME) sẽ trực tiếp đổi kích thước khung nhìn trực quan. Cơ chế đổi kích thước này cũng dựa trên giao điểm WebView-Window. Ví dụ: trong chế độ đa nhiệm của Android, nếu phần dưới cùng của WebView mở rộng 200 dp bên dưới phần dưới cùng của cửa sổ, thì khung nhìn trực quan sẽ nhỏ hơn 200 dp so với kích thước của WebView. Việc đổi kích thước khung nhìn trực quan này (cho cả IME và giao điểm WebView-Window) chỉ được áp dụng cho phần dưới cùng của WebView. Cơ chế này không hỗ trợ việc đổi kích thước cho phần chồng lấp bên trái, bên phải hoặc trên cùng. Điều này có nghĩa là bàn phím được gắn cố định xuất hiện trên các cạnh đó sẽ không kích hoạt thao tác đổi kích thước khung nhìn hình ảnh.
Trước đây, khung nhìn hình ảnh vẫn cố định, thường ẩn các trường nhập dữ liệu phía sau bàn phím. Bằng cách đổi kích thước khung nhìn, phần hiển thị của trang sẽ có thể cuộn được theo mặc định, đảm bảo người dùng có thể truy cập vào nội dung bị che khuất.
Logic về ranh giới và sự trùng lặp
WebView chỉ nên nhận các giá trị khoảng lùi khác 0 khi các phần tử trên giao diện người dùng hệ thống (thanh, vết cắt trên màn hình hoặc bàn phím) trực tiếp chồng lên ranh giới màn hình của WebView. Nếu WebView không chồng chéo với các phần tử trên giao diện người dùng này (chẳng hạn như nếu WebView nằm ở giữa màn hình và không chạm vào thanh hệ thống), thì WebView sẽ nhận được các khoảng lùi đó dưới dạng 0.
Để ghi đè logic mặc định này và cung cấp cho nội dung trên web kích thước hệ thống hoàn chỉnh bất kể có chồng chéo hay không, hãy sử dụng phương thức setOnApplyWindowInsetsListener và trả về đối tượng windowInsets ban đầu, chưa sửa đổi từ trình nghe. Việc cung cấp kích thước hệ thống hoàn chỉnh có thể giúp đảm bảo tính nhất quán trong thiết kế bằng cách cho phép nội dung trên web phù hợp với phần cứng của thiết bị, bất kể vị trí hiện tại của WebView. Điều này đảm bảo quá trình chuyển đổi diễn ra suôn sẻ khi WebView di chuyển hoặc mở rộng để chạm vào các cạnh màn hình.
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(myWebView) { _, windowInsets -> // By returning the original windowInsets object, we override the default // behavior that zeroes out system insets (like system bars or display // cutouts) when they don't directly overlap the WebView's screen bounds. windowInsets }
Java
ViewCompat.setOnApplyWindowInsetsListener(myWebView, (v, windowInsets) -> { // By returning the original windowInsets object, we override the default // behavior that zeroes out system insets (like system bars or display // cutouts) when they don't directly overlap the WebView's screen bounds. return windowInsets; });
Quản lý sự kiện đổi kích thước
Vì chế độ hiển thị bàn phím hiện kích hoạt việc đổi kích thước khung nhìn hình ảnh, nên mã web có thể thấy các sự kiện đổi kích thước thường xuyên hơn. Nhà phát triển phải đảm bảo mã của họ không phản ứng với các sự kiện đổi kích thước này bằng cách xoá tiêu điểm của phần tử. Làm như vậy sẽ tạo ra một vòng lặp mất tiêu điểm và đóng bàn phím, khiến người dùng không thể nhập dữ liệu:
- Người dùng tập trung vào một phần tử nhập dữ liệu.
- Bàn phím xuất hiện, kích hoạt sự kiện đổi kích thước.
- Mã của trang web sẽ xoá tiêu điểm để phản hồi thao tác đổi kích thước.
- Bàn phím ẩn đi vì mất tiêu điểm.
Để giảm thiểu hành vi này, hãy xem xét trình nghe phía web để đảm bảo rằng các thay đổi về khung nhìn không vô tình kích hoạt hàm JavaScript blur() hoặc hành vi xoá tiêu điểm.
Triển khai cách xử lý phần lồng ghép
Chế độ cài đặt mặc định của WebView tự động hoạt động cho hầu hết các ứng dụng. Tuy nhiên, nếu ứng dụng của bạn sử dụng bố cục tuỳ chỉnh (ví dụ: nếu bạn thêm khoảng đệm riêng để tính đến thanh trạng thái hoặc bàn phím), thì bạn có thể sử dụng các phương pháp sau để cải thiện cách nội dung web và giao diện người dùng gốc hoạt động cùng nhau. Nếu giao diện người dùng gốc của bạn áp dụng khoảng đệm cho vùng chứa dựa trên WindowInsets, thì bạn phải quản lý các phần lồng ghép này một cách chính xác trước khi chúng đến WebView để tránh khoảng đệm kép.
Khoảng đệm kép là trường hợp bố cục gốc và nội dung trên web áp dụng cùng một kích thước lồng ghép, dẫn đến khoảng cách thừa. Ví dụ: hãy tưởng tượng một chiếc điện thoại có thanh trạng thái 40px. Cả chế độ xem gốc và WebView đều thấy phần lồng ghép 40px. Cả hai đều thêm khoảng đệm 40px, dẫn đến việc người dùng thấy khoảng trống 80px ở trên cùng.
Phương pháp Zeroing
Để ngăn khoảng đệm kép, bạn phải đảm bảo rằng sau khi một khung hiển thị gốc sử dụng kích thước khoảng lùi cho khoảng đệm, bạn sẽ đặt lại kích thước đó về 0 bằng cách sử dụng Insets.NONE trên một đối tượng WindowInsets mới trước khi truyền đối tượng đã sửa đổi xuống hệ phân cấp view đến WebView.
Khi áp dụng khoảng đệm cho một thành phần hiển thị gốc, thường thì bạn nên sử dụng phương pháp đặt về 0 bằng cách đặt Insets.NONE thay vì WindowInsetsCompat.CONSUMED.
Việc trả về WindowInsetsCompat.CONSUMED có thể hiệu quả trong một số trường hợp.
Tuy nhiên, bạn có thể gặp vấn đề nếu trình xử lý của ứng dụng thay đổi phần lồng ghép hoặc thêm khoảng đệm riêng. Phương pháp đặt về 0 không có những hạn chế này.
Tránh khoảng đệm ảo bằng cách đặt các phần lồng ghép về 0
Nếu bạn sử dụng các phần lồng ghép khi ứng dụng đã truyền các phần lồng ghép chưa sử dụng trước đó hoặc nếu các phần lồng ghép thay đổi (như ẩn bàn phím), thì việc sử dụng các phần lồng ghép sẽ ngăn WebView nhận thông báo cập nhật cần thiết. Điều này có thể khiến WebView giữ lại khoảng đệm ảo từ trạng thái trước đó (ví dụ: giữ khoảng đệm bàn phím sau khi bàn phím bị ẩn).
Ví dụ sau đây cho thấy một hoạt động tương tác bị hỏng giữa ứng dụng và WebView:
- Trạng thái ban đầu: Ban đầu, ứng dụng truyền các phần lồng ghép chưa sử dụng (ví dụ:
displayCutout()hoặcsystemBars()) đến WebView. WebView sẽ áp dụng khoảng đệm cho nội dung trên web trong nội bộ. - Thay đổi trạng thái và lỗi: Nếu ứng dụng thay đổi trạng thái (ví dụ: bàn phím ẩn) và ứng dụng chọn xử lý các phần lồng ghép thu được bằng cách trả về
WindowInsetsCompat.CONSUMED. - Thông báo bị chặn: Việc sử dụng các khoảng lùi sẽ ngăn hệ thống Android gửi thông báo cập nhật cần thiết xuống hệ phân cấp view đến WebView.
- Khoảng đệm ảo: Vì WebView không nhận được thông tin cập nhật, nên WebView sẽ giữ lại khoảng đệm từ trạng thái trước đó, gây ra khoảng đệm ảo (ví dụ: giữ lại khoảng đệm bàn phím sau khi bàn phím bị ẩn).
Thay vào đó, hãy sử dụng WindowInsetsCompat.Builder để đặt các loại được xử lý thành 0 trước khi truyền đối tượng đến các chế độ xem dữ liệu trẻ em. Thao tác này sẽ thông báo cho WebView rằng những khoảng lùi cụ thể đó đã được tính đến trong khi cho phép thông báo tiếp tục đi xuống hệ phân cấp view.
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, windowInsets -> // 1. Identify the inset types you want to handle natively val types = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() // 2. Extract the dimensions and apply them as padding to the native container val insets = windowInsets.getInsets(types) view.setPadding(insets.left, insets.top, insets.right, insets.bottom) // 3. Return a new WindowInsets object with the handled types set to NONE (zeroed). // This informs the WebView that these areas are already padded, preventing // double-padding while still allowing the WebView to update its internal state. WindowInsetsCompat.Builder(windowInsets) .setInsets(types, Insets.NONE) .build() }
Java
ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> { // 1. Identify the inset types you want to handle natively int types = WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(); // 2. Extract the dimensions and apply them as padding to the native container Insets insets = windowInsets.getInsets(types); rootView.setPadding(insets.left, insets.top, insets.right, insets.bottom); // 3. Return a new Insets object with the handled types set to NONE (zeroed). // This informs the WebView that these areas are already padded, preventing // double-padding while still allowing the WebView to update its internal // state. return new WindowInsetsCompat.Builder(windowInsets) .setInsets(types, Insets.NONE) .build(); });
Cách chọn không tham gia
Để tắt các hành vi hiện đại này và quay lại cách xử lý khung nhìn cũ, hãy làm như sau:
Chặn phần lồng ghép: Sử dụng
setOnApplyWindowInsetsListenerhoặc ghi đèonApplyWindowInsetstrong một lớp conWebView.Xoá phần lồng ghép: Trả về một tập hợp phần lồng ghép đã sử dụng (ví dụ:
WindowInsetsCompat.CONSUMED) ngay từ đầu. Thao tác này ngăn thông báo về phần lồng ghép truyền đến WebView, vô hiệu hoá hiệu quả việc đổi kích thước khung nhìn hiện đại và buộc WebView giữ lại kích thước khung nhìn trực quan ban đầu.