WebView는 레이아웃 표시 영역(페이지 크기)과 시각적 표시 영역 (사용자가 실제로 보는 페이지 부분)이라는 두 개의 표시 영역을 사용하여 콘텐츠 정렬을 관리합니다. 레이아웃 뷰포트는 일반적으로 정적이지만 사용자가 확대/축소하거나 스크롤하거나 시스템 UI 요소(예: 소프트웨어 키보드)가 표시되면 시각적 뷰포트가 동적으로 변경됩니다.
기능 호환성
창 인셋에 대한 WebView 지원은 웹 콘텐츠 동작을 네이티브 Android 앱 기대치에 맞추기 위해 시간이 지남에 따라 발전해 왔습니다.
| Milestone | 기능 추가됨 | 범위 |
|---|---|---|
| M136 | CSS safe-area-insets를 통한 displayCutout() 및 systemBars() 지원 |
전체 화면 WebView만 지원됩니다. |
| M139 | ime() (입력 방식 편집기, 즉 키보드) 지원을 통해 시각적 표시 영역 크기 조절 |
모든 WebView |
| M144 | displayCutout() 및 systemBars() 지원 |
모든 WebView (전체 화면 상태와 관계없음) |
자세한 내용은 WindowInsetsCompat을 참고하세요.
핵심 메커니즘
WebView는 두 가지 기본 메커니즘을 통해 인셋을 처리합니다.
안전한 영역 (
displayCutout,systemBars): WebView는 CSS safe-area-inset-* 변수를 통해 이러한 크기를 웹 콘텐츠에 전달합니다. 이를 통해 개발자는 노치나 상태 표시줄로 인해 자체 양방향 요소 (예: 탐색 메뉴)가 가려지지 않도록 할 수 있습니다.입력 방식 편집기 (IME)를 사용한 시각적 표시 영역 크기 조절: M139부터 입력 방식 편집기 (IME)가 시각적 표시 영역의 크기를 직접 조절합니다. 이 크기 조절 메커니즘도 WebView-Window 교차를 기반으로 합니다. 예를 들어 Android 멀티태스킹 모드에서 WebView 하단이 창 하단 아래로 200dp 확장되면 시각적 뷰포트는 WebView 크기보다 200dp 작습니다. 이 시각적 뷰포트 크기 조절 (IME 및 WebView-Window 교차의 경우 모두)은 WebView 하단에만 적용됩니다. 이 메커니즘은 왼쪽, 오른쪽 또는 상단 오버랩의 크기 조정을 지원하지 않습니다. 즉, 이러한 가장자리에 도킹된 키보드는 시각적 뷰포트 크기 조절을 트리거하지 않습니다.
이전에는 시각적 표시 영역이 고정되어 입력란이 키보드 뒤에 숨겨지는 경우가 많았습니다. 표시 영역의 크기를 조정하면 페이지의 표시되는 부분이 기본적으로 스크롤 가능해져 사용자가 가려진 콘텐츠에 도달할 수 있습니다.
경계 및 중복 로직
WebView는 시스템 UI 요소 (표시줄, 디스플레이 컷아웃 또는 키보드)가 WebView의 화면 경계와 직접 겹치는 경우에만 0이 아닌 인셋 값을 수신해야 합니다. WebView가 이러한 UI 요소와 겹치지 않는 경우 (예: WebView가 화면 중앙에 있고 시스템 표시줄을 터치하지 않는 경우) 이러한 인셋을 0으로 수신해야 합니다.
이 기본 로직을 재정의하고 중복 여부와 관계없이 완전한 시스템 크기로 웹 콘텐츠를 제공하려면 setOnApplyWindowInsetsListener 메서드를 사용하고 리스너에서 수정되지 않은 원래 windowInsets 객체를 반환하세요. 전체 시스템 크기를 제공하면 WebView의 현재 위치와 관계없이 웹 콘텐츠가 기기 하드웨어와 정렬되도록 하여 설계 일관성을 유지할 수 있습니다. 이렇게 하면 WebView가 화면 가장자리를 터치하기 위해 이동하거나 확장될 때 원활하게 전환됩니다.
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
}
자바
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;
});
크기 조절 이벤트 관리
이제 키보드 표시 여부가 시각적 표시 영역 크기 조절을 트리거하므로 웹 코드에서 크기 조절 이벤트가 더 자주 발생할 수 있습니다. 개발자는 요소 포커스를 지워 코드가 이러한 크기 조절 이벤트에 반응하지 않도록 해야 합니다. 이렇게 하면 포커스 손실과 키보드 닫힘이 반복되어 사용자가 입력할 수 없습니다.
- 사용자가 입력 요소에 포커스를 둡니다.
- 키보드가 표시되어 크기 조절 이벤트가 트리거됩니다.
- 웹사이트의 코드가 크기 조절에 대한 응답으로 포커스를 지웁니다.
- 포커스가 손실되어 키보드가 숨겨집니다.
이 동작을 완화하려면 웹 측 리스너를 검토하여 뷰포트 변경으로 인해 의도치 않게 blur() JavaScript 함수나 포커스 지우기 동작이 트리거되지 않도록 하세요.
인셋 처리 구현
WebView의 기본 설정은 대부분의 앱에서 자동으로 작동합니다. 하지만 앱에서 맞춤 레이아웃을 사용하는 경우 (예: 상태 표시줄이나 키보드를 고려하여 자체 패딩을 추가하는 경우) 다음 방법을 사용하여 웹 콘텐츠와 네이티브 UI가 함께 작동하는 방식을 개선할 수 있습니다. 네이티브 UI가 WindowInsets에 따라 컨테이너에 패딩을 적용하는 경우 WebView에 도달하기 전에 이러한 인셋을 올바르게 관리하여 이중 패딩을 방지해야 합니다.
이중 패딩은 네이티브 레이아웃과 웹 콘텐츠가 동일한 인셋 측정기준을 적용하여 중복된 간격이 발생하는 상황입니다. 예를 들어 상태 표시줄이 40px인 휴대전화를 생각해 보세요. 네이티브 뷰와 WebView 모두 40px 인셋이 표시됩니다. 두 경우 모두 패딩이 40px 추가되어 사용자에게 상단에 80px 간격이 표시됩니다.
제로화 접근 방식
이중 패딩을 방지하려면 네이티브 뷰가 패딩에 인셋 측정기준을 사용한 후 수정된 객체를 뷰 계층 구조를 통해 WebView에 전달하기 전에 새 WindowInsets 객체에서 Insets.NONE를 사용하여 해당 측정기준을 0으로 재설정해야 합니다.
상위 뷰에 패딩을 적용할 때는 일반적으로 WindowInsetsCompat.CONSUMED 대신 Insets.NONE를 설정하여 0으로 설정하는 방법을 사용해야 합니다.
WindowInsetsCompat.CONSUMED를 반환하는 것이 특정 상황에서는 작동할 수 있습니다.
하지만 앱의 핸들러가 인셋을 변경하거나 자체 패딩을 추가하면 문제가 발생할 수 있습니다. 영으로 설정하는 방식에는 이러한 제한사항이 없습니다.
인셋을 0으로 설정하여 고스트 패딩 방지
앱이 이전에 사용되지 않은 인셋을 전달한 경우 인셋을 사용하거나 인셋이 변경되면 (예: 키보드 숨김) 인셋을 사용하면 WebView가 필요한 업데이트 알림을 수신하지 못합니다. 이로 인해 WebView가 이전 상태의 고스트 패딩을 유지할 수 있습니다 (예: 키보드가 숨겨진 후에도 키보드 패딩을 유지).
다음 예는 앱과 WebView 간의 깨진 상호작용을 보여줍니다.
- 초기 상태: 앱이 처음에는 사용되지 않은 인셋 (예:
displayCutout()또는systemBars())을 WebView에 전달하며, WebView는 내부적으로 웹 콘텐츠에 패딩을 적용합니다. - 상태 변경 및 오류: 앱이 상태를 변경하고 (예: 키보드가 숨겨짐) 앱이
WindowInsetsCompat.CONSUMED를 반환하여 결과 인셋을 처리하도록 선택하는 경우 - 알림 차단됨: 인셋을 사용하면 Android 시스템이 뷰 계층 구조를 통해 WebView에 필요한 업데이트 알림을 전송하지 못합니다.
- 고스트 패딩: WebView가 업데이트를 수신하지 않으므로 이전 상태의 패딩이 유지되어 고스트 패딩이 발생합니다 (예: 키보드가 숨겨진 후에도 키보드 패딩이 유지됨).
대신 WindowInsetsCompat.Builder를 사용하여 객체를 하위 뷰에 전달하기 전에 처리된 유형을 0으로 설정하세요. 이렇게 하면 WebView에 이러한 특정 인셋이 이미 고려되었음을 알리면서 알림이 뷰 계층 구조를 계속 내려갈 수 있습니다.
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()
}
자바
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();
});
탈퇴 방법
이러한 최신 동작을 사용 중지하고 기존 뷰포트 처리로 돌아가려면 다음을 실행하세요.
인셋 가로채기:
setOnApplyWindowInsetsListener를 사용하거나WebView서브클래스에서onApplyWindowInsets를 재정의합니다.인셋 지우기: 시작부터 사용된 인셋 세트 (예:
WindowInsetsCompat.CONSUMED)를 반환합니다. 이 작업은 인셋 알림이 WebView로 전파되는 것을 완전히 방지하여 최신 시각적 표시 영역 크기 조정을 효과적으로 사용 중지하고 WebView가 초기 시각적 표시 영역 크기를 유지하도록 강제합니다.