当前位置: 首页 > news >正文

[MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条

文章目录

    • 关于图形绘制
    • 创建自定义控件
    • 使用控件
    • 创建专辑封面
    • 项目地址

我们将绘制一个圆形的音乐播放控件,它包含一个圆形的进度条、专辑页面和播放按钮。

在这里插入图片描述

关于图形绘制

使用MAUI的绘制功能,需要Microsoft.Maui.Graphics库。

Microsoft.Maui.Graphics 是一个实验性的跨平台图形库,它可以在 .NET MAUI 中使用。它提供了一组基本的图形元素,如矩形、圆形、线条、路径、文本和图像。它还提供了一组基本的图形操作,如填充、描边、裁剪、变换和渐变。

Microsoft.Maui.Graphics在不同的目标平台上使用一致的API访问本机图形功能,而底层实现使用了不同的图形渲染引擎。其中通用性较好的是SkiaSharp图形库,支持几乎所有的操作系统,在不同平台上的表现也近乎一致。

创建自定义控件

在项目中添加SkiaSharp绘制功能的引用Microsoft.Maui.Graphics.Skia以及SkiaSharp.Views.Maui.Controls

<ItemGroup><PackageReference Include="Microsoft.Maui.Graphics.Skia" Version="7.0.59" /><PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.3" />
</ItemGroup>

创建CircleSlider.xaml文件,添加如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:forms="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"x:Class="MatoMusic.Controls.CircleSlider"><ContentView.Content><forms:SKCanvasView x:Name="canvasView"PaintSurface="OnCanvasViewPaintSurface" /></ContentView.Content>
</ContentView>

SKCanvasView是SkiaSharp.Views.Maui.Controls封装的View控件。

打开CircleSlider.xaml.cs文件

控件将包含以下可绑定属性:

  • Maximum:最大值
  • Minimum:最小值
  • Value:当前值
  • TintColor:进度条颜色
  • ContainerColor:进度条背景颜色
  • BorderWidth:进度条宽度

定义两个SKPaint画笔属性,OutlinePaint用于绘制进度条背景,ArcPaint用于绘制进度条本身。他们的描边宽度StrokeWidth则是圆形进度条的宽度。
两个画笔的初始值样式为SKPaintStyle.Stroke,描边宽度为BorderWidth的值。

private SKPaint _outlinePaint;public SKPaint OutlinePaint
{get{if (_outlinePaint == null){SKPaint outlinePaint = new SKPaint{Style = SKPaintStyle.Stroke,StrokeWidth = BorderWidth,};_outlinePaint = outlinePaint;}return_outlinePaint;}set { _outlinePaint = value; }
}private SKPaint _arcPaint;public SKPaint ArcPaint
{get{if (_arcPaint == null){SKPaint arcPaint = new SKPaint{Style = SKPaintStyle.Stroke,StrokeWidth = BorderWidth,};_arcPaint = arcPaint;}return _arcPaint;}set { _arcPaint = value; }
}

SetStrokeWidth用于设置描边宽度,并产生一个动效,

在BorderWidth发生变更的时候,会出现一个动效。宽度会缓慢地变化至新的值。刷新率为10ms一次,每次变化的值为1。


private float _borderWidth;public float BorderWidth
{get { return _borderWidth; }set{var old_borderWidth = _borderWidth;var span = value - old_borderWidth;SetStrokeWidth(span, old_borderWidth);_borderWidth = value;this.ArcPaint.StrokeWidth = _borderWidth;this.OutlinePaint.StrokeWidth = _borderWidth;}
}private async void SetStrokeWidth(float span, float old_borderWidth)
{if (span > 0){for (int i = 0; i <= span; i++){await Task.Delay(10);this.ArcPaint.StrokeWidth = old_borderWidth + i;this.OutlinePaint.StrokeWidth = old_borderWidth + i;RefreshMainRectPadding();}}else{for (int i = 0; i >= span; i--){await Task.Delay(10);this.ArcPaint.StrokeWidth = old_borderWidth + i;this.OutlinePaint.StrokeWidth = old_borderWidth + i;RefreshMainRectPadding();}}}

于此同时,因为描边宽度变化了,需要对Padding进行补偿。调用RefreshMainRectPadding方法计算一个新的Padding值,BoderWidth缩小时,Padding也随之增大。

private void RefreshMainRectPadding()
{this._mainRectPadding =  this.BorderWidth / 2;
}

在视觉上,进度条宽度从内向外扩张变细。

在这里插入图片描述

若设为原宽度减去计算值,从视觉上是从外向内收缩变细。

private void RefreshMainRectPadding()
{this._mainRectPadding =  15 -  this.BorderWidth / 2;
}

在这里插入图片描述

接下来写订阅了CanvaseView的PaintSurface事件的方法OnCanvasViewPaintSurface。在这个方法中,我们将编写圆形进度条的绘制逻辑。

PaintSurface事件在绘制图形时触发。程序运行时会实时触发这个方法,它的参数SKPaintSurfaceEventArgs事件附带的对象具有两个属性:

  • Info类型SKImageInfo
  • Surface类型SKSurface

SKImageInfo对象包含如宽度和高度等有关绘图区域的信息,对象SKSurface为绘制本身,我们需要利用SKImageInfo宽度和高度等信息,结合业务数据,在SKSurface绘制出我们想要的图形。

清空上一次绘制的图形,调用SKSurface.Canvas获取Canvas对象,调用Canvas.Clear方法清空上一次绘制的图形。

canvas.Clear();

rect是一个SKRect对象,进度条本身是圆形,我们需要一个正方形的区域来控制圆形区域。

sweepAngle是当前进度对应的角度,首先计算出总进度值,通过计算当前进度对应总进度的比值,换算成角度,将这一角度赋值给sweepAngle。

startAngle是进度条的起始角度,我们将其设置为-90度,即从正上方开始绘制。


SKRect rect = new SKRect(_mainRectPadding, _mainRectPadding, info.Width - _mainRectPadding, info.Height - _mainRectPadding);
float startAngle = -90;
float sweepAngle = (float)((Value / SumValue) * 360);

调用Canvas.DrawOval,使用OutlinePaint画笔绘制进度条背景,它是一个圆形

canvas.DrawOval(rect, OutlinePaint);

创建绘制路径path,调用AddArc方法,将rect对象和起始角度和终止角度传入,即可绘制出弧形。

using (SKPath path = new SKPath())
{path.AddArc(rect, startAngle, sweepAngle);canvas.DrawPath(path, ArcPaint);
}

绘制部分的完整代码如下:

private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{var SumValue = Maximum - Minimum;SKImageInfo info = args.Info;SKSurface surface = args.Surface;SKCanvas canvas = surface.Canvas;canvas.Clear();SKRect rect = new SKRect(_mainRectPadding, _mainRectPadding, info.Width - _mainRectPadding, info.Height - _mainRectPadding);float startAngle = -90;float sweepAngle = (float)((Value / SumValue) * 360);canvas.DrawOval(rect, OutlinePaint);using (SKPath path = new SKPath()){path.AddArc(rect, startAngle, sweepAngle);canvas.DrawPath(path, ArcPaint);}
}

使用控件

在MainPage.xaml中添加一个CircleSlider控件,
设置的Maximum,是当前曲目的时长,Value是当前曲目的进度

<controls:CircleSlider HeightRequest="250"WidthRequest="250"x:Name="MainCircleSlider"Maximum="{Binding Duration}"Minimum="0.0"TintColor="#FFFFFF"ContainerColor="#4CFFFFFF"IsEnabled="{Binding Canplay}"ValueChanged="OnValueChanged"Value="{Binding CurrentTime,Mode=TwoWay} ">
</controls:CircleSlider>

创建专辑封面

使用MAUI的VisualElement中的Clip属性,创建Clip裁剪,可以传入一个Geometry对象,这里我们使用RoundRectangleGeometry,将它的CornerRadius属性设置为图片宽度的一半,即可实现圆形图片。

<Image HeightRequest="250"WidthRequest="250"Margin="7.5"Source="{Binding  CurrentMusic.AlbumArt}"VerticalOptions="CenterAndExpand"HorizontalOptions="CenterAndExpand"Aspect="AspectFill"><Image.Clip><RoundRectangleGeometry  CornerRadius="125" Rect="0,0,250,250" /></Image.Clip>
</Image>

设置一个半透明背景的播放状态指示器,当IsPlaying为False时将显示一个播放按钮

<Grid IsVisible="{Binding IsPlaying, Converter={StaticResource True2FalseConverter}}"><BoxView HeightRequest="250"WidthRequest="250"Margin="7.5"Color="#60000000"VerticalOptions="CenterAndExpand"HorizontalOptions="CenterAndExpand"CornerRadius="250" ></BoxView><Label  x:Name="PauseLabel"                               HorizontalOptions="CenterAndExpand"FontSize="58"  TextColor="{Binding Canplay,Converter={StaticResource Bool2StringConverter},ConverterParameter=White|#434343}"FontFamily="FontAwesome"Margin="0"></Label>
</Grid>

在这里插入图片描述

创建PanContainer对象,用于实现拖动效果,设置AutoAdsorption属性为True,即可实现拖动后自动吸附效果。

关于PanContainer请查看上期的文章:平移手势交互

用一个Grid将专辑封面,CircleSlider,以及播放状态指示器包裹起来。完整代码如下

 <controls1:PanContainer BackgroundColor="Transparent"x:Name="DefaultPanContainer"OnTapped="DefaultPanContainer_OnOnTapped"AutoAdsorption="True"OnfinishedChoise="DefaultPanContainer_OnOnfinishedChoise"><Grid PropertyChanged="BindableObject_OnPropertyChanged"VerticalOptions="Start"HorizontalOptions="Start"><Image HeightRequest="250"WidthRequest="250"Margin="7.5"Source="{Binding  CurrentMusic.AlbumArt}"VerticalOptions="CenterAndExpand"HorizontalOptions="CenterAndExpand"Aspect="AspectFill"><Image.Clip><RoundRectangleGeometry  CornerRadius="125" Rect="0,0,250,250" /></Image.Clip></Image><controls:CircleSlider>...</controls:CircleSlider><Grid IsVisible="{Binding IsPlaying, Converter={StaticResource True2FalseConverter}}"><BoxView HeightRequest="250"WidthRequest="250"Margin="7.5"Color="#60000000"VerticalOptions="CenterAndExpand"HorizontalOptions="CenterAndExpand"CornerRadius="250" ></BoxView><Label  x:Name="PauseLabel"                               HorizontalOptions="CenterAndExpand"FontSize="58"  TextColor="{Binding Canplay,Converter={StaticResource Bool2StringConverter},ConverterParameter=White|#434343}"FontFamily="FontAwesome"Margin="0"></Label></Grid></Grid>
</controls1:PanContainer>

在这里插入图片描述
以上就是这个项目的全部内容,感谢阅读

项目地址

Github:maui-samples

相关文章:

[MAUI 项目实战] 手势控制音乐播放器(四):圆形进度条

文章目录 关于图形绘制创建自定义控件使用控件创建专辑封面项目地址 我们将绘制一个圆形的音乐播放控件&#xff0c;它包含一个圆形的进度条、专辑页面和播放按钮。 关于图形绘制 使用MAUI的绘制功能&#xff0c;需要Microsoft.Maui.Graphics库。 Microsoft.Maui.Graphics 是…...

web路径专题+会话技术

目录 自定义快捷键 1. 工程路径问题及解决方案1.1 相对路径1.2 相对路径缺点1.3 base标签1.4 作业11.5 作业21.6注意细节1.7 重定向作业1.8 web工程路径优化 2. Cookie技术2.1 Cookie简单示意图2.2 Cookie常用方法2.2 Cookie创建2.3 Cookie读取2.3.1 JSESSIONID2.3.2 读取指定C…...

Jetpack Compose 实战 宝可梦图鉴

文章目录 前言实现效果一、架构介绍二、一些的功能点的介绍加载图片并获取主色,再讲主色设置为背景一个进度缓慢增加的圆形进度条单Activity使用navigation跳转Compose可组合项返回时页面重组的问题hiltViewModel() 主要参考项目总结 前言 阅读本文需要一定compose基础&#x…...

高效时间管理日历 DHTMLX Event Calendar 2.0.3 Crack

DHTMLX Event Calendar用于高效时间管理的轻量级 JavaScript 事件日历 DHTMLX 可帮助您开发类似 Google 的 JavaScript 事件日历&#xff0c;以高效地组织约会。 用户可以通过拖放来管理事件&#xff0c;并以六种不同的模式显示它们。 JavaScript 事件日历功能 轻的简单的 Java…...

ASIC-WORLD Verilog(2)FPGA的设计流程

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。 这是网站原文&…...

数字化体验时代,企业如何做好内部知识数字化管理

随着数字化时代的到来&#xff0c;企业内部的知识管理也面临着新的挑战和机遇。数字化技术的应用&#xff0c;可以极大地提高企业内部知识的数字化管理效率和质量&#xff0c;从而提升企业内部的工作效率、员工满意度和企业竞争力。本文将从数字化时代的背景出发&#xff0c;探…...

Qt5.12實戰之Linux靜態庫與動態庫多文件生成a與so文件並調用

1.編輯並輸入內容到test.cpp與test2.cpp test.cpp #include <stdio.h> int func() {return 888; } test2.cpp #include <stdio.h> int func2() {return 999; } 將test.cpp與test2.cpp編譯成目標文件&#xff1a; g -c test.cpp test2.cpp 一次性生成目標文件…...

Spring 之初始化前中后详解

Spring 框架是一个非常流行的 Java 框架&#xff0c;它提供了一种轻量级的、可扩展的方式来构建企业级应用程序。在 Spring 的生命周期中&#xff0c;有三个重要的阶段&#xff0c;即初始化前、初始化、初始化后。这篇文章将详细介绍这些阶段&#xff0c;并提供相应的源代码示例…...

企业数字化转型路上的陷阱有哪些

近年来&#xff0c;随着科技的快速发展&#xff0c;越来越多的企业开始了数字化转型的征程&#xff0c;希望通过数字化技术来提高企业的效率、降低成本、提升竞争力。然而&#xff0c;数字化转型也存在许多陷阱&#xff0c;如果不注意&#xff0c;可能会导致企业陷入困境。下面…...

Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++)

Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强&#xff08;C&#xff09; Baumer工业相机Baumer工业相机使用图像算法增加图像的技术背景Baumer工业相机通过BGAPI SDK联合OpenCV使用图像增强算法1.引用合适的类文件2.BGAPI SDK在图像回调中引用…...

