MAUI怎么实现Shimmer加载效果 MAUI骨架屏教程

来源:Java编程网作者:霓渡头衔:草根站长
导读:本期聚焦于小伙伴创作的《MAUI怎么实现Shimmer加载效果 MAUI骨架屏教程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《MAUI怎么实现Shimmer加载效果 MAUI骨架屏教程》有用,将其分享出去将是对创作者最好的鼓励。

在MAUI应用开发里,当页面需要请求网络数据或者加载本地资源时,直接展示空白内容会让用户产生等待焦虑,Shimmer加载效果和骨架屏可以有效解决这个问题,通过动态闪烁的占位结构模拟内容加载过程,提升交互体验。

MAUI怎么实现Shimmer加载效果 MAUI骨架屏教程

Shimmer效果实现原理

Shimmer效果的核心是通过渐变遮罩和位移动画实现闪烁感,整体逻辑分为三步:首先绘制占位内容的静态骨架结构,然后在骨架上方叠加一个线性渐变的半透明遮罩,最后让遮罩沿着水平方向循环移动,就形成了常见的闪烁加载效果。

自定义Shimmer控件实现

我们可以封装一个可复用的Shimmer容器控件,方便后续在多个页面中调用,控件需要支持子内容嵌套、动画速度调整和渐变颜色配置。

控件代码实现

首先创建ShimmerLayout类,继承自ContentView,实现动画逻辑:

using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Shapes;
using System.Threading.Tasks;

namespace MauiShimmerDemo.Controls
{
    public class ShimmerLayout : ContentView
    {
        // 动画持续时间,默认1.5秒
        public double AnimationDuration { get; set; } = 1500;
        // 渐变起始颜色
        public Color StartColor { get; set; } = Color.FromArgb("#E0E0E0");
        // 渐变中间高亮颜色
        public Color HighlightColor { get; set; } = Color.FromArgb("#F5F5F5");
        // 渐变结束颜色
        public Color EndColor { get; set; } = Color.FromArgb("#E0E0E0");

        private bool _isAnimating = false;
        private RectangleGeometry _maskGeometry;
        private LinearGradientBrush _gradientBrush;

        protected override void OnHandlerChanged()
        {
            base.OnHandlerChanged();
            InitShimmerMask();
        }

        private void InitShimmerMask()
        {
            // 创建线性渐变画刷
            _gradientBrush = new LinearGradientBrush
            {
                StartPoint = new Point(0, 0.5),
                EndPoint = new Point(1, 0.5),
                GradientStops = new GradientStopCollection
                {
                    new GradientStop(StartColor, 0),
                    new GradientStop(HighlightColor, 0.5),
                    new GradientStop(EndColor, 1)
                }
            };

            // 创建遮罩矩形,初始位置在控件左侧外部
            var maskRect = new RectangleGeometry(new Rect(-this.Width, 0, this.Width * 2, this.Height));
            _maskGeometry = maskRect;

            // 创建遮罩层
            var maskLayer = new Rectangle
            {
                Fill = _gradientBrush,
                Clip = _maskGeometry,
                Opacity = 0.8
            };

            // 将遮罩层叠加到内容上方
            var grid = new Grid();
            if (Content != null)
            {
                grid.Children.Add(Content);
            }
            grid.Children.Add(maskLayer);
            Content = grid;
        }

        public async Task StartAnimation()
        {
            if (_isAnimating || this.Width == 0) return;
            _isAnimating = true;

            while (_isAnimating)
            {
                // 重置遮罩位置到左侧
                _maskGeometry.Rect = new Rect(-this.Width, 0, this.Width * 2, this.Height);
                // 执行位移动画
                await _maskGeometry.RectProperty.AnimateTo(
                    new Rect(this.Width, 0, this.Width * 2, this.Height),
                    (uint)AnimationDuration,
                    Easing.Linear
                );
            }
        }

        public void StopAnimation()
        {
            _isAnimating = false;
        }
    }
}

骨架屏页面搭建

有了ShimmerLayout控件后,我们可以搭建一个常见的列表页骨架屏,模拟列表项加载的占位效果。

页面XAML布局

