.NET MAUI 开发电子木鱼(上)

news2025/1/16 14:07:07

本文介绍如何使用 .NET MAUI 开发一个电子木鱼应用。以实际的小应用开发为例,通过这个开发过程,介绍了其涉及的 .NET MAUI、Blazor、前端等相关知识点。文章涉及的应用已开源在 Github,大家可前往下载体验: https://github.com/sangyuxiaowu/MuYu

文章目录

  • 1. 背景
  • 2. 相关知识点
  • 3. 开发过程
    • 3.1 需求分析
    • 3.2 开发第一步
    • 3.3 核心功能
    • 3.4 动效设计
    • 3.5 菜单设计
    • 3.6 对话框
    • 3.7 数据存储
    • 3.8 Blazor 循环小 BUG
  • 4. 最后

1. 背景

电子木鱼不知道从什么时候火了起来,成了年轻人的新时尚。年轻人没有选择经常去寺庙像和尚那样念经,而是下载了电子木鱼软件,进行线上敲木鱼。虽然搞不懂这种“敲电子木鱼,见机甲佛祖,积赛博功德,修图灵真经”的赛博玄学,但是这个软件它做起来容易,需求还简单。在这么焦虑的假期生活中,还是假期的最后一天,最适合玩一玩 .NET MAUI 了。

请添加图片描述

2. 相关知识点

这个项目及文章使用并介绍了一下相关知识点:

  1. Blazor 播放音频,主要为 JS 调用
  2. Blazor 调用 .NET MAUI 各平台原生对话框
  3. .NET MAUI 的 Preferences 数据存储
  4. Blazor 循环渲染中的一个常见 BUG

界面 UI 方面因为使用还是 Web 的技术,所以更多的是前端的知识,可自行探索。

3. 开发过程

3.1 需求分析

这个电子木鱼整体功能非常简单,主要就是点击一下屏幕播放一下木鱼敲击的音频,这个是核心功能。

再往下设计,就是一些动效的优化,比如:

  1. 木鱼敲击的动效,放大
  2. 木鱼敲击的震动反馈,当然这一条可能没必要
  3. 木鱼敲击后的文字浮动,一敲“+999功德”这种的

更进一层的设计:

  1. 这里需要引入设置功能了,加入设置菜单
  2. 文字浮动内容的自定义
  3. 自动敲击积累功德
  4. 自定义的敲击音和木鱼样式,这里就要开始费美工了

再接下来迭代,当然也是有很多参考的相关案例,比如:

  1. 排行榜功能,区域排行,日,周,月各种排行榜单
  2. 分享功能,满足人们的分享欲
  3. 添加诵经背景音,使氛围更融洽

当然,以上只是瞎扯,作为一个清心寡欲的木鱼,做好自己定位是第一步。“天之道,不争而善胜”,作为佛系木鱼,当然只是纯粹的敲击,满足最基本的需求就可以了。

3.2 开发第一步

这个开发第一步,沿用我之前讲的那个《MAUI 安卓 UI 资源设置》文章的内容,先把 UI 资源处理一下。这次被老婆拒绝了,我亲自拿钢笔工具把这个木鱼 Logo 给绘制出来了,作为一个合格的全栈,美工也是需要会的。

请添加图片描述

这个 Logo 画完,内部应用的主体界面木鱼自然也就设计好了。

3.3 核心功能

电子木鱼的核心功能,当然是点击屏幕播放一下敲击音效了。因为我们使用的是 .NET MAUI Blazor ,为了后续界面动效的设计,我们通过前端的方式来播放音乐。

为了图方便,直接在 wwwroot/index.html HTML 入口文件中添加如下 audio 标签和 JS 方法:

<audio id="player">
    <source  src="other/muyu.wav" />
</audio>
<script>
window.PlayAudioFile = (tips) => {
    var audio = document.getElementById('player');
    audio.play();
}
</script>

在应用的首页添加图片点击的事件,来调用 JS,并预留了文字浮动内容的传递:

<img id="img" src="/images/muyu.svg" style="height:100%;max-width:100%" @onclick="ClickHandler" />
async Task ClickHandler()
{
    await JS.InvokeAsync<string>("PlayAudioFile", tipsInfo);
}

本小节相关知识点:《JavaScript 互操作》

3.4 动效设计

动效设计这里我们主要实现木鱼敲击放大动效和敲击后的文字浮动。这里主要是前端的技术,我们使用 CSS3 的 @keyframes 创建两个动画规则:

@keyframes showbig {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.05);
    }
}

