.NET MAUI Picker在Android 7.1上选中项后点击别处反复弹出
.NET MAUI Picker在Android 7.1上选中后点击外部反复弹出的解决方案
问题场景
使用.NET MAUI Picker时,在Android 7.1系统上出现异常:选中选项后点击屏幕其他区域,Picker弹窗会反复弹出;但在最新版本Android系统上运行正常。相关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" Shell.NavBarIsVisible="false" x:Class="Views.Picker"> <ContentPage.Content > <VerticalStackLayout Margin="25, 15"> <Label Text="Device Picker" HorizontalTextAlignment="Center"/> <Grid> <Picker Grid.Column="0" SelectedItem="Dev" Margin="0,15,0,15" WidthRequest="200" HeightRequest="40"> <Picker.Items> <x:String>Dev</x:String> <x:String>Testing</x:String> <x:String>Training</x:String> <x:String>Production</x:String> </Picker.Items> </Picker> <Image Source="small_down_arrow.png" Grid.Column="1" x:Name="devicePicker" WidthRequest="15" HeightRequest="15" HorizontalOptions="End" Margin="0,0,6,0" > </Image> </Grid> </VerticalStackLayout> </ContentPage.Content> </ContentPage>
问题现象:
可行解决方案
方案一:自定义Android平台Picker渲染器
通过重写Android平台的Picker渲染逻辑,替换原生弹窗实现,避免低版本系统的兼容性问题。
- 在共享项目中定义自定义Picker控件:
namespace YourAppName.Controls { public class CustomPicker : Picker { } }
- 在Android项目中添加自定义渲染器:
using Android.App; using Android.Content; using Android.Widget; using YourAppName.Controls; using YourAppName.Platforms.Android.Renderers; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.Controls.Platform.Android; using Microsoft.Maui.Platform; [assembly: ExportRenderer(typeof(CustomPicker), typeof(CustomPickerRenderer))] namespace YourAppName.Platforms.Android.Renderers { public class CustomPickerRenderer : PickerRenderer { private AlertDialog _dialog; public CustomPickerRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Picker> e) { base.OnElementChanged(e); if (Control != null && e.NewElement != null) { Control.Click += Control_Click; } } private void Control_Click(object sender, EventArgs e) { var picker = Element as CustomPicker; if (picker == null) return; var adapter = new ArrayAdapter(Context, Android.Resource.Layout.SimpleSpinnerDropDownItem, picker.Items); var dialog = new AlertDialog.Builder(Context) .SetTitle(picker.Title ?? "") .SetAdapter(adapter, (s, args) => { picker.SelectedIndex = args.Which; _dialog?.Dismiss(); }) .SetNegativeButton("取消", (s, args) => { _dialog?.Dismiss(); }) .Create(); _dialog = dialog; dialog.Show(); } protected override void Dispose(bool disposing) { if (Control != null) { Control.Click -= Control_Click; } _dialog?.Dispose(); base.Dispose(disposing); } } }
- 在XAML中替换原Picker为自定义控件:
<?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" xmlns:controls="clr-namespace:YourAppName.Controls" Shell.NavBarIsVisible="false" x:Class="Views.Picker"> <ContentPage.Content > <VerticalStackLayout Margin="25, 15"> <Label Text="Device Picker" HorizontalTextAlignment="Center"/> <Grid> <controls:CustomPicker Grid.Column="0" SelectedItem="Dev" Margin="0,15,0,15" WidthRequest="200" HeightRequest="40"> <controls:CustomPicker.Items> <x:String>Dev</x:String> <x:String>Testing</x:String> <x:String>Training</x:String> <x:String>Production</x:String> </controls:CustomPicker.Items> </controls:CustomPicker> <Image Source="small_down_arrow.png" Grid.Column="1" WidthRequest="15" HeightRequest="15" HorizontalOptions="End" Margin="0,0,6,0" > </Image> </Grid> </VerticalStackLayout> </ContentPage.Content> </ContentPage>
方案二:使用MAUI Popup替代原生Picker弹窗
借助CommunityToolkit.Maui的Popup组件,实现完全自定义的下拉选择界面,彻底规避原生Picker的兼容性问题,同时支持更多UI定制。
安装
CommunityToolkit.MauiNuGet包。创建Popup页面:
<?xml version="1.0" encoding="utf-8" ?> <toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" x:Class="YourAppName.Views.PickerPopup" Size="200, 300"> <VerticalStackLayout> <Label Text="选择环境" Margin="10"/> <CollectionView x:Name="ItemsCollection" Margin="5" SelectionMode="Single"> <CollectionView.ItemTemplate> <DataTemplate> <Label Text="{Binding}" Padding="10" FontSize="16"/> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> <Button Text="确定" Clicked="OnConfirmClicked" Margin="10"/> </VerticalStackLayout> </toolkit:Popup>
- Popup页面后台代码:
using CommunityToolkit.Maui.Views; using Microsoft.Maui.Controls; namespace YourAppName.Views { public partial class PickerPopup : Popup { public string SelectedItem { get; private set; } public PickerPopup(List<string> items, string currentSelected) { InitializeComponent(); ItemsCollection.ItemsSource = items; ItemsCollection.SelectedItem = currentSelected; } private void OnConfirmClicked(object sender, EventArgs e) { SelectedItem = ItemsCollection.SelectedItem as string; Close(SelectedItem); } } }
- 修改原页面,用Button触发Popup:
<?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" xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" x:Class="Views.Picker" Shell.NavBarIsVisible="false"> <ContentPage.Content > <VerticalStackLayout Margin="25, 15"> <Label Text="Device Picker" HorizontalTextAlignment="Center"/> <Grid> <Button x:Name="PickerButton" Text="{Binding SelectedEnv}" Margin="0,15,0,15" WidthRequest="200" HeightRequest="40" Clicked="PickerButton_Clicked"/> <Image Source="small_down_arrow.png" Grid.Column="1" WidthRequest="15" HeightRequest="15" HorizontalOptions="End" Margin="0,0,6,0" InputTransparent="True" > </Image> </Grid> </VerticalStackLayout> </ContentPage.Content> </ContentPage>
- 原页面后台代码:
using CommunityToolkit.Maui.Views; using Microsoft.Maui.Controls; namespace YourAppName.Views { public partial class Picker : ContentPage { public string SelectedEnv { get; set; } = "Dev"; private readonly List<string> _envItems = new List<string> { "Dev", "Testing", "Training", "Production" }; public Picker() { InitializeComponent(); BindingContext = this; } private async void PickerButton_Clicked(object sender, EventArgs e) { var popup = new PickerPopup(_envItems, SelectedEnv); var result = await this.ShowPopupAsync(popup); if (result is string selected) { SelectedEnv = selected; OnPropertyChanged(nameof(SelectedEnv)); } } } }
方案选择建议
- 若希望保留原生Picker的样式和交互逻辑,优先选择自定义渲染器方案。
- 若需要自定义下拉界面样式,或对交互逻辑有更多控制需求,推荐使用Popup替代方案。
内容的提问来源于stack exchange,提问作者Pieter Burger
相关产品推荐
相关产品推荐

