Xamarin.Forms でどうにかしたい iOS と Android の違い の「BACKキーの制御」の 現時点(1.1.0.6201) での回答。

Android の BACKキーの制御を、Xamarin.Forms ではどう扱えるかを調べた。

シナリオ

Xamarin.Forms による画面1(MainPage)、2(SecondPage)があり、MainPage では BACKキーで戻る(=アプリ終了)事ができるが、SecondPage ではBACKキーが効かない、ようにしたい。

対策

まず画面1と2はこんな感じ。ボタンを押したら画面2へ遷移するだけ。

```csharp Pages.cs
// 画面1
public class MainPage : ContentPage
{
public MainPage()
{
var button = new Button
{
Text = “To Second”,
VerticalOptions = LayoutOptions.Center,
};

    button.Clicked += (sender, e) => 
    {
        this.Navigation.PushAsync(new SecondPage());
    };

    Content = button;
} }

// 画面2
public class SecondPage : ContentPage
{
public SecondPage()
{
Content = new Label
{
Text = “Second”
};
}
}


ここからが本題。
まず Android側のエントリポイントである ``MainActivity.cs`` は以下のように、``ContentPage`` プロパティを設ける。そして ``OnBackPressed`` メソッドを override して、MainPage だったら OnBackPressed を親へ伝搬する。

```csharp MainActivity.cs
[Activity(Label = "ScrollTest.Android.Android", MainLauncher = true)]
public class MainActivity : AndroidActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        Xamarin.Forms.Forms.Init(this, bundle);

        SetPage(new NavigationPage(new MainPage()));
    }

    internal Page ContentPage
    {
        get;
        set;
    }

    public override void OnBackPressed()
    {
        if (this.ContentPage is MainPage)
        {
            base.OnBackPressed();
        }
    }
}

次に、MainActivity.ContentPage への設定を行うコードは以下の通り。
PageRenderer を拡張して ExportRenderer することで、すべての Page にフックをかけ、Page の表示時に MainActivity.ContentPage に設定する。

```csharp MyPageRenderer.cs
using System;
using Xamarin.Forms.Platform.Android;
using Android.App;
using Xamarin.Forms;
using ScrollTest.Android;
using Android.Views;
using Android.Graphics;

[assembly:ExportRenderer(typeof(ContentPage), typeof(MyPageRenderer))]

namespace ScrollTest.Android
{
public class MyPageRenderer : PageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e);

        // なんとなく不安なので weak にしてみた
        var activity = new WeakReference<MainActivity>(this.Context as MainActivity);

        e.NewElement.Appearing += (_, __) =>
        {
            MainActivity a;
            if (activity.TryGetTarget(out a)) {
                a.ContentPage = e.NewElement;    
            }
        };
    }
} } ```

これで、画面1(MainPage)の時だけ BACKキーが効くようにできる。

Appearing イベントが必要なの?

 Xamarin.Forms の Android実装では、画面遷移の度に 「同じインスタンスの MainActivity」 が使いまわされる、さらに OnElementChanged は、各Pageにつき1度しか発生しない。その為、画面1→2→1と遷移すると MainActivity.ContentPageSecondPage のままになってしまう。ので Appearing イベントで表示の度に MainActivity.ContentPage を設定する必要がある。

AndroidActivity に static な BackPressed イベントがあるんだけど…

イベントハンドラの定義は
public delegate bool BackButtonPressedEventHandler(object sender, EventArgs e);
となっていて、true を返すと BACK キーを無効にできるようなのだけど、senderMainActivityだし、EventArgs は Page を取得できないしで使えないじゃん。。。

なんだかすごく発展途上な気がする、その内いろいろ整備されそうなので、それまで待った方が良い気がします。。。

Xamarin
C#
Android

published

Ads

Read more!

amay077

Microsoft MVP(Xamarin). フルリモートワーカー. Geospatial Mobile app developer. Love C#.

amay077 amay077