@keyframes showtips {
    0% {
        opacity: 1;
        transform: translateY(0);
    }

    75% {
        opacity: .9;
        transform: translateY(-1.5em);
    }

    100% {
        opacity: 0;
        transform: translateY(-2em);
    }
}

这里两个动效都是默认是只循环一遍,时长 0.3s :

.showbig {
    animation-timing-function: ease-in-out;
    animation-duration: .3s;
    animation-name: showbig;
}

通过 JS 方式控制 DOM 的 Class 添加删除来实现动画的播放:

function ShowBig() {
    var img = document.getElementById('img');
    img.classList.add('showbig');
    setTimeout(() => {
        img.classList.remove('showbig');
    }, 300);
}

对于浮动的文字来说,快速敲击时,为了可以显示多条,这边的浮动文字是动态创建的 P 标签。还记得前面预留的 tipsInfo 吧,是在这里使用的。

function ShowTips(text) {
    var p = document.createElement("p");
    p.classList.add('tips');
    p.innerHTML = text;
    var tips = document.getElementById('tips');
    tips.appendChild(p);
    setTimeout(() => {
        tips.removeChild(p)
    }, 300);
}

3.5 菜单设计

有了设置当然就可以搞个菜单出来了,菜单这里的设计也非常简单,我们可以直接新建一个 Blazor 组件 Shared/SettingMenu.razor,来实现传递菜单项来生成菜单组件,并在点击选项后返回点击的菜单序号。

<div class="mask" style="display:@(Show?"flex":"none")">
    <div class="setting_menu">
        <div class="title">@MenuTitle</div>
        <ul class="list">
            @for (int i=0;i<NavData.Count(); i++)
            {
                <li @onclick="e=>MenuClick(i)">@NavData[i]</li>
            }
        </ul>
    </div>
</div>
/// <summary>
/// 点击回调
/// </summary>
[Parameter]
public EventCallback<int> OnMenuClick { get; set; }

/// <summary>
/// 菜单标题
/// </summary>
[Parameter]
public string MenuTitle { get; set; } = "设置";

/// <summary>
/// 显示隐藏
/// </summary>
[Parameter]
public bool Show { get; set; } = false;

/// <summary>
/// 菜单信息
/// </summary>
[Parameter]
public string[] NavData { get; set; }

/// <summary>
/// 通知点击事件
/// </summary>
/// <param name="inx">点击的index</param>
/// <returns></returns>
async Task MenuClick(int inx)
{
    await OnMenuClick.InvokeAsync(inx);
}

那,跑一下看看,似乎,大概,也许是非常完美:

请添加图片描述

3.6 对话框

在我们的悬浮文字设置选项中,需要提供输入功能。当然,直接前端的方案,设计一个模态对话框似乎是一个非常好的方案,也符合 UI 的一致性。但是考虑到能省则省,这边没有使用前端的框架,所以再手写一个会稍微费点事的。如果说省事,当然是直接前端 JS 的 prompt ,但是这样又显得格格不入:

请添加图片描述

.NET MAUI Blazor 这种混合开发的模式大多都一样,对话框也是一个小问题。不过这里可以非常方便的使用本机对话框,只要拿到 Page 即可,在 razor 文件的 code 代码块中,我们可以这样用:

string result = await Application.Current.MainPage.DisplayPromptAsync("悬浮文字设置", "请设置敲击后的自定义悬浮文字", "确定", "取消", "请输入悬浮文字", 5, null, tipsInfo);

本小节相关知识点:《显示弹出窗口》

3.7 数据存储

搞定了设置文字的交互,接下来就是存储了,总体来说我们需要存储的木鱼的总敲击次数,今日敲击次数和刚刚的自定义悬浮文字。毕竟不是复杂的应用,就没必要上数据库了,对于这种简单的键值数据存储我们可以直接选用 PreferencesPreferences 通过调用 Preferences.Set 方法来设置,提供键和值,以下就是完整的设置悬浮文字和存储的方法:

/// <summary>
/// 获取初始的悬浮文字
/// </summary>
private string tipsInfo = Preferences.Default.Get("tips", "功德+1");

async Task SetTips()
{
    string result = await Application.Current.MainPage.DisplayPromptAsync("悬浮文字设置", "请设置敲击后的自定义悬浮文字", "确定", "取消", "请输入悬浮文字", 5, null, tipsInfo);
    if (string.IsNullOrWhiteSpace(result)) return;
    Preferences.Default.Set("tips", result);
    tipsInfo = result;
}

本小节相关知识点:《Preferences》

3.8 Blazor 循环小 BUG