面试官:“你会组件化开发操作吗?它的优势在哪?”

随着 Android 版本的不断更新升级和用户对 APP 产品需求技术越来越高&#xff0c;相对的各大公司对 Android 开发者们设置的招聘门槛也越来越高。 至于如何去看一个开发者水平的高低&#xff0c;一般看面试官会怎么问&#xff0c;会问哪些部分的技术内容&#xff1f; 一般公司…...

腾讯新增长,AI扛大旗?

经历了疫情期间的低谷与波折&#xff0c;腾讯正在恢复它的活力。 3月22日&#xff0c;腾讯发布了2022年第四季度及全年财报。财报显示&#xff0c;2022全年营收为5546亿元人民币&#xff0c;归母净利润(Non-IFRS)为1156亿元人民币&#xff1b;2022年腾讯第四季度的营收为1450亿…...

项目6:实现数据字典的展示与缓存

项目6:实现数据字典的展示与缓存 1.数据字典如何展示? 2.前后端如何设计? 3.前端设计代码? 4.后端设计代码? 5.实现数据字典缓存到redis 项目6:实现数据字典的展示与缓存 1.数据字典如何展示? ①数据字典展示 树形结构②...

JsNode算法题acm模式输入

js分为jsNode和JsV8两种输入输出方式&#xff0c;一般的执行代码还是一样的 牛客是两种都支持 华为的题目大多只支持jsNode 本文主要介绍jsNode的输入 JsNode 首先他是逐行输入的&#xff0c;就和py差不多&#xff0c;一定是每行每行地输入&#xff0c;用字符串line&#x…...

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 目录 Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 0.创建数据库 1. 在resources目录下创建db.properties文件 2. /** * 获取链接与释放资源的工具类--JdbcUtil类 */ 3…...

