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へ遷移するだけ。
//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 を親へ伝搬する。
//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 に設定する。
//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<Xamarin.Forms.Page> 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.ContentPage
は SecondPage
のままになってしまう。ので Appearing
イベントで表示の度に MainActivity.ContentPage を設定する必要がある。
AndroidActivity
に static な BackPressed
イベントがあるんだけど…イベントハンドラの定義は
public delegate bool BackButtonPressedEventHandler(object sender, EventArgs e);
となっていて、true
を返すと BACK キーを無効にできるようなのだけど、sender
は MainActivity
だし、EventArgs
は Page を取得できないしで使えないじゃん。。。
なんだかすごく発展途上な気がする、その内いろいろ整備されそうなので、それまで待った方が良い気がします。。。