与 Xamarin.Forms 集成

创建响应触摸和 Xamarin.Forms 元素的 SkiaSharp 图形

SkiaSharp 图形可以通过多种方式与 Xamarin.Forms 的其余部分集成。 可以将 SkiaSharp 画布和 Xamarin.Forms 元素合并在同一页上,甚至可以将 Xamarin.Forms 元素放置在 SkiaSharp 画布的之上:

使用滑块选择颜色

在 Xamarin.Forms 中创建交互式 SkiaSharp 图形的另一种方法是通过触摸。

示例程序中的第二页标题为“点击切换填充”。 它用两种方式绘制一个简单的圆圈 - 无填充和有填充 - 点击即可切换。 TapToggleFillPage 类演示如何更改 SkiaSharp 图形以响应用户输入。

对于此页面,SKCanvasView 类会在 TapToggleFill.xaml 文件中实例化,该文件还会在视图上设置 Xamarin.FormsTapGestureRecognizer

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.TapToggleFillPage"
             Title="Tap Toggle Fill">

    <skia:SKCanvasView PaintSurface="OnCanvasViewPaintSurface">
        <skia:SKCanvasView.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnCanvasViewTapped" />
        </skia:SKCanvasView.GestureRecognizers>
    </skia:SKCanvasView>
</ContentPage>

请注意 skia XML 命名空间声明。

TapGestureRecognizer 对象的 Tapped 处理程序只需切换布尔字段的值并调用 SKCanvasViewInvalidateSurface 方法:

bool showFill = true;
...
void OnCanvasViewTapped(object sender, EventArgs args)
{
    showFill ^= true;
    (sender as SKCanvasView).InvalidateSurface();
}

InvalidateSurface 的调用会有效地生成对 PaintSurface 处理程序的调用,该处理程序使用 showFill 字段来填充或不填充圆圈:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    SKPaint paint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = Color.Red.ToSKColor(),
        StrokeWidth = 50
    };
    canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint);

    if (showFill)
    {
        paint.Style = SKPaintStyle.Fill;
        paint.Color = SKColors.Blue;
        canvas.DrawCircle(info.Width / 2, info.Height / 2, 100, paint);
    }
}

StrokeWidth 属性已设置为 50,以强调差异。 还可以先绘制内部,然后再绘制轮廓来查看整个线条宽度。 默认情况下,稍后在 PaintSurface 事件处理程序中绘制的图形会掩盖之前在处理程序中绘制的图形。

颜色探索”页面演示了如何将 SkiaSharp 图形与其他 Xamarin.Forms 元素集成,并演示了在 SkiaSharp 中定义颜色的两种替代方法之间的差异。 静态 SKColor.FromHsl 方法基于“色调-饱和度-亮度”模型创建 SKColor 值:

public static SKColor FromHsl (Single h, Single s, Single l, Byte a)

静态 SKColor.FromHsv 方法基于类似的“色调-饱和度-值”模型创建 SKColor 值:

public static SKColor FromHsv (Single h, Single s, Single v, Byte a)

在这两种情况下,参数 h 的范围均为 0 到 360。 slv 参数的范围为 0 到 100。 a(alpha 或不透明度)参数的范围为 0 到 255。