在ContentPage中添加骨架屏结构和真实内容结构,通过绑定控制显示隐藏:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:MauiShimmerDemo.Controls"
             x:Class="MauiShimmerDemo.MainPage"
             Title="Shimmer骨架屏示例">
    <ContentPage.Resources>
        <!-- 骨架项模板 -->
        <DataTemplate x:Key="ShimmerItemTemplate">
            <Grid Padding="10" HeightRequest="80">
                <!-- 头像占位 -->
                <Frame WidthRequest="60" HeightRequest="60" CornerRadius="30" BackgroundColor="#E0E0E0" HorizontalOptions="Start" VerticalOptions="Center"/>
                <!-- 文本占位 -->
                <StackLayout Margin="70,0,0,0" VerticalOptions="Center">
                    <Frame HeightRequest="20" BackgroundColor="#E0E0E0" CornerRadius="4" WidthRequest="200"/>
                    <Frame HeightRequest="16" BackgroundColor="#E0E0E0" CornerRadius="4" WidthRequest="150" Margin="0,10,0,0"/>
                </StackLayout>
            </Grid>
        </DataTemplate>
    </ContentPage.Resources>

    <Grid>
        <!-- 骨架屏区域 -->
        <controls:ShimmerLayout x:Name="ShimmerContainer" IsVisible="{Binding IsLoading}">
            <ListView ItemTemplate="{StaticResource ShimmerItemTemplate}" ItemsSource="{Binding ShimmerItems}">
                <ListView.ItemsSource>
                    <x:Array Type="{x:Type x:String}">
                        <x:String>占位</x:String>
                        <x:String>占位</x:String>
                        <x:String>占位</x:String>
                        <x:String>占位</x:String>
                        <x:String>占位</x:String>
                    </x:Array>
                </ListView.ItemsSource>
            </ListView>
        </controls:ShimmerLayout>

        <!-- 真实内容区域 -->
        <ListView ItemsSource="{Binding RealItems}" IsVisible="{Binding IsLoading, Converter={StaticResource InverseBoolConverter}}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid Padding="10" HeightRequest="80">
                        <Image Source="{Binding Avatar}" WidthRequest="60" HeightRequest="60" HorizontalOptions="Start" VerticalOptions="Center"/>
                        <StackLayout Margin="70,0,0,0" VerticalOptions="Center">
                            <Label Text="{Binding Name}" FontSize="16" FontAttributes="Bold"/>
                            <Label Text="{Binding Desc}" FontSize="14" TextColor="Gray" Margin="0,10,0,0"/>
                        </StackLayout>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</ContentPage>

页面后台逻辑

在页面后台代码中控制加载状态,模拟数据请求过程:

using Microsoft.Maui.Controls;
using System.Collections.ObjectModel;
using System.Threading.Tasks;

namespace MauiShimmerDemo
{
    public partial class MainPage : ContentPage
    {
        public ObservableCollection ShimmerItems { get; set; }
        public ObservableCollection RealItems { get; set; }
        public bool IsLoading { get; set; } = true;

        public MainPage()
        {
            InitializeComponent();
            ShimmerItems = new ObservableCollection();
            RealItems = new ObservableCollection();
            // 初始化占位数据
            for (int i = 0; i < 5; i++)
            {
                ShimmerItems.Add("占位");
            }
            BindingContext = this;
        }

        protected override async void OnAppearing()
        {
            base.OnAppearing();
            // 启动Shimmer动画
            await ShimmerContainer.StartAnimation();
            // 模拟2秒数据请求
            await Task.Delay(2000);
            // 加载真实数据
            LoadRealData();
            // 停止动画,切换显示真实内容
            ShimmerContainer.StopAnimation();
            IsLoading = false;
            OnPropertyChanged(nameof(IsLoading));
        }

        private void LoadRealData()
        {
            RealItems.Add(new UserModel { Avatar = "https://ipipp.com/avatar1.png", Name = "张三", Desc = "这是第一条用户描述信息" });
            RealItems.Add(new UserModel { Avatar = "https://ipipp.com/avatar2.png", Name = "李四", Desc = "这是第二条用户描述信息" });
            RealItems.Add(new UserModel { Avatar = "https://ipipp.com/avatar3.png", Name = "王五", Desc = "这是第三条用户描述信息" });
        }
    }

    public class UserModel
    {
        public string Avatar { get; set; }
        public string Name { get; set; }
        public string Desc { get; set; }
    }
}

注意事项

  • Shimmer动画的渐变颜色需要和骨架占位颜色保持协调,避免闪烁效果过于突兀
  • 页面销毁时需要手动调用StopAnimation方法停止动画,避免后台无效循环消耗性能
  • 如果页面有横竖屏切换场景,需要在布局尺寸变化时重新初始化遮罩的几何尺寸,保证动画效果正常
  • 骨架屏的占位结构需要和真实内容的布局结构保持一致,避免内容加载完成后出现明显的布局跳动

MAUIShimmer加载效果骨架屏ContentPage修改时间:2026-06-18 08:48:43

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。