做的差不多了,菜单也有了,可以简单跑一下看看了,但是似乎菜单出了些问题,设置菜单的按钮并不好用,每一个回调都是 4:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ywko3Od-1675503302385)(./img/2.png)]

啊对,前面的完美,我是说界面。BUG 总会有的,还总是发生在不经意之间。

元芳你怎么看?

元芳:问一问 ChatGPT 吧,他说他懂这个的。

ChatGPT:是的,我了解Blazor。Blazor是一种用于开发Web应用程序的框架,允许使用C#代码和.NET运行时在浏览器中运行Web应用程序。它提供了一种方便的方法来使用.NET技术来构建客户端Web应用程序,而无需学习JavaScript。

仔细检查我们会发现,在前面的 SettingMenu.razor 菜单组件中,我们有一个循环,通过枚举NavData数组中的每一项并创建一个包含该项的列表项。因为需要序号,所以这里用的 for 循环,问题在于,在回调函数 MenuClick(i) 中,变量i是局部的,在循环结束后其值就改变了。

为了解决这个问题,可以将当前项的索引存储在闭包中:

<ul class="list">
    @for (int i = 0; i < NavData.Count(); i++)
    {
        int index = i;
        <li @onclick="e => MenuClick(index)">@NavData[i]</li>
    }
</ul>

修改完代码,再次运行:

请添加图片描述

4. 最后

至此,这个简单的电子木鱼的基础功能已实现,应用已经完成了大半。对于未尽的事宜,比如敲击计数和其他设置的功能我下次再说,容我先去敲一会木鱼,静个心。

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/198111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[架构之路-99]:《软件架构设计:程序员向架构师转型必备》-9-确定关键性需求与决定系统架构的因素

第9章 确定关键性需求与决定系统架构的因素9.1 概念架构是什么9.1.1 概念架构是直指目标的设计思想、重大选择9.1.2 案例1&#xff1a;汽车电子AUTOSAR——跨平台复用NA9.1.3 案例2&#xff1a;腾讯QQvideo架构——高性能NA9.1.4 案例3&#xff1a;微软MFC架构——简化开发NA9.…

断网后,是否能够ping通127.0.0.1?

引言 说起这个问题很搞笑&#xff0c;其实也是挺有意思的。是这么回事&#xff0c;公司突然断网了&#xff0c;有人突然来了一句&#xff0c;断网了&#xff0c;能不能ping通127.0.0.1呢&#xff1f;大家就实验起来了&#xff01; 结果显而易见&#xff0c;如上图&#xff0c;…

什么时候可以不进行测试?

如果存在任何原因导致不需要使用测试结果提供的信息&#xff0c;就没有必要进行测试。测试得到的信息不可靠&#xff0c;也没有必要测试。 1、测试后风险增加 软件行业的经理经常需要做出带风险的决定&#xff0c;通常在获得部分信息的情况下做出决定是比较保险的。但有些时候…

沿着数字中国的大江大河,领略云上三峡

长年以来&#xff0c;提到沿江旅行&#xff0c;国人脑海中浮现的画面一定是三峡。而在今天&#xff0c;沿着数字中国的大江大河溯源而上&#xff0c;也会看到一座云上三峡。郦道元在《水经注》里是这样描写三峡的“至于夏水襄陵&#xff0c;沿溯阻绝。有时朝发白帝&#xff0c;…

Docker - 11. 本地镜像发布到私有库

1. 为什么要有私有库&#xff1f; 如果涉及到公司机密文件&#xff0c;使用DockerHub、阿里云这样的公共镜像仓库就不合适&#xff0c;所以需要创建一个本地私人仓库提供给团队使用&#xff0c;基于公司内部项目构建镜像。而 Docker Registry是官方提供的工具&#xff0c;可以…

【技术应用】java实现排行榜方案

【技术应用】java实现排行榜方案一、前言二、实现方案方案一、通过数据库实现方案二、通过集合List实现数据排序功能方案三、通过redis的zset实现方案四、通过java中的sortedSet集合实现方案五、通过java的priorityQueue队列实现一、前言 最近在做一个项目的性能优化&#xff…

12、获取字符串信息

目录 一、获取字符串长度 二、字符串查找 &#xff08;1&#xff09;indexOf(String s) &#xff08;2&#xff09;lastIndexOf(String str) 三、获取指定索引位置的字符 一、获取字符串长度 使用String类的length()方法可获取声明的字符串对象的长度。 语法如下&#x…

Linux Centos9 Stream 安装mysql8

