MAUI Blazor项目中如何添加一个返回服务,并支持安卓返回键

前言

MAUI Blazor中,安卓项目的返回键体验很不好,只能如同浏览器一样返回上一页。但很多时候,我们想让他返回的上一页,不一定就是实际上的上一页。而且也想让返回键去支持一些事件,按下返回键触发,例如关闭弹窗。Blazor中有NavigationManager,平时页面的跳转都是靠注入它,调用它的NavigateTo()方法。我们可以写个NavigateService类把他包装起来,再写个方法对安卓返回键做一些专门的处理,放在安卓返回键的处理事件中调用。

正文

添加所需代码

  1. 添加一个接口INavigateService
public interface INavigateService
{
    /// <summary>
    /// 存储想要返回时触发的方法
    /// </summary>
    event Action Action;
    NavigationManager Navigation { get;protected set; }
    /// <summary>
    /// 存储URL历史记录
    /// </summary>
    List<string> HistoryUrl { get; protected set; }
    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="navigation"></param>
    void Initialize(NavigationManager navigation);
    /// <summary>
    /// 导航
    /// </summary>
    /// <param name="url"></param>
    void NavigateTo(string url);
    /// <summary>
    /// 返回上一页
    /// </summary>
    void NavigateToBack();
    /// <summary>
    /// 返回键处理
    /// </summary>
    /// <returns>true已处理事件,false为无事件可处理(便于做进一步处理,例如退出应用)</returns>
    bool OnBackButtonPressed();
}
  1. 添加实现类NavigateService
public class NavigateService : INavigateService
{
    public NavigationManager Navigation { get; set; } = default!;

    public event Action? Action;

    public void Initialize(NavigationManager navigation)
    {
        Navigation = navigation;
    }

    public List<string> HistoryUrl { get; set; } = new List<string>();

    public void NavigateTo(string url)
    {
        var href = Navigation.ToBaseRelativePath(Navigation.Uri);
        HistoryUrl.Add(href);
        Navigation.NavigateTo(url);
    }
    public void NavigateToBack()
    {
        string href = string.Empty;
        if (HistoryUrl.Count > 0)
        {
            href = HistoryUrl.Last();
        }
        Navigation.NavigateTo(href);
        if (HistoryUrl.Count > 0)
        {
            HistoryUrl.RemoveAt(HistoryUrl.Count - 1);
        }
    }

    public bool OnBackButtonPressed()
    {
        if (Action != null && Action?.GetInvocationList().Length > 0)
        {
            var delegates = Action!.GetInvocationList();
            (delegates.Last() as Action)!.Invoke();
            return true;
        }

        if (HistoryUrl.Count > 0)
        {
            NavigateToBack();
            return true;
        }

        return false;
    }
}
  1. 添加安卓返回键处理类,放在Platforms/Android下,别忘记更改命名空间
using Android.OS;
using Android.Views;
using Android.Widget;
using SwashbucklerDiary.IServices;
using Application = Android.App.Application;

namespace XXX.Platforms.Android
{
    public static class BackButtonPressed
    {
        private static byte BackPressCounter;

        public static bool OnBackButtonPressed(KeyEvent e)
        {
            if (e.KeyCode == Keycode.Back)
            {
                if (e.Action == KeyEventActions.Down)
                {
                    var service = MauiApplication.Current.Services.GetRequiredService<INavigateService>();
                    bool flag = service!.OnBackButtonPressed();
                    if (!flag)
                    {
                        QuitApp();
                    }
                }
                return true;
            }
            return false;
        }

        public static void QuitApp()
        {
            if (BackPressCounter == 1)
            {
                Process.KillProcess(Process.MyPid());
            }
            else if (BackPressCounter == 0)
            {
                BackPressCounter++;
                Toast.MakeText(Application.Context, "再按一次退出", ToastLength.Long)!.Show();
                Task.Run(async () =>
                {
                    await Task.Delay(2000);
                    BackPressCounter = 0;
                });
            }
        }
    }
}

4.修改Platforms/Android/MainActivity.cs,添加以下代码

public override bool DispatchKeyEvent(KeyEvent e)
{
    var flag = BackButtonPressed.OnBackButtonPressed(e);
    if (flag)
    {
        return true;
    }

    return base.DispatchKeyEvent(e);
}

5.Shared下添加MainLayout.razor.cs

public partial class MainLayout 
{
    [Inject]
    NavigationManager Navigation { get; set; } = default!;
    [Inject]
    INavigateService NavigateService { get; set; } = default!;

    protected override void OnInitialized()
    {
        NavigateService.Initialize(Navigation);
        base.OnInitialized();
    }
}

6.修改MauiProgram.cs,添加以下代码

builder.Services.AddSingleton<INavigateService, NavigateService>();

如何使用

注入INavigateService

[Inject]
protected INavigateService NavigateService { get; set; } = default!;

如果想要将跳转后的上一页保存到返回服务中,跳转页面时使用NavigateService.NavigateTo(url),当你主动调用NavigateService.NavigateToBack()或者按下安卓返回键时,会跳转回之前保存的上一页。如果想要让返回键执行什么方法,就加入到委托中NavigateService.Action += 你的方法;别忘了在执行后和组件Dispose中把你的方法移出委托NavigateService.Action -= 你的方法;

一些想法

说一下为什么没有继承NavigationManager或者在NavigateService 的构造方法中注入它,NavigationManager这个类比较特殊,也没太玩明白。无论是继承还是构造方法中注入它,得到的都是没有经过初始化的NavigationManager,可以理解为空的,后续也没有办法使用。所以最后选择在MainLayout.razor.cs中注入的NavigationManager,把它存起来。我觉得这么写肯定是不够好,只是没有更好的办法。

后记

本人才疏学浅,也没怎么认真写过文章,所以文章中可能会存在纰漏,也欢迎各位大佬指出。

posted @ 2023-03-11 01:11  Yu-Core  阅读(320)  评论(0编辑  收藏  举报