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

.NET MAUI Android端CollectionView搜索过滤卡顿问题求助

.NET MAUI Android端SearchBar过滤CollectionView卡顿问题解决

问题描述

.NET MAUI项目中,Android端使用SearchBar配合仅加载50条城市数据的CollectionView时,触发TextChanged事件过滤数据出现严重卡顿,无法正常输入。Windows端运行正常,所用Android设备性能良好,已尝试异步调用、切换为ListView等方案均无效。

现有代码

XAML代码

<StackLayout HorizontalOptions="FillAndExpand" Orientation="Vertical" Margin="5">
    <Frame BackgroundColor="MintCream" Padding="10"
           CornerRadius="20">
        <VerticalStackLayout>
            <SearchBar x:Name="FromSearchBar" TextChanged="FromSearchBar_TextChanged" Text="{Binding FromSearchText}" Placeholder="From City" HorizontalOptions="FillAndExpand" />            
        </VerticalStackLayout>
    </Frame>
    <CollectionView x:Name="FromColl" ItemsSource="{Binding Cities}" IsVisible="true">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <HorizontalStackLayout Padding="10">
                    <Label Grid.Column="1" 
                           Text="{Binding FullCityName}" 
                           FontAttributes="Bold" />
                </HorizontalStackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

后台C#代码

public MainPage()
{
    InitializeComponent();
    ViewModel = new NewItemViewModel();
    this.BindingContext = ViewModel;
}
public NewItemViewModel ViewModel { get; set; }


private void FromSearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
    var searchBar = (SearchBar)sender;
    ViewModel.Cities = new ObservableCollection<CityList>(CityRepository.GetAllFromDbAsync(searchBar.Text));
    ViewModel.FromListViewVisible = true;
}

数据库仓储代码

public static List<CityList> GetAllFromDbAsync(string str)
{
    try
    {
        var rtdb = new RTDatabase();
        if (string.IsNullOrEmpty(str))
            return rtdb.GetItems().Result.Take(50).ToList();
        else
            return rtdb.GetItems().Result.Where(a => a.CityName.ToLower().StartsWith(str.ToLower())).Take(50).ToList();
    }
    catch (Exception ex)
    {
        LogError.log("CityRepository.cs - GetAllFromDbAsync - " + ex.Message);
        return new List<CityList>();
    }
}

public Task<List<CityList>> GetItems()
{
    return Database.Table<CityList>().ToListAsync();
}

问题根源

  1. UI线程阻塞:仓储方法中使用.Result同步等待异步数据库操作,直接阻塞Android UI线程,导致输入卡顿。
  2. 频繁创建集合:每次搜索都新建ObservableCollection,触发CollectionView全量重绘,开销极大。
  3. 低效数据过滤:先查询全量城市数据再在内存中过滤,即使只取50条,也浪费了IO和内存资源。
  4. 无防抖处理:TextChanged事件随用户输入频繁触发,短时间内多次执行数据库查询。
  5. 冗余布局层级:XAML中嵌套不必要的布局容器,增加Android渲染负担。

解决方案

1. 修复仓储异步方法,SQL层面过滤数据

public static async Task<List<CityList>> GetAllFromDbAsync(string str)
{
    try
    {
        using var rtdb = new RTDatabase();
        var query = rtdb.Database.Table<CityList>();
        
        if (!string.IsNullOrEmpty(str))
        {
            var lowerSearchStr = str.ToLower();
            // 让SQLite直接执行过滤,减少内存处理
            query = query.Where(a => a.CityName.ToLower().StartsWith(lowerSearchStr));
        }
        
        // 先限制数量再查询,减少数据传输
        return await query.Take(50).ToListAsync();
    }
    catch (Exception ex)
    {
        LogError.log("CityRepository.cs - GetAllFromDbAsync - " + ex.Message);
        return new List<CityList>();
    }
}

2. 优化TextChanged事件,增加防抖+异步更新集合

private CancellationTokenSource _searchCancellationTokenSource;

private async void FromSearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
    var searchBar = (SearchBar)sender;
    var searchText = searchBar.Text?.Trim() ?? string.Empty;

    // 取消上一次未完成的搜索任务,实现防抖
    _searchCancellationTokenSource?.Cancel();
    _searchCancellationTokenSource = new CancellationTokenSource();

    try
    {
        // 300ms延迟,避免输入过程中频繁触发查询
        await Task.Delay(300, _searchCancellationTokenSource.Token);
        
        var filteredCities = await CityRepository.GetAllFromDbAsync(searchText);
        
        // 更新现有集合而非新建,减少UI重绘
        ViewModel.Cities.Clear();
        foreach (var city in filteredCities)
        {
            ViewModel.Cities.Add(city);
        }
        
        ViewModel.FromListViewVisible = true;
    }
    catch (OperationCanceledException)
    {
        // 忽略取消的任务,无需处理
    }
    catch (Exception ex)
    {
        // 按需添加异常处理逻辑
    }
}

3. ViewModel中初始化固定ObservableCollection

public class NewItemViewModel : INotifyPropertyChanged
{
    // 初始化固定集合,后续仅更新内容
    public ObservableCollection<CityList> Cities { get; } = new ObservableCollection<CityList>();
    
    private bool _fromListViewVisible;
    public bool FromListViewVisible
    {
        get => _fromListViewVisible;
        set
        {
            _fromListViewVisible = value;
            OnPropertyChanged();
        }
    }
    
    // 实现INotifyPropertyChanged接口
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

4. 简化XAML布局,减少层级

<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Margin="5">
    <Frame BackgroundColor="MintCream" Padding="10" CornerRadius="20" HorizontalOptions="FillAndExpand">
        <SearchBar x:Name="FromSearchBar" 
                   TextChanged="FromSearchBar_TextChanged" 
                   Text="{Binding FromSearchText}" 
                   Placeholder="From City" 
                   HorizontalOptions="FillAndExpand" />
    </Frame>
    <CollectionView x:Name="FromColl" 
                    ItemsSource="{Binding Cities}" 
                    IsVisible="{Binding FromListViewVisible}"
                    VerticalOptions="FillAndExpand">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Label Text="{Binding FullCityName}" 
                       FontAttributes="Bold" 
                       Padding="10" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

关键优化点说明

  • 用await替代.Result,彻底避免UI线程阻塞。
  • 防抖处理减少无效查询,降低资源消耗。
  • SQL层面过滤数据,减少数据库IO和内存占用。
  • 更新现有集合而非新建,让CollectionView仅渲染变化项。
  • 简化布局层级,提升Android端渲染性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 10:06:19