ColorExplorePage.xaml 文件与 SliderLabel 视图会并排在 StackLayout 中创建两个 SKCanvasView 对象,允许用户选择 HSL 和 HSV 颜色值:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Basics.ColorExplorePage"
             Title="Color Explore">
    <StackLayout>
        <!-- Hue slider -->
        <Slider x:Name="hueSlider"
                Maximum="360"
                Margin="20, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label HorizontalTextAlignment="Center"
               Text="{Binding Source={x:Reference hueSlider},
                              Path=Value,
                              StringFormat='Hue = {0:F0}'}" />

        <!-- Saturation slider -->
        <Slider x:Name="saturationSlider"
                Maximum="100"
                Margin="20, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label HorizontalTextAlignment="Center"
               Text="{Binding Source={x:Reference saturationSlider},
                              Path=Value,
                              StringFormat='Saturation = {0:F0}'}" />

        <!-- Lightness slider -->
        <Slider x:Name="lightnessSlider"
                Maximum="100"
                Margin="20, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label HorizontalTextAlignment="Center"
               Text="{Binding Source={x:Reference lightnessSlider},
                              Path=Value,
                              StringFormat='Lightness = {0:F0}'}" />

        <!-- HSL canvas view -->
        <Grid VerticalOptions="FillAndExpand">
            <skia:SKCanvasView x:Name="hslCanvasView"
                               PaintSurface="OnHslCanvasViewPaintSurface" />

            <Label x:Name="hslLabel"
                   HorizontalOptions="Center"
                   VerticalOptions="Center"
                   BackgroundColor="Black"
                   TextColor="White" />
        </Grid>

        <!-- Value slider -->
        <Slider x:Name="valueSlider"
                Maximum="100"
                Margin="20, 0"
                ValueChanged="OnSliderValueChanged" />

        <Label HorizontalTextAlignment="Center"
               Text="{Binding Source={x:Reference valueSlider},
                              Path=Value,
                              StringFormat='Value = {0:F0}'}" />

        <!-- HSV canvas view -->
        <Grid VerticalOptions="FillAndExpand">
            <skia:SKCanvasView x:Name="hsvCanvasView"
                               PaintSurface="OnHsvCanvasViewPaintSurface" />

            <Label x:Name="hsvLabel"
                   HorizontalOptions="Center"
                   VerticalOptions="Center"
                   BackgroundColor="Black"
                   TextColor="White" />
        </Grid>
    </StackLayout>
</ContentPage>

这两个 SKCanvasView 元素位于单个单元格 Grid 中,顶部有一个 Label,用于显示生成的 RGB 颜色值。

ColorExplorePage.xaml.cs 代码隐藏文件相对简单。 三个 Slider 元素的共享 ValueChanged 处理程序会使两个 SKCanvasView 元素失效。 PaintSurface 处理程序使用 Slider 元素指示的颜色清除画布,并会设置位于 SKCanvasView 元素顶部的 Label

public partial class ColorExplorePage : ContentPage
{
    public ColorExplorePage()
    {
        InitializeComponent();

        hueSlider.Value = 0;
        saturationSlider.Value = 100;
        lightnessSlider.Value = 50;
        valueSlider.Value = 100;
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        hslCanvasView.InvalidateSurface();
        hsvCanvasView.InvalidateSurface();
    }

    void OnHslCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKColor color = SKColor.FromHsl((float)hueSlider.Value,
                                        (float)saturationSlider.Value,
                                        (float)lightnessSlider.Value);
        args.Surface.Canvas.Clear(color);

        hslLabel.Text = String.Format(" RGB = {0:X2}-{1:X2}-{2:X2} ",
                                      color.Red, color.Green, color.Blue);
    }

    void OnHsvCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKColor color = SKColor.FromHsv((float)hueSlider.Value,
                                        (float)saturationSlider.Value,
                                        (float)valueSlider.Value);
        args.Surface.Canvas.Clear(color);

        hsvLabel.Text = String.Format(" RGB = {0:X2}-{1:X2}-{2:X2} ",
                                      color.Red, color.Green, color.Blue);
    }
}

在 HSL 和 HSV 颜色模型中,色调值范围为 0 到 360,并会指示颜色的主导色调。 这些是彩虹的传统颜色:红色、橙色、黄色、绿色、蓝色、靛蓝色、紫罗兰色,并会循环回红色。

在 HSL 模型中,亮度的 0 值始终为黑色,100 值始终为白色。 当饱和度值为 0 时,0 到 100 之间的亮度值为不同的灰色。 增加饱和度会增加更多颜色。 当饱和度为 100,亮度为 50 时,会出现纯色(即 RGB 值中一个分量等于 255,另一个分量等于 0,第三个分量在 0 到 255 之间)。

在 HSV 模型中,纯色会在饱和度和值均为 100 时产生。 如果值为 0,不论其他设置如何,颜色均为黑色。 当饱和度为 0 且值范围为 0 到 100 时,会出现灰色阴影。

但是,了解这两种模式的最佳方法是亲自尝试:

“颜色浏览”页面的三重屏幕截图