本系列参考https://github.com/xamarin/xamarin-forms-samples。先读懂代码,后续进行实战演练
App效果如图所示
前言
Xamarin.Forms是一款跨平台开发工具。能够并行开发iOS、Andriod、UWP软件。为什么选择Xamarin.Forms开发安卓,很大一部分原因是Andriod Studio对新手太不友好,之前配置Andriod开发环境配了几天,各种槽点,在此也不多提。而我们的微软爸爸收购了Xamarin,在我们熟悉的VS上即可开发Andriod APP,幸甚至哉!话不多说,开始学习。
环境准备
- VS 2019
- Xamarin 工具(利用VS installer下载即可)
步骤
- 选择Xamarin.Forms空白版式,命名为CatClock
- 观察结构
- 安装SkiaSharp和SkiaSharp.Views.Forms NuGet包,在GitHub上嫖到WoodGrain.png。CatClock目录结构如图所示。
- 更新CatClock.csproj代码
代码以贴出
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="1.68.1.1" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="1.68.1.1" />
<PackageReference Include="Xamarin.Forms" Version="4.3.0.908675" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="WoodGrain.png" />
</ItemGroup>
</Project>
- App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="CatClock.App">
<Application.Resources>
</Application.Resources>
</Application>
- App.xaml.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace CatClock
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
- MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
xmlns:local="clr-namespace:CatClock"
mc:Ignorable="d"
x:Class="CatClock.MainPage">
<skia:SKCanvasView x:Name="canvasView"
PaintSurface="canvasView_PaintSurface" />
</ContentPage>
- MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using System.Reflection;
using System.IO;
namespace CatClock
{
public partial class MainPage : ContentPage
{
//保存绘制几何的信息(text或者bitmap)
SKPaint blackFillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Black
};
SKPaint whiteStrokePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.White,
StrokeWidth = 2,
StrokeCap = SKStrokeCap.Round,
IsAntialias = true
};
SKPaint whiteFillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.White
};
SKPaint greenFillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.PaleGreen
};
SKPaint blackStrokePaint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Black,
StrokeWidth = 20,
StrokeCap = SKStrokeCap.Round
};
SKPaint grayFillPaint = new SKPaint
{
Style = SKPaintStyle.Fill,
Color = SKColors.Gray
};
//背景
SKPaint backgroundFillPaint = new SKPaint
{
Style = SKPaintStyle.Fill
};
//保存绘制路径
SKPath catEarPath = new SKPath();
SKPath catEyePath = new SKPath();
SKPath catPupilPath = new SKPath();
SKPath catTailPath = new SKPath();
//基于SVG创建时钟动作的路径
SKPath hourHandPath = SKPath.ParseSvgPathData(
"M 0 -60 C 0 -30 20 -30 5 -20 L 5 0 C 5 7.5 -5 7.5 -5 0 L -5 -20 C -20 -30 0 -30 0 -60");
SKPath minuteHandPath = SKPath.ParseSvgPathData(
"M 0 -80 C 0 -75 0 -70 2.5 -60 L 2.5 0 C 2.5 5 -2.5 5 -2.5 0 L -2.5 -60 C 0 -70 0 -75 0 -80");
public MainPage()
{
InitializeComponent();
//猫的耳朵路径
catEarPath.MoveTo(0, 0);
catEarPath.LineTo(0, 75);
catEarPath.LineTo(100, 75);
catEarPath.Close();
//猫眼睛路径
catEyePath.MoveTo(0, 0);
catEyePath.ArcTo(50, 50, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 50, 0);
catEyePath.ArcTo(50, 50, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 0, 0);
catEyePath.Close();
//瞳孔路径
catPupilPath.MoveTo(25, -5);
catPupilPath.ArcTo(6, 6, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 25, 5);
catPupilPath.ArcTo(6, 6, 0, SKPathArcSize.Small, SKPathDirection.Clockwise, 25, -5);
catPupilPath.Close();
//猫尾路径
catTailPath.MoveTo(0, 100);
catTailPath.CubicTo(50, 200, 0, 250, -50, 200);
//Cubic-Bezier, 贝塞尔曲线,控制动画
//创建着色器
Assembly assembly = GetType().GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("CatClock.WoodGrain.png"))
using (SKManagedStream skStream = new SKManagedStream(stream))
using (SKBitmap bitmap = SKBitmap.Decode(skStream))
using (SKShader shader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror))
{
backgroundFillPaint.Shader = shader;
}
//开始计时,让Canvas不断绘制自身,这里每秒绘制60次
Device.StartTimer(TimeSpan.FromSeconds(1f / 60), () => {
canvasView.InvalidateSurface();
return true;
});
}
private void canvasView_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
//获得手机surface
SKSurface surface = e.Surface;
//获得画板(包含surface或bitmap)
SKCanvas canvas = surface.Canvas;
canvas.DrawPaint(backgroundFillPaint);
int width = e.Info.Width;
int height = e.Info.Height;
//设置transform
canvas.Translate(width / 2, height / 2);//设定视图位置
canvas.Scale(Math.Min(width / 340f, height / 340f));//设定缩放大小
//获取当前时间
DateTime dateTime = DateTime.Now;
//绘制头部
canvas.DrawCircle(0, -160, 75, blackFillPaint);
//绘制猫耳和眼睛
for (int i = 0; i < 2; i++)
{
canvas.Save();
canvas.Scale(2 * i - 1, 1);
canvas.Save();
canvas.Translate(-65, -255);
canvas.DrawPath(catEarPath, blackFillPaint);
canvas.Restore();
canvas.Save();
canvas.Translate(10, -170);
canvas.DrawPath(catEyePath, greenFillPaint);
canvas.DrawPath(catPupilPath, blackFillPaint);
canvas.Restore();
// 绘制胡须
canvas.DrawLine(10, -120, 100, -100, whiteStrokePaint);
canvas.DrawLine(10, -125, 100, -120, whiteStrokePaint);
canvas.DrawLine(10, -130, 100, -140, whiteStrokePaint);
canvas.DrawLine(10, -135, 100, -160, whiteStrokePaint);
canvas.Restore();
}
//让尾巴动起来
float t = (float)Math.Sin((dateTime.Second % 2 + dateTime.Millisecond / 1000.0) * Math.PI);
catTailPath.Reset();
catTailPath.MoveTo(0, 100);
SKPoint point1 = new SKPoint(-50 * t, 200);
SKPoint point2 = new SKPoint(0, 250 - Math.Abs(50 * t));
SKPoint point3 = new SKPoint(50 * t, 250 - Math.Abs(75 * t));
catTailPath.CubicTo(point1, point2, point3);
//绘制尾巴
canvas.DrawPath(catTailPath, blackStrokePaint);
//绘制钟表背景
canvas.DrawCircle(0, 0, 100, blackFillPaint);
//刻度
for (int angle = 0; angle < 360; angle += 6)
{
canvas.DrawCircle(0, -90, angle % 30 == 0 ? 4 : 2, whiteFillPaint);
canvas.RotateDegrees(6);
}
//时针
canvas.Save();
canvas.RotateDegrees(30 * dateTime.Hour + dateTime.Minute / 2f);
canvas.DrawPath(hourHandPath, grayFillPaint);
canvas.DrawPath(hourHandPath, whiteStrokePaint);
canvas.Restore();
//分针
canvas.Save();
canvas.RotateDegrees(6 * dateTime.Minute + dateTime.Second / 10f);
canvas.DrawPath(minuteHandPath, grayFillPaint);
canvas.DrawPath(minuteHandPath, whiteStrokePaint);
canvas.Restore();
//秒针
canvas.Save();
float seconds = dateTime.Second + dateTime.Millisecond / 1000f;
canvas.RotateDegrees(6 * seconds);
whiteStrokePaint.StrokeWidth = 2;
canvas.DrawLine(0, 10, 0, -80, whiteStrokePaint);
canvas.Restore();
}
}
}