(十七)排序算法-基数排序

1 基本介绍 1.1 概述 &#xff08;1&#xff09;基数排序&#xff08;radix sort&#xff09;属于“分配式排序”&#xff0c;顾名思义&#xff0c;它是通过键值的各个位的值&#xff0c;将要排序的元素分配至某些“桶”中&#xff0c;达到排序的作用。 &#xff08;2&#x…...

JMM之先行发生原则(happens-before)详解

1、概述 在JMM规范下&#xff0c;如果一个操作执行的结果需要对另一个操作可见&#xff0c;那么这两个操作之间必须存在happers-before(先行发生)原则。 例如 int x 10 ; int y x; 这两行代码中第二个操作 yx &#xff0c;因为需要将x值赋值给y&#xff0c;所以第一行代码的…...

含分布式电源的配电网可靠性评估研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

安全加固服务是什么?哪些行业需要做?

安全加固服务是什么&#xff1f;安全加固服务是一种针对企业信息系统、网络设备、应用程序等进行安全加固和优化的服务。安全加固服务的主要目的是保障企业信息系统的安全性和稳定性&#xff0c;有效防范各类网络攻击和安全威胁。 安全加固服务是什么&#xff1f;通常包括以下…...

好程序员:Java书籍推荐,程序员必看的5本Java书籍,赶紧收藏!

