You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动