安装mysql8教程前言安装Mysql8.0使用Mysql yum 存储库进行安装。安装mysql8.0启动mysql 服务创建用户完成安装使用Navicat 连接刚装好的mysql如果博主的文章对您有所帮助&#xff0c;可以评论、点赞、收藏&#xff0c;支持一下博主!!!前言 操作系统&#xff1a;Linux Centos9 …

JAVA-Spring Bean作用域

目录 基本概念 Bean 作用域 spring支持的bean作用域有哪些&#xff1f; 近日研究Spring和SpringBoot的一些内容&#xff0c;给大家做一些分享&#xff0c;请大家多多提出您的宝贵意见。 学习知识要了解其涉及到的基本概念&#xff0c;才能理解这个知识&#xff0c;并且做到…

八种排序算法

文章目录1、冒泡排序1.基本思路2.代码实现3.时间复杂度和空间复杂度2、快速排序1.基本思路2.代码实现3.时间复杂度和空间复杂度3、直接插入1.基本思路2.代码实现3.时间复杂度和空间复杂度4、希尔排序1.基本思路2.代码实现3.时间复杂度和空间复杂度5、简单选择1.基本思路2.代码实…

数据库管理系统有哪些

文章目录RDBMS非RDBMSDocumentKey-valueGraphhttps://db-engines.com/en/ranking该网站根据各 DBMS的流行度&#xff0c;列出了它们的排名&#xff0c;每月更新一次。当前是2023年2月份的排名。DataBase Model这一列中显示了各 DBMS所使用的 数据模型&#xff0c;有的使用了单个…

SpringAMQP从0到1

初识MQ 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但是你却…

Redis最佳实践 | 黑马Redis高级篇

目录 一、Redis键值设计 1、优雅的key结构 2、BigKey问题 什么是BigKey BigKey的危害 如何发现BigKey 如何删除BigKey 3、恰当的数据类型 二、批处理优化 1、Pipeline 大量数据导入的方式 MSET Pipeline 2、集群下的批处理 三、服务端优化 1、持久化配置 2、慢…

MyBatis案例 | 使用映射配置文件实现CRUD操作——多条件查询

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JavaWeb Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&…

【论文阅读】Exathlon: A Benchmark for Explainable Anomaly Detection over Time Series

论文来源 标题: Exathlon: A Benchmark for Explainable Anomaly Detection over Time Series (Vincent Jacob,2021) 作者: Vincent Jacob, Fei Song, Arnaud Stiegler, Bijan Rad, Yanlei Diao, Nesime Tatbul 期刊: Proceedings of the VLDB Endowment 研究问题 Exathlon是…

尚医通(三)医院设置模块后端 | swagger | 统一日志 | 统一返回结果

目录一、医院设置模块需求二、医院设置表结构三、医院模块配置四、医院查询功能1、创建包结构&#xff0c;创建SpringBoot启动类2、编写controller代码3、创建SpringBoot配置类5、运行启动类6、统一返回的json时间格式五、医院设置逻辑删除功能1、HospitalSetController添加删除…

CDA Level Ⅱ 模拟题(二)

练习题 【单选题】1/20 一项针对全国25-35岁用户群的手机喜好调查&#xff0c;但调研项目经费大概是10万元&#xff0c;并且用户群相对集中在中国中部城市。前期预调研显示&#xff0c;用户群的数值方差和调研费用不等。以下哪种情况是比较适宜的调查方式&#xff1f; A.简单随…

【C++入门】

目录1、命名空间1.1、命名空间定义1.2、命名空间的使用2、C输入和输出3、缺省参数3.1 缺省参数概念3.2缺省参数分类4、函数重载4.1、函数重载概念4.2 C支持函数重载的原理--名字修饰5、引用5.1、引用概念5.2、引用特性5.3、常引用5.4、使用场景5.5、传值、传引用效率比较5.6、引…

【JavaEE】如何构造 HTTP请求认识HTTPS

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f6f0;️&#x1f6f0;️系列专栏:【JavaEE】 ✈️✈️本篇内容:如何构造 HTTP 请求同时认识HTTPS&#xff01; &#x1f680;&#x1f680;代码存放仓库gitee&#xff1a;JavaEE代码&#xff01; …

HW在即,那些被遗忘的物理安全还好吗?

近段时间&#xff0c;一个网络攻击的段子在互联网上火了起来。 “某公司被黑客勒索&#xff0c;每20分钟断一次网&#xff0c;给公司带来了极其严重的影响&#xff0c;但通过技术手段怎么也找不到问题。最后公司发现是黑客买通了保安&#xff0c;每20分钟拔一次网线。” 看完…