今天好程序员给大家推荐5本Java书籍&#xff0c;各大高校都在使用&#xff08;具体名单如下&#xff09;&#xff0c;所有学习Java的程序员都不应该错过&#xff01; 第一本Java书籍《Java EE&#xff08;SSM框架&#xff09;企业应用实战》 本书全面介绍了JavaEE中MyBatis、Sp…...

PlatformIO脚本进阶:告别修改库文件,用Python脚本精准控制FreeRTOS heap文件编译

PlatformIO脚本进阶&#xff1a;精准控制FreeRTOS堆管理文件编译的工程实践 在嵌入式开发中&#xff0c;第三方库的管理一直是个令人头疼的问题。特别是像FreeRTOS这样的实时操作系统&#xff0c;其源代码结构往往包含多个可选组件&#xff0c;开发者需要根据具体硬件和需求选择…...

SecGPT-14B实操手册:Gradio界面中temperature=0.3对安全答案确定性的影响

SecGPT-14B实操手册&#xff1a;Gradio界面中temperature0.3对安全答案确定性的影响 1. 引言&#xff1a;为什么安全问答需要“确定性”&#xff1f; 想象一下&#xff0c;你正在向一位网络安全专家咨询一个紧急的安全漏洞问题。你希望得到的回答是清晰、准确、且唯一的正确答…...

