Fix tastiera che copre il campo di scrittura
Da Android 15 (API 35+) l'edge-to-edge e' forzato e adjustResize non ridimensiona piu' la finestra: la tastiera si limita a coprire la BlazorWebView. Con il layout rigido (html overflow:hidden, #app 100vh, page/main height:100% overflow:hidden) nemmeno il browser riesce a portare in vista il campo col focus. - MainActivity: WindowSoftInputMode AdjustResize|StateHidden e listener sugli WindowInsets che applica al content view il padding bottom pari all'inset IME (meno quello delle system bars, gia' coperto da SafeAreaEdges in MainPage), cosi' il 100vh si ricalcola. - main.js: su focusin e visualViewport resize porta il campo attivo al centro con scrollIntoView, necessario per i contenitori scrollabili. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,49 @@
|
|||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Views;
|
||||||
|
using AndroidX.Core.View;
|
||||||
|
|
||||||
namespace SteUp.Maui
|
namespace SteUp.Maui
|
||||||
{
|
{
|
||||||
[Activity(
|
[Activity(
|
||||||
Theme = "@style/Maui.SplashTheme",
|
Theme = "@style/Maui.SplashTheme",
|
||||||
MainLauncher = true,
|
MainLauncher = true,
|
||||||
|
WindowSoftInputMode = SoftInput.AdjustResize | SoftInput.StateHidden,
|
||||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
|
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
|
||||||
ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
|
ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
|
||||||
public class MainActivity : MauiAppCompatActivity
|
public class MainActivity : MauiAppCompatActivity
|
||||||
{
|
{
|
||||||
|
protected override void OnCreate(Bundle? savedInstanceState)
|
||||||
|
{
|
||||||
|
base.OnCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Da Android 15 (API 35) il edge-to-edge e' forzato e adjustResize non ridimensiona
|
||||||
|
// piu' la finestra: la tastiera si limita a coprire la WebView. Riduciamo noi il
|
||||||
|
// content view dell'altezza dell'IME cosi' il layout Blazor (100vh) si ricalcola.
|
||||||
|
if (FindViewById(Android.Resource.Id.Content) is { } content)
|
||||||
|
ViewCompat.SetOnApplyWindowInsetsListener(content, new ImeInsetsListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ImeInsetsListener : Java.Lang.Object, IOnApplyWindowInsetsListener
|
||||||
|
{
|
||||||
|
public WindowInsetsCompat OnApplyWindowInsets(Android.Views.View? v, WindowInsetsCompat? insets)
|
||||||
|
{
|
||||||
|
if (v is null || insets is null)
|
||||||
|
return insets!;
|
||||||
|
|
||||||
|
var ime = insets.GetInsets(WindowInsetsCompat.Type.Ime()).Bottom;
|
||||||
|
var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()).Bottom;
|
||||||
|
|
||||||
|
// La barra di navigazione e' gia' gestita da SafeAreaEdges in MainPage:
|
||||||
|
// teniamo solo la parte di tastiera che la eccede, per non sommare due volte.
|
||||||
|
var bottom = System.Math.Max(0, ime - systemBars);
|
||||||
|
|
||||||
|
if (v.PaddingBottom != bottom)
|
||||||
|
v.SetPadding(v.PaddingLeft, v.PaddingTop, v.PaddingRight, bottom);
|
||||||
|
|
||||||
|
return insets;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,3 +95,38 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// Tastiera: porta il campo attivo dentro l'area visibile.
|
||||||
|
// Il layout e' a 100vh con overflow:hidden, quindi quando l'host riduce la
|
||||||
|
// WebView per la tastiera il browser non scrolla da solo sul campo col focus.
|
||||||
|
(function () {
|
||||||
|
const SELECTOR = 'input, textarea, [contenteditable="true"]';
|
||||||
|
let pending = null;
|
||||||
|
|
||||||
|
function scrollFocusedIntoView() {
|
||||||
|
const el = document.activeElement;
|
||||||
|
if (!el || !el.matches || !el.matches(SELECTOR)) return;
|
||||||
|
el.scrollIntoView({block: 'center', behavior: 'smooth'});
|
||||||
|
}
|
||||||
|
|
||||||
|
function schedule() {
|
||||||
|
clearTimeout(pending);
|
||||||
|
// attende la fine dell'animazione di apertura della tastiera e del relayout
|
||||||
|
pending = setTimeout(scrollFocusedIntoView, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('focusin', function (event) {
|
||||||
|
if (event.target && event.target.matches && event.target.matches(SELECTOR)) {
|
||||||
|
schedule();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Il resize arriva quando la tastiera si apre/chiude o cambia altezza
|
||||||
|
// (es. passaggio a tastiera emoji o suggerimenti).
|
||||||
|
if (window.visualViewport) {
|
||||||
|
window.visualViewport.addEventListener('resize', schedule);
|
||||||
|
} else {
|
||||||
|
window.addEventListener('resize', schedule);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user