WebView quản lý việc căn chỉnh nội dung bằng cách sử dụng 2 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 tĩnh, nhưng khung nhìn trực quan sẽ thay đổi linh hoạt khi người dùng thu phóng, cuộn hoặc khi các phần tử 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ợ phần lồng ghép cửa sổ của WebView đã phát triển theo thời gian để điều chỉnh hành vi của nội dung web cho phù hợp với kỳ vọng của ứng dụng Android gốc:
| Milestone | Đã thêm tính năng | Phạm vi |
|---|---|---|
| M136 | displayCutout() và systemBars() hỗ trợ thông qua CSS safe-area-insets. |
Chỉ có WebView toàn màn hình. |
| M139 | Hỗ trợ ime() (trình chỉnh sửa phương thức nhập, là một bàn phím) thông qua việc đổi kích thước khung hiển thị trực quan. |
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ý phần lồng ghép thông qua 2 cơ chế chính:
Khu vực an toàn (
displayCutout,systemBars): WebView chuyển tiếp các phương diện này đến nội dung 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 phần tử tương tác của riêng họ (chẳng hạn như thanh điều hướng) bị che khuất bởi vết cắt hoặc thanh trạng thái.Thay đổi kích thước khung hiển thị trực quan bằng trình chỉnh sửa phương thức nhập (IME): Kể từ M139, trình chỉnh sửa phương thức nhập (IME) sẽ trực tiếp thay đổi kích thước khung hiển thị 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 hiển thị trực quan sẽ nhỏ hơn 200 dp so với kích thước của WebView. Việc thay đổi kích thước khung hiển thị trực quan này (cho cả IME và giao điểm WebView-Window) chỉ được áp dụng cho cuối WebView. Cơ chế này không hỗ trợ đổi kích thước cho phần chồng chéo 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 ở chế độ cố định xuất hiện ở những cạnh đó sẽ không kích hoạt thao tác đổi kích thước khung hiển thị trực quan.
Trước đây, khung nhìn trực quan vẫn cố định, thường ẩn các trường nhập liệu phía sau bàn phím. Bằng cách đổi kích thước khung hiển thị, phần có thể nhìn thấy của trang sẽ có thể cuộn 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ỉ nhận được các giá trị phần lồng ghép khác 0 khi các phần tử 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) chồng lấp trực tiếp với ranh giới màn hình của WebView. Nếu WebView không trùng lặp với các phần tử 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 các thanh hệ thống), thì WebView đó sẽ nhận được các phần lồng ghép đó dưới dạng 0.
Để ghi đè logic mặc định này và cung cấp cho nội dung web kích thước hệ thống hoàn chỉnh bất kể có trùng lặp hay không, hãy sử dụng phương thức setOnApplyWindowInsetsListener và trả về đối tượng windowInsets ban đầu, chưa được sửa đổi từ trình nghe. Việc cung cấp đầy đủ các kích thước hệ thống 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 web căn chỉnh 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ý các sự kiện đổi kích thước
Vì chế độ hiển thị bàn phím hiện kích hoạt thao tác đổi kích thước khung hiển thị trực quan, 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 những sự kiện thay đổ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à loại bỏ bàn phím, ngăn chặn hoạt động đầu vào của người dùng:
- Người dùng tập trung vào một phần tử đầu vào.
- 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 yêu cầu đổi kích thước.
- Bàn phím ẩn vì tiêu điểm bị mất.
Để giảm thiểu hành vi này, hãy xem xét các trình nghe phía web để đảm bảo rằng các thay đổi về khung hiển thị không vô tình kích hoạt hàm JavaScript blur() hoặc các hành vi xoá tiêu điểm.
Triển khai tính năng xử lý phần lồng ghép
Chế độ cài đặt mặc định của WebView sẽ tự động hoạt động đối với 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 của riêng mình để tính đến thanh trạng thái hoặc bàn phím), 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 một vùng chứa dựa trên WindowInsets, 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 kích thước phần lồng ghép, dẫn đến khoảng cách dư thừa. Ví dụ: giả sử một chiếc điện thoại có thanh trạng thái 40 px. Cả khung hiển thị gốc và WebView đều thấy phần lồng ghép 40px. Cả hai đều thêm 40 px khoảng đệm, dẫn đến việc người dùng nhìn thấy khoảng trống 80 px ở trên cùng.
Phương pháp Đặt lại
Để ngăn chặn việc đệm gấp đôi, bạn phải đảm bảo rằng sau khi khung hiển thị gốc sử dụng một phương diện lồng ghép để đệm, bạn sẽ đặt lại phương diện đó 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 khung hiển thị đến WebView.
Khi áp dụng khoảng đệm cho một khung hiển thị gốc, bạn thường nên sử dụng phương pháp đặt lại bằng cách thiết lập Insets.NONE thay vì WindowInsetsCompat.CONSUMED.
Việc trả về WindowInsetsCompat.CONSUMED có thể hoạt động trong một số trường hợp.
Tuy nhiên, có thể xảy ra 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 lại về 0 không có những hạn chế này.
Tránh khoảng đệm ảo bằng cách đặt phần lồng ghép về 0
Nếu bạn sử dụng phần lồng ghép khi ứng dụng đã truyền các phần lồng ghép chưa được sử dụng trước đó hoặc nếu phần lồng ghép thay đổi (chẳng hạn như bàn phím ẩn), thì việc sử dụng các phần lồng ghép sẽ ngăn WebView nhận được 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ị gián đoạn giữa ứng dụng và WebView:
- Trạng thái ban đầu: Ban đầu, ứng dụng sẽ truyền các phần lồng ghép chưa dùng (ví dụ:
displayCutout()hoặcsystemBars()) đến WebView, trong đó áp dụng khoảng đệm cho nội dung web. - 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ý phần lồng ghép kết quả bằng cách trả về
WindowInsetsCompat.CONSUMED. - Thông báo bị chặn: Việc sử dụng phần lồng ghép 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 khung hiển thị đến WebView.
- Khoảng đệm ảo: Vì WebView không nhận được bản cập nhật, nên nó vẫn 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 khung hiển thị con. Thao tác này thông báo cho WebView rằng những phần lồng ghép 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 khung hiển thị.
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 chế độ xử lý khung hiển thị cũ, hãy làm như sau:
Phần lồng ghép chặn: Sử dụng
setOnApplyWindowInsetsListenerhoặc ghi đèonApplyWindowInsetstrong một lớp conWebView.Xoá phần lồng ghép: Trả về một nhóm phần lồng ghép đã dùng (ví dụ:
WindowInsetsCompat.CONSUMED) từ đầu. Thao tác này ngăn thông báo lồng lan truyền đến WebView, từ đó 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.