Xamarin.Forms学习之路——黑猫时钟App

Xamarin.Forms学习之路——黑猫时钟App

本系列参考https://github.com/xamarin/xamarin-forms-samples。先读懂代码,后续进行实战演练

App效果如图所示
在这里插入图片描述

前言

Xamarin.Forms是一款跨平台开发工具。能够并行开发iOS、Andriod、UWP软件。为什么选择Xamarin.Forms开发安卓,很大一部分原因是Andriod Studio对新手太不友好,之前配置Andriod开发环境配了几天,各种槽点,在此也不多提。而我们的微软爸爸收购了Xamarin,在我们熟悉的VS上即可开发Andriod APP,幸甚至哉!话不多说,开始学习。

环境准备

  1. VS 2019
  2. Xamarin 工具(利用VS installer下载即可)

步骤

  1. 选择Xamarin.Forms空白版式,命名为CatClock
  2. 观察结构
    在这里插入图片描述
  3. 安装SkiaSharp和SkiaSharp.Views.Forms NuGet包,在GitHub上嫖到WoodGrain.png。CatClock目录结构如图所示。
    在这里插入图片描述
  4. 更新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>
  1. 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>
  1. 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()
        {
        }
    }
}
  1. 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>

  1. 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();
        }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值