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

Xamarin.Forms实现ActivityIndicator在ListView加载完成前持续运行

解决ActivityIndicator持续显示至ListView数据加载完成的问题

你当前的实现问题在于用Task.Delay(5000)硬编码了等待时间,完全没考虑实际的网络请求耗时——不管ListView的数据有没有加载完成,5秒后ActivityIndicator就会消失,这显然不符合需求。正确的做法是将ActivityIndicator的状态和数据加载的生命周期绑定,也就是数据开始加载时显示,加载完成(成功/失败)后隐藏。

下面是具体的实现步骤,遵循MVVM的最佳实践:

1. 优化你的MainViewModel

首先确保ViewModel实现INotifyPropertyChanged接口,添加IsBusy属性来跟踪加载状态,同时用异步方法处理Web Service请求:

public class MainViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Report> _reportList;
    public ObservableCollection<Report> ReportList
    {
        get => _reportList;
        set
        {
            _reportList = value;
            OnPropertyChanged(nameof(ReportList));
        }
    }

    private bool _isBusy;
    public bool IsBusy
    {
        get => _isBusy;
        set
        {
            _isBusy = value;
            OnPropertyChanged(nameof(IsBusy));
        }
    }

    public MainViewModel()
    {
        // 构造函数中触发数据加载
        _ = LoadReportsAsync();
    }

    private async Task LoadReportsAsync()
    {
        IsBusy = true; // 开始加载,显示ActivityIndicator
        try
        {
            // 替换成你的Web Service请求逻辑
            var fetchedReports = await YourWebService.GetReportsFromServerAsync();
            ReportList = new ObservableCollection<Report>(fetchedReports);
        }
        catch (Exception ex)
        {
            // 这里可以添加错误处理,比如弹出提示
            Console.WriteLine($"加载数据失败: {ex.Message}");
        }
        finally
        {
            IsBusy = false; // 加载完成,隐藏ActivityIndicator
        }
    }

    // 实现INotifyPropertyChanged接口
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 修改XAML,绑定UI元素到ViewModel状态

我们用RelativeLayout让ActivityIndicator覆盖在ListView上方,同时把ActivityIndicator的IsRunning、IsVisible绑定到IsBusy,ListView的可见性则通过转换器取反绑定:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.MainPage">
    <ContentPage.Resources>
        <!-- 声明布尔值取反转换器 -->
        <local:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
    </ContentPage.Resources>

    <RelativeLayout>
        <!-- ListView:加载完成时显示,加载中隐藏 -->
        <ListView x:Name="ReportListView" 
                  ItemsSource="{Binding ReportList}"
                  IsVisible="{Binding IsBusy, Converter={StaticResource InverseBooleanConverter}}"
                  RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}"
                  RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Padding="9" 
                                     HorizontalOptions="StartAndExpand" 
                                     Orientation="Vertical" 
                                     BackgroundColor="Lavender">
                            <Label Text="{Binding ReportName}" 
                                   FontSize="Medium" 
                                   WidthRequest="10000" 
                                   TextColor="Blue" 
                                   Style="{DynamicResource ListItemTextStyle}" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <!-- ActivityIndicator:加载中显示,完成后隐藏 -->
        <ActivityIndicator HorizontalOptions="Center" 
                           VerticalOptions="Center" 
                           IsRunning="{Binding IsBusy}" 
                           IsVisible="{Binding IsBusy}" 
                           x:Name="activity"
                           RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width}"
                           RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height}"/>
    </RelativeLayout>
</ContentPage>

补充:实现InverseBooleanConverter

上面用到的转换器需要你自己实现,用来把IsBusy的布尔值取反,控制ListView的可见性:

public class InverseBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !(bool)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !(bool)value;
    }
}

3. 简化后台代码

现在你不需要手动控制UI元素的显示状态了,只需要设置BindingContext即可:

public MainPage()
{
    InitializeComponent();
    // 只需要这一行,剩下的交给ViewModel处理
    BindingContext = new MainViewModel();
}

为什么这样做?

  • 数据驱动UI:通过MVVM模式,让ViewModel的状态直接控制UI,避免了后台代码直接操作UI的耦合,代码更易维护。
  • 精准响应加载状态:IsBusy在异步请求开始时设为true,请求完成(无论成功失败)在finally块设为false,完全贴合实际的网络请求生命周期,不会出现提前隐藏或一直显示ActivityIndicator的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:22:07