You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI Android端ScrollView软键盘遮挡问题求助

.NET MAUI软键盘遮挡页面问题解决

问题描述

我开发了一个.NET MAUI页面,供用户输入速度与百分比,已设置输入框仅弹出数字软键盘,其余功能均正常,但ScrollView存在适配问题:当软键盘弹出时会遮挡页面,需手动关闭才能查看底部的Send按钮。请问如何开启软键盘滚动避让功能?若无法实现,是否有办法检测软键盘并调整页面尺寸?

XAML代码

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="DoradSmartphone.Views.AvatarPage"
         xmlns:model="clr-namespace:DoradSmartphone.Models"
         xmlns:viewmodel="clr-namespace:DoradSmartphone.ViewModels"
         xmlns:helpers="clr-namespace:DoradSmartphone.Helpers"
         x:DataType="viewmodel:AvatarViewModel"
         Title="{Binding Title}">

<ContentPage.Resources>
    <ResourceDictionary>
        <Style x:Key="EntryContainerStyle" TargetType="Entry">
            <Setter Property="FontSize" Value="20" />
            <Setter Property="HeightRequest" Value="48" />
        </Style>
    </ResourceDictionary>
</ContentPage.Resources>

<ContentPage.Content>
    <ScrollView VerticalOptions="FillAndExpand">
        <StackLayout x:Name="MainLayout" VerticalOptions="FillAndExpand">
            <Grid VerticalOptions="Center" HorizontalOptions="Center" RowSpacing="15">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <Image Source="{x:Static helpers:Icons.AvatarXg}" HeightRequest="300" WidthRequest="300" HorizontalOptions="Center" Grid.Row="0" />

                <Label Text="The Route Speed was 17km/h" FontSize="20" TextColor="RoyalBlue" HorizontalTextAlignment="Center" Grid.Row="1" Margin="20"/>

                <Entry x:Name="Speed" Placeholder="Type the speed" Text="{Binding Speed}" Grid.Row="2" Style="{StaticResource EntryContainerStyle}" Keyboard="Numeric">
                    <Entry.Behaviors>
                        <helpers:NumericValidationBehavior MaximumValue="30" />
                    </Entry.Behaviors>
                </Entry>

                <Entry x:Name="Percentage" Placeholder="Type the percentage of speed" Text="{Binding Percentage}" Grid.Row="3" Style="{StaticResource EntryContainerStyle}" Keyboard="Numeric">
                    <Entry.Behaviors>
                        <helpers:NumericValidationBehavior MaximumValue="100" />
                    </Entry.Behaviors>
                </Entry>

                <Label Text="{Binding NewSpeed, StringFormat='The new speed is {0} km/h'}" FontSize="20" TextColor="RoyalBlue" HorizontalTextAlignment="Center" Grid.Row="4" Margin="20"/>

                <Button Text="Send" WidthRequest="100" CornerRadius="20" HorizontalOptions="Center" 
                            Command="{Binding SetAvatarCommand}" Grid.Row="5" Margin="10" 
                            FontSize="15"/>
            </Grid>
        </StackLayout>
    </ScrollView>
</ContentPage.Content>
</ContentPage>

ViewModel代码

public partial class AvatarViewModel : BaseViewModel, INotifyPropertyChanged
{
    private double speed;
    private double routeSpeed;
    private double newSpeed;
    private double percentage;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Speed
    {
        get { return speed; }
        set
        {
            if (speed != value)
            {
                speed = value;
                OnPropertyChanged(nameof(Speed));
                UpdateNewSpeed();
            }
        }
    }

    public double RouteSpeed
    {
        get { return routeSpeed; }
        set
        {
            if (routeSpeed != value)
            {
                routeSpeed = value;
                OnPropertyChanged(nameof(RouteSpeed));
                UpdateNewSpeed();
            }
        }
    }

    public double NewSpeed
    {
        get { return newSpeed; }
        set
        {
            if (newSpeed != value)
            {
                newSpeed = value;
                OnPropertyChanged(nameof(NewSpeed));
            }
        }
    }

    public double Percentage
    {
        get { return percentage; }
        set
        {
            if (percentage != value)
            {
                percentage = value;
                OnPropertyChanged(nameof(Percentage));
                UpdateNewSpeed();
            }
        }
    }

