如何设置导航栈的根

How to set the root of the navigation stack

我想要实现的是应用程序以 login page 开头,登录后应显示 Main page。从那里可以打开其他页面并允许正常导航。
但是,我不希望用户导航回登录页面。登录后 main page 必须是导航的根目录。

我在 google 上找到了很多关于如何操作的信息,但它们似乎都不适合我。主要是我被告知通过将 MainPage 直接设置为我的 PageMain 来使我的 main page 成为根目录,这现在也在我的代码中,但它不起作用。

其他方法应该是从导航堆栈中删除 login page,但我无法让它工作。我找到的示例编译但在运行时它们使我的应用程序崩溃,说我无法删除当前页面或找不到我要删除的页面。

这是我的代码:

我的应用程序以 PageLogin 开头,现在它只有一个按钮,当您单击它时它会打开我的 PageMain

private void ButtonLogin_Clicked(object sender, EventArgs e)
{
    // almost does what I want          
    Application.Current.MainPage = new PageMain();

    // almost does what I want          
    // make PageMain the new main page, so you cannot go back to the login screen
    //Application.Current.MainPage = new NavigationPage(new PageMain());

    // error you cannot remove the page you are on
    //var _navigation = Application.Current.MainPage.Navigation;
    //var _lastPage = _navigation.NavigationStack.LastOrDefault();
    ////Remove last page
    //_navigation.RemovePage(_lastPage);
    ////Go back 
    //_navigation.PopAsync();

    // error page does not exists
    //Application.Current.MainPage.Navigation.RemovePage(this);

    //Navigation.PopAsync();      not supported on android
    //Navigation.RemovePage(this); not supported on android

}

MainPage 设置在 App.xaml.cs 这样

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        //MainPage = new NavigationPage(new Pages.PageLogin());
        MainPage = new Pages.PageLogin();
    }

上面的代码打开我的页面 PageMain 目前一切顺利。
现在,当我点击设备的 back button 时,我的应用程序会最小化(或者它在 android 上执行的任何操作以隐藏自身)
这很好,因为我不想让用户回到 login form
但是,当我现在单击设备上的 recent apps 按钮,然后单击我的应用程序以将其返回到前台时,它会返回到登录表单。

看这个视频

我怎样才能避免这种情况?

编辑

我尝试将 IsTabStop 设置为 false,但也没有结果

public PageLogin()
{
    InitializeComponent();
    this.IsTabStop = false;
    ButtonLogin.Clicked += ButtonLogin_Clicked;
}

这是纯粹的 Android 行为,与 Xamarin.Forms 无关,当您在应用程序的导航堆栈为空时按下后退按钮,具体取决于 Android版本是 运行,它的行为如下:

  • Android 11 及以下:系统完成 activity.
  • Android 12 岁及以上:

系统将 activity 及其任务移至后台,而不是完成 activity。此行为与使用主页按钮或手势导航出应用程序时的默认系统行为相匹配。

在大多数情况下,此行为意味着用户可以更快地从热状态恢复您的应用,而不必从冷状态完全重启应用...

来源:Back press behavior for root launcher activities.

在您按下主屏幕上的后退按钮的情况下,Android 完成了 activity,如果您想确认这一点,请在 AppShell.cs 构造函数上设置一个断点或者 MainActivity.cs/OnCreate() 你会注意到:

  • 在主屏幕上按下主页按钮并从 android 应用程序堆栈恢复应用程序:none 个断点将被命中,因为应用程序 activity 已保存。事实上 Android 会调用 MainActivity.OnResume().
  • 按下后退按钮您将到达断点,因为 activity 已终止并且您正在重新开始它。

一些潜在的解决方案

  1. 在本地数据库 (SQLite) 或文件上保存并保留日志记录状态的更新记录,在应用程序启动时读取此布尔值并相应地显示或不显示登录页面(设置主页)。
  2. 如果您不希望您的应用程序在单击后退按钮时退出,请使用空代码覆盖 MainActivity 中的 OnBackPressed()
public override void OnBackPressed()
{
}
  1. 将您的应用程序发送到后面(暂停)而不是终止它:
   public override void OnBackPressed() => MoveTaskToBack(true);

更多关于OnBackPressed()

相关问题

我根据 CFun 的回答制定了一个方法,在我的测试中似乎工作正常。
我将在这里展示我对这个答案的实现,以便其他有同样问题的人可以使用它。
通过这样做,我也给大家一个改变,让大家评论这个答案的实现。

想法是在每次打开另一个页面时保留对前一页的引用。然后在 OnBackPressed 方法的 MainActivity.cs 中,我可以检查我是否在 Root Page(这是我的 PageMain)上。
在根页面上时,我可以执行 MoveTaskToBack 否则我可以将当​​前页面设置为 priorPage.

代码如下:

我做了什么,首先在我的 App.xaml.cs 我把这个静态变量 priorPage

public partial class App : Application
{
    public static Page priorPage;

    public App()
    {
        InitializeComponent();
        MainPage = new Pages.PageLogin();
    }

然后当我的应用程序启动时(第一个页面是登录页面)并且用户点击登录,这段代码将被执行。在打开新页面之前,变量 priorPage 将设置为当前活动的页面

private void ButtonLogin_Clicked(object sender, EventArgs e)
{
    App.priorPage = this;
    Application.Current.MainPage = new PageMain();
}

每个打开的页面都会使用相同的原理

private void ButtonSettings_Clicked(object sender, EventArgs e)
{
    App.priorPage = this;
    Application.Current.MainPage = new PageSettings();
}

最后,在 MainActivity.cs 我现在可以做到这一点

public override void OnBackPressed()
{
    if (App.Current.MainPage is PageMain)
    {
        MoveTaskToBack(true); // you are on the root, hide the app
    }
    else if (App.priorPage != null)
    {
        App.Current.MainPage = App.priorPage; // navigate back to you prior page
    }
    else
    {
        base.OnBackPressed();
    }
}