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

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

1. 背景

前面我们介绍了 《.NET MAUI 开发电子木鱼(上)》 ,接下来进行设置相关功能的开发。主要包含:敲击计数和自动敲击模式切换。

2. 相关知识点

本篇主要有如下相关知识点:

  1. .NET MAUI 生命周期
  2. .NET MAUI Blazor 在应用关闭前保存数据
  3. Blazor 中的计时器,System.Threading.Timer 的使用和启停

3. 开发过程

3.1 敲击计数

敲击计数这里主要记录当日敲击数和总敲击数,为了满足各路神仙的敲击,这里我们用 long,还是使用 Preferences 存储。

private long AllNum = 0;
private long TodayNum = 0;

在主页面 Index.razor 首次渲染时,我们读取存储的计数信息。

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        AllNum = Preferences.Default.Get("all_num", 0L);
        if (DateTime.Now.ToShortDateString() == Preferences.Default.Get("today",""))
        {
            TodayNum = Preferences.Default.Get("today_num", 0L);
        }
    }
}
需要注意的是: AllNumlong 型, Preferences.Default.Get 传的默认值是为了推断取得的存储内容的类型,所以这里要用 0L 需要标记为 long 型。否则首次启动无错误,当存储过数据后就会报错,数据类型无法转换。

接下来我们只需要实现一个计数的添加和存储功能即可:

void AddNum()
{
    // 当前日期
    var today = DateTime.Now.ToShortDateString();
    // 存储的日期
    string save_today = Preferences.Default.Get("today", "");

    // 天数未发生变更,防的就是半夜敲的
    if (save_today == today)
    {
        TodayNum++;
    }else{
        TodayNum = 1;
        Preferences.Default.Set("today", today);
    }

    AllNum++;

    Preferences.Default.Set("today_num", TodayNum);
    Preferences.Default.Set("all_num", AllNum);
}

3.2 计数问题优化

前面一节虽然实现了敲击计数,但是设计也存在一些不合理的地方:频繁的 Preferences 写入毕竟不好。毕竟这种应用,没必要实时保存了,最好是获取到应用的关闭事件,在应用关闭时保存即可。

但是因为 .NET MAUI Blazor 这种混合开发的模式,情况会稍微复杂一点,走的弯路这里就不再提及了。无论如何处理,我们首先还是需要了解一下 .NET MAUI 应用的生命周期。这里为了方便跨平台处理,我们可以选择跨平台的生命周期事件 Stopped:当窗口不再可见时,将引发此事件。此时我们就可以做一些关键数据存储了。

而对于敲击计数的功能,我们采用一个全局的静态类进行管理,方便在 Blazor 和 App 中访问。在 Data 目录下创建如下类 HitCounter.cs

public static class HitCounter
{
    private static long count;
    private static long todayCount;
    private static string today;

    static HitCounter()
    {
        count = Preferences.Default.Get("all_num", 0L);
        todayCount = 0;
        today = DateTime.Now.ToShortDateString();
        if (today == Preferences.Default.Get("today", ""))
        {
            todayCount = Preferences.Default.Get("today_num", 0L);
        }
    }

    /// <summary>
    /// 敲击后更新计数
    /// </summary>
    public static void Increment()
    {
        if (today != DateTime.Now.ToShortDateString())
        {
            today = DateTime.Now.ToShortDateString();
            todayCount = 0;
        }
        count++;
        todayCount++;
    }

    /// <summary>
    /// 保存数据到 Preferences
    /// </summary>
    public static void Save()
    {
        if (today != DateTime.Now.ToShortDateString())
        {
            today = DateTime.Now.ToShortDateString();
            todayCount = 0;
        }
        Preferences.Default.Set("today_num", todayCount);
        Preferences.Default.Set("today", today);
        Preferences.Default.Set("all_num", count);
    }

    /// <summary>
    /// 总计数
    /// </summary>
    public static long Count
    {
        get { return count; }
    }

