Странное поведение модальной страницы при вводе текста и отключении клавиатуры вручную (.NET MAUI Android)
Я создаю приложение для мониторинга растительности, которое впервые разрабатываю в MAUI. В формах Xamarin я не столкнулся с этой проблемой. У меня есть форма ввода данных, как показано ниже:
Когда я приступаю к вводу данных, если запись расположена значительно над виртуальной клавиатурой, она работает без каких-либо проблем. Я могу добавить данные и вручную закрыть клавиатуру.
Однако, когда у меня есть запись ниже, где отображается клавиатура, и модальная страница перемещается вверх, чтобы показать запись, я получаю странную прыгающую анимацию страницы каждый раз, когда я набираю букву, и когда я вручную закрываю клавиатуру, она представление не обновляется, но представление остается там, куда оно было перемещено, как показано на рисунках ниже:
Клавиатура перемещает модальную страницу вверх
Модальная страница не перемещается вниз после закрытия клавиатуры.
Я попытался добавить обходной пакет NuGet под названием PureWeen.Maui.FixesAndWorkarounds. Это не сработало. Я попробовал расширения клавиатуры Maui Community Toolkit, но они не сработали. Я попытался добавить следующие строки в начало xaml этой страницы, но это не сработало:
xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"
android:Application.WindowSoftInputModeAdjust="Resize"
Мой xaml выглядит так:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ElephantMonitoring.DistanceSampling.Views.DSRecordingAddEditView"
xmlns:helpers ="clr-namespace:ElephantMonitoring.HelperClasses"
xmlns:valueConverters = "clr-namespace:ElephantMonitoring.Value_Converters"
Disappearing="ContentPage_Disappearing"
xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"
android:Application.WindowSoftInputModeAdjust="Resize">
<!--VALUE CONVERTER: ERROR MESSAGE TO BOOL-->
<ContentPage.Resources>
<valueConverters:ErrorTextToBoolConverter x:Key="errorToBool"/>
<valueConverters:GPSAccuracyIndicatorValueConverter x:Key="gpsAccuracy"/>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="*"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<!--HEADER-->
<Frame Style="{StaticResource HeaderCard}" Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" HeightRequest="50" WidthRequest="50" BackgroundColor="DarkGreen" Command="{Binding BackCommand}"
HorizontalOptions="Start">
<Button.ImageSource>
<FontImageSource Glyph="{x:Static helpers:FASolid.ArrowLeft}" FontFamily="FASolid" Size="30"/>
</Button.ImageSource>
</Button>
<Label Grid.Column="1" Text="{Binding Title, FallbackValue=PAGE_TITLE}" Style="{StaticResource HeaderLabel}"
HorizontalOptions="Center"/>
<Button Grid.Column="2" HeightRequest="50" WidthRequest="50" BackgroundColor="DarkGreen" Command="{Binding AcceptCommand}"
HorizontalOptions="End">
<Button.ImageSource>
<FontImageSource Glyph="{x:Static helpers:FASolid.FloppyDisk}" FontFamily="FASolid" Size="30"/>
</Button.ImageSource>
</Button>
</Grid>
</Frame>
<!--DATA FIELDS-->
<ScrollView Grid.Row="1" Margin="0,20,0,0">
<VerticalStackLayout>
<!--Perpendicuar distance - ENTRY-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Perpendicular distance from transect (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.PerpendicularDistanceError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.PerpendicularDistanceError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.PerpendicularDistance}"
Keyboard="Numeric"/>
</VerticalStackLayout>
</Frame>
<!--Plant Location - GPS LOCATION GROUP-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Loading indicator that will be shown when the GPS is trying to get a location-->
<HorizontalStackLayout HorizontalOptions="Center" IsVisible="{Binding ShowGPSLoading}">
<ActivityIndicator Color="DarkGreen" IsRunning="True" IsVisible="True" HeightRequest="40"/>
<Label Text="Getting device location..." Style="{StaticResource InformationLabelHeader}"
VerticalTextAlignment="Center"/>
</HorizontalStackLayout>
<!--The fields-->
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<VerticalStackLayout Grid.Column="0">
<!--Start Location S - ENTRY-->
<VerticalStackLayout>
<!--Caption-->
<Label Text="Plant Location South (decimal degrees) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.PlantLocationSError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.PlantLocationSError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.PlantLocationS}"
Keyboard="Numeric"/>
</VerticalStackLayout>
<!--End Location E - ENTRY-->
<VerticalStackLayout>
<!--Caption-->
<Label Text="Plant Location East (decimal degrees) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.PlantLocationEError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.PlantLocationEError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.PlantLocationE}" Keyboard="Numeric"/>
</VerticalStackLayout>
<!--Accuracy Indicator - LABEL-->
<HorizontalStackLayout>
<!--Caption-->
<Label Text="Accuracy: " Style="{StaticResource EntryCaptionLabel}"/>
<Label Text="{Binding RecordingModel.LocationAccuracy, FallbackValue=NA}" Style="{StaticResource InformationLabelHeader}"/>
<Label Text="m" Style="{StaticResource InformationLabelHeader}"/>
<Label Text="{Binding RecordingModel.LocationAccuracy, FallbackValue=NONE, Converter={StaticResource gpsAccuracy}}"
Style="{StaticResource InformationLabelHeader}"/>
</HorizontalStackLayout>
</VerticalStackLayout>
<Button HeightRequest="50" WidthRequest="50" BackgroundColor="DarkGreen" Grid.Column="1"
Command="{Binding GetGPSLocationCommand}"
Margin="0,0,5,0">
<Button.ImageSource>
<FontImageSource Glyph="{x:Static helpers:FASolid.LocationDot}" FontFamily="FASolid" Size="30"/>
</Button.ImageSource>
</Button>
</Grid>
</VerticalStackLayout>
</Frame>
<!--Trunk Circumference - ENTRY-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Trunk circumference (m) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.TrunkCircumferenceError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.TrunkCircumferenceError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.TrunkCircumference}"
Keyboard="Numeric"/>
</VerticalStackLayout>
</Frame>
<!--Damaged trunk circumference - ENTRY-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Damaged trunk circumference (m) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.DamagedTrunkCircError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.DamagedTrunkCircError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.DamagedTrunkCirc}"
IsReadOnly="True">
<Entry.GestureRecognizers>
<TapGestureRecognizer Command="{Binding AddDamagedTrunkCommand}"/>
</Entry.GestureRecognizers>
</Entry>
</VerticalStackLayout>
</Frame>
<!--Plant height - ENTRY-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Plant Height (m) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.PlantHeightError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.PlantHeightError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.PlantHeight}"
Keyboard="Numeric"/>
</VerticalStackLayout>
</Frame>
<!--Crown Diameter - ENTRY-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Crown Diameter (m) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.CrownDiameterError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.CrownDiameterError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.CrownDiameter}"
IsReadOnly="True">
<Entry.GestureRecognizers>
<TapGestureRecognizer Command="{Binding AddCrownDiameterCommand}"/>
</Entry.GestureRecognizers>
</Entry>
</VerticalStackLayout>
</Frame>
<!--First Leaves height - ENTRY-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="First Leaves Height (m) (*)" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.FirstLeavesHeightError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.FirstLeavesHeightError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Entry Style="{StaticResource EntryBase}" Text="{Binding RecordingModel.FirstLeavesHeight}"
Keyboard="Numeric"/>
</VerticalStackLayout>
</Frame>
<!--Toppled - PICKER-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Is the tree Toppled?" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.ToppledError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.ToppledError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Picker
SelectedItem="{Binding RecordingModel.Toppled}"
TitleColor="{StaticResource PrimaryMid}"
Style="{StaticResource PickerBase}"
BackgroundColor="White">
<Picker.Items>
<x:String>YES</x:String>
<x:String>NO</x:String>
</Picker.Items>
</Picker>
</VerticalStackLayout>
</Frame>
<!--Top Kill - PICKER-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Is Top Kill evident?" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.TopKillError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.TopKillError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Picker
SelectedItem="{Binding RecordingModel.TopKill}"
TitleColor="{StaticResource PrimaryMid}"
Style="{StaticResource PickerBase}"
BackgroundColor="White">
<Picker.Items>
<x:String>YES</x:String>
<x:String>NO</x:String>
</Picker.Items>
</Picker>
</VerticalStackLayout>
</Frame>
<!--Coppiced - PICKER-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Is the tree Coppicing?" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.CoppicedError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.CoppicedError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Picker
SelectedItem="{Binding RecordingModel.Coppiced}"
TitleColor="{StaticResource PrimaryMid}"
Style="{StaticResource PickerBase}"
BackgroundColor="White">
<Picker.Items>
<x:String>YES</x:String>
<x:String>NO</x:String>
</Picker.Items>
</Picker>
</VerticalStackLayout>
</Frame>
<!--Utilization Scale - PICKER-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Utilization on scale" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.UtilizationScaleError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.UtilizationScaleError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Picker
SelectedItem="{Binding RecordingModel.UtilizationScale}"
TitleColor="{StaticResource PrimaryMid}"
Style="{StaticResource PickerBase}"
BackgroundColor="White">
<Picker.Items>
<x:String>1</x:String>
<x:String>2</x:String>
<x:String>3</x:String>
<x:String>4</x:String>
</Picker.Items>
</Picker>
</VerticalStackLayout>
</Frame>
<!--Fire Damaged - PICKER-->
<Frame Padding="2" Margin="2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Fire damage evident?" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="{Binding RecordingModel.FireDamageError, FallbackValue=False, Converter={StaticResource errorToBool}}"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding RecordingModel.FireDamageError}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Picker
SelectedItem="{Binding RecordingModel.FireDamage}"
TitleColor="{StaticResource PrimaryMid}"
Style="{StaticResource PickerBase}"
BackgroundColor="White">
<Picker.Items>
<x:String>YES</x:String>
<x:String>NO</x:String>
</Picker.Items>
</Picker>
</VerticalStackLayout>
</Frame>
<!--Notes - ENTRY-->
<Frame Padding="2" Margin="2, 2, 2, 2" BorderColor="Black">
<VerticalStackLayout>
<!--Caption-->
<Label Text="Notes" Style="{StaticResource EntryCaptionLabel}"/>
<!--Error Box-->
<Frame IsVisible="False"
Style="{StaticResource ErrorCard}">
<Label Text="{Binding SomeBinding}"
Style="{StaticResource ErrorCardLabel}"/>
</Frame>
<Editor Style="{StaticResource EditorBase}" Text="{Binding PlantSpeciesItem.ScientificName}"
Placeholder="Enter notes here" x:Name="notesEditor" Focused="notesEditor_Focused"/>
</VerticalStackLayout>
</Frame>
</VerticalStackLayout>
</ScrollView>
</Grid>
</ContentPage>
Если кто-то нашел решение этой проблемы, пожалуйста, дайте мне знать.