Activity completed
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.Maui.Handlers;
|
using Microsoft.Maui.Handlers;
|
||||||
using Microsoft.Maui.Graphics;
|
using Microsoft.Maui.Graphics;
|
||||||
using SkiaSharp;
|
using Microsoft.Maui.Platform;
|
||||||
|
|
||||||
namespace Microsoft.Maui.Platform.Linux.Handlers;
|
namespace Microsoft.Maui.Platform.Linux.Handlers;
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator,
|
|||||||
[nameof(IActivityIndicator.IsRunning)] = MapIsRunning,
|
[nameof(IActivityIndicator.IsRunning)] = MapIsRunning,
|
||||||
[nameof(IActivityIndicator.Color)] = MapColor,
|
[nameof(IActivityIndicator.Color)] = MapColor,
|
||||||
[nameof(IView.Background)] = MapBackground,
|
[nameof(IView.Background)] = MapBackground,
|
||||||
|
[nameof(IView.IsEnabled)] = MapIsEnabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static CommandMapper<IActivityIndicator, ActivityIndicatorHandler> CommandMapper = new(ViewHandler.ViewCommandMapper)
|
public static CommandMapper<IActivityIndicator, ActivityIndicatorHandler> CommandMapper = new(ViewHandler.ViewCommandMapper)
|
||||||
@@ -38,6 +39,19 @@ public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator,
|
|||||||
return new SkiaActivityIndicator();
|
return new SkiaActivityIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void ConnectHandler(SkiaActivityIndicator platformView)
|
||||||
|
{
|
||||||
|
base.ConnectHandler(platformView);
|
||||||
|
|
||||||
|
// Sync properties
|
||||||
|
if (VirtualView != null)
|
||||||
|
{
|
||||||
|
MapIsRunning(this, VirtualView);
|
||||||
|
MapColor(this, VirtualView);
|
||||||
|
MapIsEnabled(this, VirtualView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
|
public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
|
||||||
{
|
{
|
||||||
if (handler.PlatformView is null) return;
|
if (handler.PlatformView is null) return;
|
||||||
@@ -49,7 +63,7 @@ public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator,
|
|||||||
if (handler.PlatformView is null) return;
|
if (handler.PlatformView is null) return;
|
||||||
|
|
||||||
if (activityIndicator.Color is not null)
|
if (activityIndicator.Color is not null)
|
||||||
handler.PlatformView.Color = activityIndicator.Color.ToSKColor();
|
handler.PlatformView.Color = activityIndicator.Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MapBackground(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
|
public static void MapBackground(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
|
||||||
@@ -61,4 +75,11 @@ public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator,
|
|||||||
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
|
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void MapIsEnabled(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
|
||||||
|
{
|
||||||
|
if (handler.PlatformView is null) return;
|
||||||
|
handler.PlatformView.IsEnabled = activityIndicator.IsEnabled;
|
||||||
|
handler.PlatformView.Invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,35 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.Maui.Controls;
|
using Microsoft.Maui.Controls;
|
||||||
|
using Microsoft.Maui.Graphics;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Microsoft.Maui.Platform;
|
namespace Microsoft.Maui.Platform;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Skia-rendered activity indicator (spinner) control with full XAML styling support.
|
/// Skia-rendered activity indicator (spinner) control with full MAUI compliance.
|
||||||
|
/// Implements IActivityIndicator interface requirements:
|
||||||
|
/// - IsRunning property to start/stop animation
|
||||||
|
/// - Color property for the indicator color
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SkiaActivityIndicator : SkiaView
|
public class SkiaActivityIndicator : SkiaView
|
||||||
{
|
{
|
||||||
|
#region SKColor Helper
|
||||||
|
|
||||||
|
private static SKColor ToSKColor(Color? color)
|
||||||
|
{
|
||||||
|
if (color == null) return SKColors.Transparent;
|
||||||
|
return new SKColor(
|
||||||
|
(byte)(color.Red * 255),
|
||||||
|
(byte)(color.Green * 255),
|
||||||
|
(byte)(color.Blue * 255),
|
||||||
|
(byte)(color.Alpha * 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region BindableProperties
|
#region BindableProperties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,9 +50,9 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
public static readonly BindableProperty ColorProperty =
|
public static readonly BindableProperty ColorProperty =
|
||||||
BindableProperty.Create(
|
BindableProperty.Create(
|
||||||
nameof(Color),
|
nameof(Color),
|
||||||
typeof(SKColor),
|
typeof(Color),
|
||||||
typeof(SkiaActivityIndicator),
|
typeof(SkiaActivityIndicator),
|
||||||
new SKColor(0x21, 0x96, 0xF3),
|
Color.FromRgb(0x21, 0x96, 0xF3), // Material Blue
|
||||||
BindingMode.TwoWay,
|
BindingMode.TwoWay,
|
||||||
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
|
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
|
||||||
|
|
||||||
@@ -43,9 +62,9 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
public static readonly BindableProperty DisabledColorProperty =
|
public static readonly BindableProperty DisabledColorProperty =
|
||||||
BindableProperty.Create(
|
BindableProperty.Create(
|
||||||
nameof(DisabledColor),
|
nameof(DisabledColor),
|
||||||
typeof(SKColor),
|
typeof(Color),
|
||||||
typeof(SkiaActivityIndicator),
|
typeof(SkiaActivityIndicator),
|
||||||
new SKColor(0xBD, 0xBD, 0xBD),
|
Color.FromRgb(0xBD, 0xBD, 0xBD),
|
||||||
BindingMode.TwoWay,
|
BindingMode.TwoWay,
|
||||||
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
|
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
|
||||||
|
|
||||||
@@ -55,9 +74,9 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
public static readonly BindableProperty SizeProperty =
|
public static readonly BindableProperty SizeProperty =
|
||||||
BindableProperty.Create(
|
BindableProperty.Create(
|
||||||
nameof(Size),
|
nameof(Size),
|
||||||
typeof(float),
|
typeof(double),
|
||||||
typeof(SkiaActivityIndicator),
|
typeof(SkiaActivityIndicator),
|
||||||
32f,
|
32.0,
|
||||||
BindingMode.TwoWay,
|
BindingMode.TwoWay,
|
||||||
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).InvalidateMeasure());
|
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).InvalidateMeasure());
|
||||||
|
|
||||||
@@ -67,9 +86,9 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
public static readonly BindableProperty StrokeWidthProperty =
|
public static readonly BindableProperty StrokeWidthProperty =
|
||||||
BindableProperty.Create(
|
BindableProperty.Create(
|
||||||
nameof(StrokeWidth),
|
nameof(StrokeWidth),
|
||||||
typeof(float),
|
typeof(double),
|
||||||
typeof(SkiaActivityIndicator),
|
typeof(SkiaActivityIndicator),
|
||||||
3f,
|
3.0,
|
||||||
BindingMode.TwoWay,
|
BindingMode.TwoWay,
|
||||||
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).InvalidateMeasure());
|
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).InvalidateMeasure());
|
||||||
|
|
||||||
@@ -79,9 +98,9 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
public static readonly BindableProperty RotationSpeedProperty =
|
public static readonly BindableProperty RotationSpeedProperty =
|
||||||
BindableProperty.Create(
|
BindableProperty.Create(
|
||||||
nameof(RotationSpeed),
|
nameof(RotationSpeed),
|
||||||
typeof(float),
|
typeof(double),
|
||||||
typeof(SkiaActivityIndicator),
|
typeof(SkiaActivityIndicator),
|
||||||
360f,
|
360.0,
|
||||||
BindingMode.TwoWay);
|
BindingMode.TwoWay);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -112,45 +131,45 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the indicator color.
|
/// Gets or sets the indicator color.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SKColor Color
|
public Color Color
|
||||||
{
|
{
|
||||||
get => (SKColor)GetValue(ColorProperty);
|
get => (Color)GetValue(ColorProperty);
|
||||||
set => SetValue(ColorProperty, value);
|
set => SetValue(ColorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the disabled color.
|
/// Gets or sets the disabled color.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SKColor DisabledColor
|
public Color DisabledColor
|
||||||
{
|
{
|
||||||
get => (SKColor)GetValue(DisabledColorProperty);
|
get => (Color)GetValue(DisabledColorProperty);
|
||||||
set => SetValue(DisabledColorProperty, value);
|
set => SetValue(DisabledColorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the indicator size.
|
/// Gets or sets the indicator size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float Size
|
public double Size
|
||||||
{
|
{
|
||||||
get => (float)GetValue(SizeProperty);
|
get => (double)GetValue(SizeProperty);
|
||||||
set => SetValue(SizeProperty, value);
|
set => SetValue(SizeProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the stroke width.
|
/// Gets or sets the stroke width.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float StrokeWidth
|
public double StrokeWidth
|
||||||
{
|
{
|
||||||
get => (float)GetValue(StrokeWidthProperty);
|
get => (double)GetValue(StrokeWidthProperty);
|
||||||
set => SetValue(StrokeWidthProperty, value);
|
set => SetValue(StrokeWidthProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the rotation speed in degrees per second.
|
/// Gets or sets the rotation speed in degrees per second.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float RotationSpeed
|
public double RotationSpeed
|
||||||
{
|
{
|
||||||
get => (float)GetValue(RotationSpeedProperty);
|
get => (double)GetValue(RotationSpeedProperty);
|
||||||
set => SetValue(RotationSpeedProperty, value);
|
set => SetValue(RotationSpeedProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +184,15 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Private Fields
|
||||||
|
|
||||||
private float _rotationAngle;
|
private float _rotationAngle;
|
||||||
private DateTime _lastUpdateTime = DateTime.UtcNow;
|
private DateTime _lastUpdateTime = DateTime.UtcNow;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handlers
|
||||||
|
|
||||||
private void OnIsRunningChanged()
|
private void OnIsRunningChanged()
|
||||||
{
|
{
|
||||||
if (IsRunning)
|
if (IsRunning)
|
||||||
@@ -177,6 +202,10 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Rendering
|
||||||
|
|
||||||
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
||||||
{
|
{
|
||||||
if (!IsRunning && !IsEnabled)
|
if (!IsRunning && !IsEnabled)
|
||||||
@@ -184,9 +213,13 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var size = (float)Size;
|
||||||
|
var strokeWidth = (float)StrokeWidth;
|
||||||
|
var rotationSpeed = (float)RotationSpeed;
|
||||||
|
|
||||||
var centerX = bounds.MidX;
|
var centerX = bounds.MidX;
|
||||||
var centerY = bounds.MidY;
|
var centerY = bounds.MidY;
|
||||||
var radius = Math.Min(Size / 2, Math.Min(bounds.Width, bounds.Height) / 2) - StrokeWidth;
|
var radius = Math.Min(size / 2, Math.Min(bounds.Width, bounds.Height) / 2) - strokeWidth;
|
||||||
|
|
||||||
// Update rotation
|
// Update rotation
|
||||||
if (IsRunning)
|
if (IsRunning)
|
||||||
@@ -194,27 +227,27 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
var elapsed = (now - _lastUpdateTime).TotalSeconds;
|
var elapsed = (now - _lastUpdateTime).TotalSeconds;
|
||||||
_lastUpdateTime = now;
|
_lastUpdateTime = now;
|
||||||
_rotationAngle = (_rotationAngle + (float)(RotationSpeed * elapsed)) % 360;
|
_rotationAngle = (_rotationAngle + (float)(rotationSpeed * elapsed)) % 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.Save();
|
canvas.Save();
|
||||||
canvas.Translate(centerX, centerY);
|
canvas.Translate(centerX, centerY);
|
||||||
canvas.RotateDegrees(_rotationAngle);
|
canvas.RotateDegrees(_rotationAngle);
|
||||||
|
|
||||||
var color = IsEnabled ? Color : DisabledColor;
|
var colorSK = ToSKColor(IsEnabled ? Color : DisabledColor);
|
||||||
|
|
||||||
// Draw arcs with varying opacity
|
// Draw arcs with varying opacity
|
||||||
for (int i = 0; i < ArcCount; i++)
|
for (int i = 0; i < ArcCount; i++)
|
||||||
{
|
{
|
||||||
var alpha = (byte)(255 * (1 - (float)i / ArcCount));
|
var alpha = (byte)(255 * (1 - (float)i / ArcCount));
|
||||||
var arcColor = color.WithAlpha(alpha);
|
var arcColor = colorSK.WithAlpha(alpha);
|
||||||
|
|
||||||
using var paint = new SKPaint
|
using var paint = new SKPaint
|
||||||
{
|
{
|
||||||
Color = arcColor,
|
Color = arcColor,
|
||||||
IsAntialias = true,
|
IsAntialias = true,
|
||||||
Style = SKPaintStyle.Stroke,
|
Style = SKPaintStyle.Stroke,
|
||||||
StrokeWidth = StrokeWidth,
|
StrokeWidth = strokeWidth,
|
||||||
StrokeCap = SKStrokeCap.Round
|
StrokeCap = SKStrokeCap.Round
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -239,8 +272,28 @@ public class SkiaActivityIndicator : SkiaView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
|
protected override void OnEnabledChanged()
|
||||||
|
{
|
||||||
|
base.OnEnabledChanged();
|
||||||
|
SkiaVisualStateManager.GoToState(this, IsEnabled
|
||||||
|
? SkiaVisualStateManager.CommonStates.Normal
|
||||||
|
: SkiaVisualStateManager.CommonStates.Disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layout
|
||||||
|
|
||||||
protected override SKSize MeasureOverride(SKSize availableSize)
|
protected override SKSize MeasureOverride(SKSize availableSize)
|
||||||
{
|
{
|
||||||
return new SKSize(Size + StrokeWidth * 2, Size + StrokeWidth * 2);
|
var size = (float)Size;
|
||||||
|
var strokeWidth = (float)StrokeWidth;
|
||||||
|
return new SKSize(size + strokeWidth * 2, size + strokeWidth * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user