    public AvatarViewModel()
    {
        Title = "AvatarPage";
        RouteSpeed = 17;
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private void UpdateNewSpeed()
    {
        if (percentage != 0 || percentage >= 0)
        {
            var calc = RouteSpeed + (RouteSpeed * (Percentage / 100));
            NewSpeed = double.Round(calc, 2, MidpointRounding.AwayFromZero);
        }            
    }

    public ICommand SetAvatarCommand => new Command(SetAvatar);

    private void SetAvatar()
    {
        // Perform any necessary actions when the button is clicked
    }
}

解决方案

方式一:启用软键盘自动避让(推荐)

针对不同平台配置系统级软键盘避让:

  1. Android平台
    在Platforms/Android/MainActivity.cs中设置软键盘模式:

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        // AdjustResize:调整布局大小腾出键盘空间;AdjustPan:平移页面确保焦点控件可见
        Window.SetSoftInputMode(Android.Views.SoftInput.AdjustResize);
    }
    
  2. iOS平台
    iOS默认支持自动避让,若无效可检查:

    • 确认ScrollView的VerticalOptions为FillAndExpand(你的代码已满足)
    • 在Platforms/iOS/Info.plist中添加配置:
      <key>UIViewControllerBasedStatusBarAppearance</key>
      <true/>
      

方式二:手动检测软键盘并调整布局

若自动避让失效,可通过监听软键盘事件手动调整:

  1. 在页面后台代码添加监听逻辑:

    private IKeyboardService _keyboardService;
    
    public AvatarPage(IKeyboardService keyboardService)
    {
        InitializeComponent();
        _keyboardService = keyboardService;
    }
    
    protected override void OnAppearing()
    {
        base.OnAppearing();
        _keyboardService.KeyboardShown += OnKeyboardShown;
        _keyboardService.KeyboardHidden += OnKeyboardHidden;
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        _keyboardService.KeyboardShown -= OnKeyboardShown;
        _keyboardService.KeyboardHidden -= OnKeyboardHidden;
    }
    
    private void OnKeyboardShown(object sender, KeyboardEventArgs e)
    {
        // 给主布局添加底部边距,避开软键盘
        MainLayout.Margin = new Thickness(0, 0, 0, e.KeyboardHeight);
    }
    
    private void OnKeyboardHidden(object sender, EventArgs e)
    {
        // 恢复原边距
        MainLayout.Margin = Thickness.Zero;
    }
    
  2. 实现跨平台键盘服务(以Android为例):
    创建Platforms/Android/KeyboardService.cs:

    public class KeyboardService : IKeyboardService
    {
        public event EventHandler<KeyboardEventArgs> KeyboardShown;
        public event EventHandler KeyboardHidden;
    
        public void Initialize()
        {
            var activity = Platform.CurrentActivity;
            var decorView = activity.Window.DecorView;
            decorView.ViewTreeObserver.GlobalLayout += (s, args) =>
            {
                Rect r = new Rect();
                decorView.GetWindowVisibleDisplayFrame(r);
                var screenHeight = decorView.RootView.Height;
                var keypadHeight = screenHeight - r.Bottom;
    
                // 判定软键盘是否显示(高度超过屏幕15%)
                if (keypadHeight > screenHeight * 0.15)
                {
                    KeyboardShown?.Invoke(this, new KeyboardEventArgs { KeyboardHeight = keypadHeight });
                }
                else
                {
                    KeyboardHidden?.Invoke(this, EventArgs.Empty);
                }
            };
        }
    }
    
    public interface IKeyboardService
    {
        event EventHandler<KeyboardEventArgs> KeyboardShown;
        event EventHandler KeyboardHidden;
        void Initialize();
    }
    
    public class KeyboardEventArgs : EventArgs
    {
        public int KeyboardHeight { get; set; }
    }
    
  3. 在MauiProgram.cs注册服务:

    builder.Services.AddSingleton<IKeyboardService, KeyboardService>();
    
  4. 在App启动时初始化服务:

    public App(IKeyboardService keyboardService)
    {
        InitializeComponent();
        keyboardService.Initialize();
        MainPage = new AppShell();
    }
    

额外优化建议

  • 避免给布局设置固定HeightRequest,优先使用Auto或FillAndExpand实现自适应
  • 确保ScrollView直接作为ContentPage.Content的子元素(你的代码已满足)

内容的提问来源于stack exchange,提问作者Luiz Carlos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:17:03