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

MAUI(Android)如何实现带独立上下文菜单且不改变选中项的列表

MAUI Android 实现带独立上下文菜单的单选列表

核心实现思路

放弃之前的SwipeView和ContextFlyout方案,改用Android原生PopupMenu实现任意数量的菜单项,同时通过CollectionView的单选模式+DataTrigger实现选中项高亮,确保菜单操作不干扰选中状态。

1. 定义数据模型与视图模型

首先创建包含必要属性和菜单命令的模型类:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls;

namespace YourAppNamespace;

public class ItemModel : INotifyPropertyChanged
{
    private string _imagePath;
    public string ImagePath
    {
        get => _imagePath;
        set { _imagePath = value; OnPropertyChanged(); }
    }

    private string _name;
    public string Name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }

    // 触发菜单的命令
    public ICommand ShowMenuCommand { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

视图模型负责管理列表数据、选中项和菜单逻辑:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Android.Widget;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;

namespace YourAppNamespace;

public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<ItemModel> Items { get; set; }

    private ItemModel _selectedItem;
    public ItemModel SelectedItem
    {
        get => _selectedItem;
        set { _selectedItem = value; OnPropertyChanged(); }
    }

    public MainViewModel()
    {
        Items = new ObservableCollection<ItemModel>();
        // 初始化测试数据
        for (int i = 1; i <= 5; i++)
        {
            Items.Add(new ItemModel
            {
                ImagePath = "sample_image.png", // 替换为你的图片路径
                Name = $"列表项 {i}",
                ShowMenuCommand = new Command<Image>(ShowItemContextMenu)
            });
        }
    }

    // 显示原生上下文菜单
    void ShowItemContextMenu(Image menuIcon)
    {
        if (menuIcon == null) return;
        var currentActivity = Platform.CurrentActivity;
        if (currentActivity == null) return;

        // 创建Android原生PopupMenu
        var popupMenu = new PopupMenu(currentActivity, menuIcon.Handler.PlatformView as Android.Views.View);
        
        // 添加最多10个菜单项
        for (int i = 1; i <= 10; i++)
        {
            popupMenu.Menu.Add($"菜单选项 {i}");
        }

        // 处理菜单点击事件
        popupMenu.MenuItemClick += (s, e) =>
        {
            Toast.MakeText(currentActivity, $"你选择了:{e.Item.TitleFormatted}", ToastLength.Short).Show();
            // 这里可以添加自定义业务逻辑,不会修改SelectedItem
        };

        popupMenu.Show();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. XAML页面布局

使用CollectionView实现单选列表,通过DataTrigger实现选中项高亮,右侧添加三点图标触发菜单:

<?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:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.MainPage">
    <ContentPage.BindingContext>
        <local:MainViewModel />
    </ContentPage.BindingContext>

    <CollectionView ItemsSource="{Binding Items}"
                    SelectionMode="Single"
                    SelectedItem="{Binding SelectedItem}"
                    Margin="10">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Grid Padding="12" ColumnDefinitions="Auto,*,Auto">
                    <!-- 选中项高亮样式 -->
                    <Grid.Style>
                        <Style TargetType="Grid">
                            <Style.Triggers>
                                <DataTrigger TargetType="Grid"
                                             Binding="{Binding Source={RelativeSource AncestorType={x:Type CollectionViewItem}}, Path=IsSelected}"
                                             Value="True">
                                    <Setter Property="BackgroundColor" Value="#E5E7EB" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Grid.Style>

                    <!-- 显示项图片 -->
                    <Image Source="{Binding ImagePath}"
                           WidthRequest="48"
                           HeightRequest="48"
                           Aspect="AspectFill"
                           CornerRadius="8" />

                    <!-- 显示项名称 -->
                    <Label Grid.Column="1"
                           Text="{Binding Name}"
                           FontSize="16"
                           VerticalOptions="Center"
                           Margin="16,0" />

                    <!-- 菜单触发图标 -->
                    <Image Grid.Column="2"
                           Source="dots_menu.png" <!-- 替换为你的三点图标,放入Android项目Resources/drawable -->
                           WidthRequest="24"
                           HeightRequest="24"
                           VerticalOptions="Center">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding ShowMenuCommand}"
                                                  CommandParameter="{Binding Source={RelativeSource Self}}" />
                        </Image.GestureRecognizers>
                    </Image>
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

关键细节说明

  • 选中项高亮:通过DataTrigger绑定CollectionViewItem.IsSelected属性,动态设置Grid背景色,无需额外代码维护选中状态。
  • 上下文菜单:直接调用Android原生PopupMenu,支持显示任意数量的菜单项(这里设置最多10个),避免MAUI内置菜单的兼容性问题。菜单触发通过TapGestureRecognizer绑定命令,传递当前图标控件作为参数,确保菜单显示在正确位置。
  • 选中状态不被干扰:菜单操作仅处理自身逻辑,未修改ViewModel的SelectedItem属性,因此操作菜单不会改变当前选中项。
  • Android适配注意:将三点图标dots_menu.png放入Android项目的Resources/drawable目录,确保资源能正确加载;Platform.CurrentActivity需要MAUI项目已正确初始化,若出现空引用,可在Android项目的MainApplication中添加Microsoft.Maui.ApplicationModel.Platform.Init(this, savedInstanceState);。

内容的提问来源于stack exchange,提问作者Chris Fellows

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 23:30:59