如何零门槛集成专业金融图表?从技术选型到上线的全流程攻略

如何零门槛集成专业金融图表&#xff1f;从技术选型到上线的全流程攻略 【免费下载链接】charting-library-examples Examples of Charting Library integrations with other libraries, frameworks and data transports 项目地址: https://gitcode.com/gh_mirrors/ch/charti…...

Fillinger终极指南:Illustrator智能填充脚本如何10倍提升你的设计效率

Fillinger终极指南&#xff1a;Illustrator智能填充脚本如何10倍提升你的设计效率 【免费下载链接】illustrator-scripts Adobe Illustrator scripts 项目地址: https://gitcode.com/gh_mirrors/il/illustrator-scripts 你是否曾在Illustrator中为了填充图案而花费数小时…...

FLUX.1-dev开源镜像部署教程:像素幻梦免配置环境3步快速上手

FLUX.1-dev开源镜像部署教程&#xff1a;像素幻梦免配置环境3步快速上手 1. 像素幻梦简介 像素幻梦(Pixel Dream Workshop)是一款基于FLUX.1-dev扩散模型构建的像素艺术生成工具。它采用独特的16-bit像素风格界面设计&#xff0c;为创作者提供沉浸式的AI绘图体验。 与传统AI…...

实战指南:基于快马生成代码构建支持验证码的2048论坛登录系统

实战指南&#xff1a;基于快马生成代码构建支持验证码的2048论坛登录系统 最近在开发一个2048游戏社区时&#xff0c;需要为论坛设计一个安全可靠的登录入口。这个登录系统不仅要考虑用户体验&#xff0c;还要兼顾安全性。通过InsCode(快马)平台生成的代码作为基础&#xff0c…...

5G NR物理层实战:如何利用TS 38.211优化无线资源管理

5G NR物理层实战&#xff1a;TS 38.211无线资源管理优化指南 在5G网络部署的深水区&#xff0c;无线资源管理&#xff08;RRM&#xff09;的精细化程度直接决定了网络性能天花板。作为3GPP物理层协议集的核心文档&#xff0c;TS 38.211规范中隐藏着诸多未被充分挖掘的优化密钥—…...

别再手动敲代码了!用通义千问+PHPStudy,30分钟搞定一个带数据库的登录注册系统

零基础30分钟构建登录系统&#xff1a;AIPHPStudy极速开发指南 上周帮学妹调试课程设计时&#xff0c;我发现90%的初学者都在重复造轮子——手动编写那些千篇一律的表单验证和数据库连接代码。其实借助现代开发工具链&#xff0c;完全可以在喝杯咖啡的时间里搭建出完整的登录注…...

Ansys SCDM高效建模技巧:从基础到进阶

1. 初识Ansys SCDM&#xff1a;工程师的3D建模利器 第一次打开Ansys SpaceClaim Direct Modeler&#xff08;简称SCDM&#xff09;时&#xff0c;你可能会有种相见恨晚的感觉。这个被工程师们称为"几何手术刀"的软件&#xff0c;用起来比传统CAD软件顺手得多。我当年…...

ChatTTS 小说播音参数优化指南:如何实现自然流畅的语音合成

最近在做一个有声小说项目&#xff0c;尝试了多种语音合成方案&#xff0c;最终发现 ChatTTS 在中文小说播音的灵活性和自然度上表现相当不错。不过&#xff0c;刚上手时&#xff0c;直接使用默认参数生成的语音总感觉“味儿不对”&#xff0c;要么像机器人念稿&#xff0c;要么…...