如何在Xamarin.Forms中实现圆形水填充加载动画?
嘿,这个圆形水填充加载动画效果确实很吸睛!在Xamarin.Forms里实现它,我给你准备了两种靠谱的方案,你可以根据自己的需求来选:
方案一:使用SkiaSharp(跨平台首选)
SkiaSharp是Xamarin官方主推的跨平台图形库,用它来实现这类自定义动画最省心,一套代码就能跑遍iOS、Android和UWP。
步骤1:安装NuGet包
在你的Xamarin.Forms项目中,搜索并安装SkiaSharp.Views.Forms这个NuGet包,确保所有平台项目都安装上。
步骤2:自定义水填充加载视图
创建一个继承自SKCanvasView的自定义视图,在里面实现绘制逻辑和动画:
using SkiaSharp; using SkiaSharp.Views.Forms; using Xamarin.Forms; namespace YourAppNamespace { public class WaterFillLoadingView : SKCanvasView { private float _progress = 0f; // 外圆画笔 private readonly SKPaint _circlePaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = SKColors.DodgerBlue, StrokeWidth = 4, IsAntialias = true }; // 水填充画笔 private readonly SKPaint _waterPaint = new SKPaint { Style = SKPaintStyle.Fill, Color = SKColors.LightSkyBlue, IsAntialias = true }; // 循环动画 private readonly Animation _fillAnimation; public WaterFillLoadingView() { // 创建一个从0到1循环的动画,用SinInOut缓动让动画更自然 _fillAnimation = new Animation( v => _progress = (float)v, start: 0, end: 1, easing: Easing.SinInOut) { Duration = 2000, // 动画时长2秒 Repeat = () => true // 无限循环 }; // 启动动画 _fillAnimation.Commit(this, "WaterFillLoop", length: 2000, repeat: () => true); // 绑定绘制事件 PaintSurface += OnPaintSurface; } private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e) { var canvas = e.Surface.Canvas; canvas.Clear(); // 清空画布 var canvasSize = e.Info.Size; var centerPoint = new SKPoint(canvasSize.Width / 2, canvasSize.Height / 2); var circleRadius = Math.Min(canvasSize.Width, canvasSize.Height) / 2 - _circlePaint.StrokeWidth; // 1. 绘制外圆边框 canvas.DrawCircle(centerPoint, circleRadius, _circlePaint); // 2. 创建圆形裁剪路径,确保水只在圆内显示 using var clipPath = new SKPath(); clipPath.AddCircle(centerPoint, circleRadius); canvas.ClipPath(clipPath); // 3. 计算水的填充高度:progress=0是空,progress=1是满 var waterHeight = circleRadius * 2 * _progress; var waterTopY = centerPoint.Y + circleRadius - waterHeight; // 4. 绘制水填充区域(矩形被裁剪后只显示圆内部分) var waterRect = new SKRect( centerPoint.X - circleRadius, waterTopY, centerPoint.X + circleRadius, centerPoint.Y + circleRadius); canvas.DrawRect(waterRect, _waterPaint); } // 可选:添加绑定属性,方便外部控制进度 public static readonly BindableProperty ProgressProperty = BindableProperty.Create( nameof(Progress), typeof(float), typeof(WaterFillLoadingView), 0f, propertyChanged: (bindable, oldVal, newVal) => { if (bindable is WaterFillLoadingView view) { view._progress = (float)newVal; view.InvalidateSurface(); // 触发重绘 } }); public float Progress { get => (float)GetValue(ProgressProperty); set => SetValue(ProgressProperty, value); } } }
步骤3:在XAML中使用视图
像普通Xamarin.Forms控件一样引用这个自定义视图:
<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"> <StackLayout VerticalOptions="Center" HorizontalOptions="Center"> <!-- 设置宽高,调整你需要的大小 --> <local:WaterFillLoadingView WidthRequest="120" HeightRequest="120" /> </StackLayout> </ContentPage>
方案二:自定义平台渲染器(原生优化)
如果你的项目对性能要求极高,或者需要更贴近原生的效果,可以用自定义渲染器分别在Android和iOS上实现。这里以Android为例:
步骤1:共享项目定义视图
using Xamarin.Forms; namespace YourAppNamespace { public class WaterFillLoadingView : View { } }
步骤2:Android端渲染器
using Android.Content; using Android.Graphics; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer(typeof(WaterFillLoadingView), typeof(WaterFillLoadingRenderer))] namespace YourAppNamespace.Droid { public class WaterFillLoadingRenderer : ViewRenderer<WaterFillLoadingView, Android.Views.View> { private float _progress = 0f; private readonly Paint _circlePaint = new Paint(PaintFlags.AntiAlias) { Style = Paint.Style.Stroke, Color = Android.Graphics.Color.DodgerBlue, StrokeWidth = 4 }; private readonly Paint _waterPaint = new Paint(PaintFlags.AntiAlias) { Style = Paint.Style.Fill, Color = Android.Graphics.Color.LightSkyBlue }; private readonly ValueAnimator _animator; public WaterFillLoadingRenderer(Context context) : base(context) { // 创建Android原生动画 _animator = ValueAnimator.OfFloat(0f, 1f); _animator.SetDuration(2000); _animator.RepeatCount = ValueAnimator.Infinite; _animator.Update += (s, e) => { _progress = (float)e.Animation.AnimatedValue; Invalidate(); // 触发重绘 }; _animator.Start(); } protected override void OnElementChanged(ElementChangedEventArgs<WaterFillLoadingView> e) { base.OnElementChanged(e); if (Control == null) { SetNativeControl(new Android.Views.View(Context)); } } protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); var width = Width; var height = Height; var centerX = width / 2f; var centerY = height / 2f; var radius = Math.Min(width, height) / 2f - _circlePaint.StrokeWidth; // 绘制外圆 canvas.DrawCircle(centerX, centerY, radius, _circlePaint); // 裁剪圆形区域 var clipPath = new Path(); clipPath.AddCircle(centerX, centerY, radius, Path.Direction.Ccw); canvas.ClipPath(clipPath); // 绘制水填充 var waterHeight = radius * 2 * _progress; var waterTopY = centerY + radius - waterHeight; var waterRect = new RectF(centerX - radius, waterTopY, centerX + radius, centerY + radius); canvas.DrawRect(waterRect, _waterPaint); } } }
额外优化建议
- 你可以修改画笔的
Color属性来调整外圆和水的颜色,匹配你的App主题 - 调整动画
Duration可以改变填充速度 - 如果需要非循环的加载效果,可以去掉动画的
Repeat设置,改为绑定Progress属性来控制填充进度
内容的提问来源于stack exchange,提问作者Hetal