    /// <summary>
    /// 当日计数
    /// </summary>
    public static long TodayCount
    {
        get { return todayCount; }
    }
}

要订阅 Window 生命周期事件,则需要在 App.xaml.cs 文件的 App 类中重写 CreateWindow 方法,并在其中添加订阅事件的实例:

protected override Window CreateWindow(IActivationState activationState)
{
    Window window = base.CreateWindow(activationState);

    window.Stopped += (s, e) =>
    {
        Data.HitCounter.Save();
    };

    return window;
}

Index.razor 则不需要重写 OnAfterRenderAsync ,直接赋值即可:

private long AllNum = Data.HitCounter.Count;
private long TodayNum = Data.HitCounter.TodayCount;

但是,此后敲击计数的更新并不是实时的在 Blazor 界面中显示了。在需要更新显示的时候,比如这里的动作是点开设置菜单显示在右上角,则只需要在打开菜单的事件中重新赋值:

void ShowMenu()
{
    ShowSetting = true;
    // 更新数据
    AllNum = Data.HitCounter.Count;
    TodayNum = Data.HitCounter.TodayCount;
}

计数显示

3.3 自动敲击

自动敲击,积攒功德也是电子木鱼的一个重要功能。在 Blazor 中,我们可以使用 System.Threading.TimerJavaScript Interop 来实现计时器。这里选择使用 System.Threading.Timer 来实现。

首先我们需要了解一下 Timer(TimerCallback callback, object state, int dueTime, int period); 的参数:

参数 说明
callback 委托将会在period时间间隔内重复执行
state 参数可以传入想在callback委托中处理的对象
dueTime 标识多久后callback开始执行
period 标识多久执行一次callback

Index.razor 我们定义并在 OnInitialized 时创建计时器,这里为了控制启动和停止,这里的 dueTimeperiod 设置为 Timeout.InfiniteTimeout.Infinite 是一个用于指定无限长等待时间的常数,这里就可以保证计时器不被触发 callback

private Timer _timer;//自动敲击计时器
private bool _isTimerRunning = false;//计时器启动开关

protected override void OnInitialized()
{
    _timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
    base.OnInitialized();
}

之后我们只需通过下面的方法更改计时参数即可切换计时器的启停状态了:

private void TimerSwitch()
{
    if (_isTimerRunning)
    {
        _timer.Change(Timeout.Infinite, Timeout.Infinite);
        _isTimerRunning = false;
    }
    else
    {
        _timer.Change(0, 1000);
        _isTimerRunning = true;
    }
}

4. 最后

至此,一个简单的 .NET MAUI Blazor 的小应用已开发完毕,在 GitHub 上提供了预编译的 apk 和旁载的 windows_x64 应用, 感兴趣的同学可以前去尝试一下。

相关文章
|
1月前
|
设计模式 开发框架 JavaScript
基于.NET8 + Vue/UniApp前后端分离的快速开发框架,开箱即用!
基于.NET8 + Vue/UniApp前后端分离的快速开发框架,开箱即用!
|
26天前
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
28 4
|
27天前
|
机器学习/深度学习 人工智能 物联网
.NET 技术:引领未来开发潮流
.NET 技术以其跨平台兼容性、高效的开发体验、强大的性能表现和安全可靠的架构,成为引领未来开发潮流的重要力量。本文深入探讨了 .NET 的核心优势与特点,及其在企业级应用、移动开发、云计算、人工智能等领域的广泛应用,展示了其卓越的应用价值和未来发展前景。
57 5
|
1月前
|
存储 缓存 NoSQL
2款使用.NET开发的数据库系统
2款使用.NET开发的数据库系统
|
1月前
|
开发框架 JavaScript 前端开发
2024年全面且功能强大的.NET快速开发框架推荐,效率提升利器!
2024年全面且功能强大的.NET快速开发框架推荐,效率提升利器!
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
46 7
|
3月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
74 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
55 0