SIN@SAPPOROWORKSの覚書

C#を中心に、夜な夜な試行錯誤したコードの記録です。

Xamarin.Forms 「ARC Welder」を使用してChromeブラウザで動作させて見た

【 Xamarin 記事一覧 】


ARC Welder

 昨日(2015年4月3日)、GoogleからAndroidアプリをChromeで動作させるというニュースがありました。
 ARCとは「Android Runtime for Chrome」の頭文字であり、Androidが使うAPIをラップして、ChromeブラウザまたはChrome OSで動かすランタイムとの事。まだ、ベータ版という事ですが・・・Xamarin.Formsで作成したAPKがうまく動作するかどうか試してました。
http://japanese.engadget.com/2015/04/02/google-android-chrome-arc-welder-apk/
f:id:furuya02:20150405104214p:plain

Xamarin.Formsのプロジェクト

結論から言ってしまうと、アプリのアイコンを設定して、リリースビルドすれば、証明書のあるパッケージは、問題なく動作するようです。

アイコン設定

プロジェクトテンプレートのデフォルトでは、アイコンが無いため、「ARC Welder」で「Launch App」時にエラーが発生してしまいます。
f:id:furuya02:20150405104224p:plain
ERROR: Required badging "icon" not found in package.

デバッグビルド

Xamarinのデバッグビルドは、高速化のため、「共有ランタイム(Shared Runtime)」などがリンクされていないのが原因だと思います。

証明書

証明書が無いパッケージでは、「Launch App」まで行けますが、実行時にcrashします。
f:id:furuya02:20150405104250p:plain

作業手順

それでは、実際に行った手順を順に紹介してみたいと思います。
(1)プロジェクトは、「Blank App (Xamarin.Forms Portable)」テンプレートを使用します。
f:id:furuya02:20150405104259p:plain
(2)構成マネージャで「Release」に変更します。
f:id:furuya02:20150405104304p:plain
(3)Androidプロジェクトのプロパティで、アイコンを設定します。
f:id:furuya02:20150405104309p:plain
(4)「ARC Welder」をインストールします。
https://chrome.google.com/webstore/detail/arc-welder/emfinbmielocnlhgmfkkmkngdoccbadn
f:id:furuya02:20150405104314p:plain
(5)インストールが完了すると、「chrome://extensions」で利用可能になります。
f:id:furuya02:20150405104320p:plain
(6)初回起動時に、作業ディレクトリの指定があるので、適当なフォルダを指定します。
f:id:furuya02:20150405104325p:plain:w200
(7)「Add your APK」で、Releaseビルドの証明書がある方を選択します。(App1/App1.Droid/bin/Release/App1.Droid-Signed.apk)
f:id:furuya02:20150405104332p:plain
(8)Orientationで「Portrait」、Form Factorで「Phone」を選択して「Launch App」を押します。
f:id:furuya02:20150405104339p:plain
(9)起動が確認できます。
f:id:furuya02:20150405104346p:plain
(10)「Launch App」で成功したアプリは、「拡張機能」の一覧に入り、次からは、ここから起動できます。
f:id:furuya02:20150405104351p:plain


Xamarin.Forms.Maps

試しに、Xamarin.Forms.Mapsを起動してみましたが、「Play開発者サービスの入手」をクリックするとクラッシュしましたw
f:id:furuya02:20150405104958p:plain:w200

【 Xamarin 記事一覧 】

Xamarin.Forms ライフサイクル

【 Xamarin 記事一覧 】

ライフサイクル

Xamarin.Forms 1.3において新しく刷新した、Xamarin.Forms.Applicationクラスには、次の3つのメソッドがオーバーライド可能になりました。

OnStart アプリのスタート時
OnSleep アプリがバックグランド(スリープ状態)に移行した時
OnResume アプリがバックグランド(スリープ状態)から復帰した時

この動作について、詳細なレポートが@matatabi_uxさんの所で紹介されています。

Xamarin.Forms の App Lifecycle の呼び出され方を調べたい - しっぽを追いかけて
Xamarin.Forms の App Lifecycle の呼び出され方を調べたい 【Windows Phone 8.0 編】 - しっぽを追いかけて
Xamarin.Forms の App Lifecycle の呼び出され方を調べたい 【iOS編】 - しっぽを追いかけて
Xamarin.Forms の App Lifecycle の呼び出され方を調べたい 【Android 編】 - しっぽを追いかけて

上記のページで、全然、完璧なのですが、もともとiOSやAndroidのライフサイクルについて知識の乏しい私が理解するにはちょっと重たかったので・・・
完全真似っこですが、私なりに確かめてみました。

動作確認用のコード

動作確認のために書いたコードは下記の通りです。
ナビゲーションページをルートとして1ページ目をライム色、2ページ目をピンク色にしました。
また、1ページ目には、2ページ目に移動するボタンと、ブラウザを開くボタンを配置しました。

通過したメソッドを確認するためには、Appクラスに、呼び出し元のクラス名及びメソッド名を出力するLog()を作成し、オーバライドした各メソッドからこれを呼び出すようにしました。

//App.cs
namespace App1{
    public class App : Application{
        public App(){
            //NavigationPageの上に最初のページを構築する
            MainPage = new NavigationPage(new TopPage());
        }

        protected override void OnStart(){
            Log();//ログ出力
        }

        protected override void OnSleep(){
            Log();//ログ出力
        }

        protected override void OnResume(){
            Log();//ログ出力
        }

        //呼び出し元のメソッド名を出力のする 「クラス名.メソッド名()」の形式
        public static void Log([CallerFilePath] string file = "", [CallerMemberName] string member = ""){
            Debug.WriteLine(string.Format("{0}.{1}()", Path.GetFileNameWithoutExtension(file).Split('\\').LastOrDefault(), member));
        }

    }

}

//最初のぺージ TopPage.cs
namespace App1{
    class TopPage : ContentPage{
        public TopPage(){
            BackgroundColor = Color.Lime;//背景色(ライム)

            //次ぼページ(SecondPage)を開くボタン
            var button1 = new Button{
                Text = "Next",
                Command = new Command(async () =>{
                    await Navigation.PushAsync(new SecondPage());
                })

            };
            //ブラウザを開くボタン
            var button2 = new Button{
                Text = "Browser",
                Command = new Command(() =>{
                    Device.OpenUri(new Uri("http://xamarin.com/"));
                })

            };
            //2つのボタンを上下に配置する
            Content = new StackLayout{
                Children = { button1, button2 }
            };
        }

        protected override void OnAppearing(){
            App.Log();//ログ出力
            base.OnAppearing();
        }
    }
}

//2つ目のページ SecondPage.cs
namespace App1{
    class SecondPage : ContentPage{
        public SecondPage(){
            BackgroundColor = Color.Pink;//背景色(ピンク)
        }
        protected override void OnAppearing(){
            App.Log();//ログ出力
            base.OnAppearing();
        }
    }
}



Androidでの実行画面
f:id:furuya02:20150403000800p:plain:w400
iOSでの実行画面
f:id:furuya02:20150403000757p:plain:w400
WindowsPhoneでの実行画面
f:id:furuya02:20150403000759p:plain:w400


iOSによる動作確認

AppDelegate.csでも、各メソッドをオーバーライドしてLog()をコールしました。

namespace App1.iOS{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate{
        public override bool FinishedLaunching(UIApplication app, NSDictionary options){
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            App.Log();//ログ出力

            return base.FinishedLaunching(app, options);
        }

        public override void FinishedLaunching(UIApplication application) {
            App.Log();//ログ出力
            base.FinishedLaunching(application);
        }

        ・・・省略・・・

アプリ起動

f:id:furuya02:20150403001442p:plain:w150:leftAppDelegate.WillFinishLaunching() //起動時に呼び出される
AppDelegate.FinishedLaunching() //はじめての起動時に呼び出される
App.OnStart()
AppDelegate.OnActivated() // アクティブ化
TopPage.OnAppearing() // 表示

ページ遷移

f:id:furuya02:20150403001623p:plain:w200:leftSecondPage.OnAppearing()// 表示

ページ復帰

f:id:furuya02:20150403001826p:plain:w200:leftTopPage.OnAppearing()// 表示

ブラウザ起動

f:id:furuya02:20150403001846p:plain:w200:leftAppDelegate.OnResignActivation() //アプリが非Activeになる直前に呼び出される
App.OnSleep()
AppDelegate.DidEnterBackground() //アプリが非Activeになりバックグランド実行になった際に呼び出される

ホーム

f:id:furuya02:20150403001911p:plain:w200:leftAppDelegate.OnResignActivation() //アプリが非Activeになる直前に呼び出される
App.OnSleep()
AppDelegate.DidEnterBackground() //アプリが非Activeになりバックグランド実行になった際に呼び出される

タスクリストからの起動

f:id:furuya02:20150403011536p:plain:w200:leftAppDelegate.WillEnterForeground()
App.OnResume()
AppDelegate.OnActivated()

タスクリストへの移行

f:id:furuya02:20150403001932p:plain:w200:leftAppDelegate.OnResignActivation() //アプリが非Activeになる直前に呼び出される
App.OnSleep()

アプリ終了

f:id:furuya02:20150403001954p:plain:w150:leftAppDelegate.DidEnterBackground() //アプリが非Activeになりバックグランド実行になった際に呼び出される
AppDelegate.WillTerminate() //システムからのアプリ終了の際に呼び出される


Androidによる動作確認

MainActivity.csでも、各メソッドをオーバーライドしてLog()をコールしました。

namespace App1.Droid{
    [Activity(Label = "App1", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity{
        protected override void OnCreate(Bundle bundle){
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());

            App.Log();//ログ出力
        }

        protected override void OnStart() {
            App.Log();//ログ出力
            base.OnStart();
        }

        protected override void OnRestoreInstanceState(Bundle savedInstanceState) {
            App.Log();//ログ出力
            base.OnRestoreInstanceState(savedInstanceState);
        }
        
        ・・・省略・・・

アプリ起動

f:id:furuya02:20150403002304p:plain:w100:leftApp.OnStart()
MainActivity.OnCreate()
MainActivity.OnStart()
MainActivity.OnPostCreate()
MainActivity.OnResume()
MainActivity.OnPostResume()
TopPage.OnAppearing()

ページ遷移

f:id:furuya02:20150403002419p:plain:w200:leftSecondPage.OnAppearing()

ページ復帰

f:id:furuya02:20150403002438p:plain:w200:leftTopPage.OnAppearing()

ブラウザ起動

f:id:furuya02:20150403002452p:plain:w200:leftMainActivity.OnUserLeaveHint()
MainActivity.OnPause()
MainActivity.OnSaveInstanceState()
MainActivity.OnStop()
App.OnSleep()

ブラウザからの復帰

f:id:furuya02:20150403002507p:plain:w200:leftMainActivity.OnRestart()
App.OnResume()
MainActivity.OnStart()
App.OnResume()
MainActivity.OnResume()
MainActivity.OnPostResume()

ホーム

f:id:furuya02:20150403002523p:plain:w200:leftMainActivity.OnUserLeaveHint()
MainActivity.OnPause()
MainActivity.OnSaveInstanceState()
MainActivity.OnStop()
App.OnSleep()

タスクリストからの起動

f:id:furuya02:20150403010949p:plain:w200:left
MainActivity.OnRestart()
App.OnResume()
MainActivity.OnStart()
App.OnResume()
MainActivity.OnResume()
MainActivity.OnPostResume()

タスクリストへの移行

f:id:furuya02:20150403002535p:plain:w200:left
ログなし(おそらくこの時点ではActive)


アプリ終了

f:id:furuya02:20150403002550p:plain:w100:left
ログなし(既にHomeに遷移した時点でOnStopとなっているから)



サンプル

github.com



【 Xamarin 記事一覧 】

Xamarin.FormsでListViewのコンテキストアクションを使用するには?

【 Xamarin 記事一覧 】

BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。

Xamarin.FormsでListViewのコンテキストアクションを使用するには? - Build Insider

使用したコードは、下記にあります。

furuya02/XamarinTips.ContextActionsSample · GitHub




【 Xamarin 記事一覧 】

Xamarin.Formsでビヘイビアーを使用するには?

【 Xamarin 記事一覧 】

BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。

Xamarin.Formsでビヘイビアーを使用するには? - Build Insider

使用したコードは、下記にあります。

furuya02/XamarinTips.BehaviorSample · GitHub




【 Xamarin 記事一覧 】

Xamarin.Formsで地図の現在位置やピンの表示、縮尺や地図タイプの変更を行うには?(Xamarin.Forms.Maps使用)

【 Xamarin 記事一覧 】

BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。


Xamarin.Formsで地図の現在位置やピンの表示、縮尺や地図タイプの変更を行うには?(Xamarin.Forms.Maps使用) - Build Insider

使用したコードは、下記にあります。


furuya02/XamarinTips.MapsSample · GitHub




【 Xamarin 記事一覧 】

Xamarin.Formsで地図を表示するには?(Xamarin.Forms.Maps使用)?

【 Xamarin 記事一覧 】

BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。


Xamarin.Formsで地図を表示するには?(Xamarin.Forms.Maps使用) - Build Insider

使用したコードは、下記にあります。


furuya02/XamarinTips.MapsSample · GitHub





【 Xamarin 記事一覧 】

Xamarin.Forms NavigationPageでアクションバーのアイコンを非表示にする(Android)

【 Xamarin 記事一覧 】

アクションバーのアイコン

Xamarin.FormsでNavigationPageを使用すると、Androidでは、ページの左上にアイコンが表示されます。
このアイコンの消す方法は、Xamarin.Formsの機能としては提供されておらず、力づくなら、背景色に紛れた1ドットのアイコンなどに置き換えるしかないようです。

今回は、NavigationPageのレンダラーを記述して、正規に(?)、このアイコンを非表示にする方法が、Xamarinのフォーラムで紹介されていたので、ここに記録しました。

f:id:furuya02:20150310094150p:plain:w200:leftf:id:furuya02:20150310094148p:plain:w200:left

NavigationPage

NavigationPageを使用したPCL側のコードです。
レンダラー記述のためだけにNavigationPageの拡張クラス(ExNavigationPage)を作成しています。

//App.cs
using Xamarin.Forms;
namespace App1{
    public class App : Application{
        public App(){
            //ルートビューをNavigationPageにする
            MainPage = new ExNavigationPage(new MainPage());
        }
        //・・・省略・・・
    }

    //拡張NavigationPage レンダラのためだけのクラス
    public class ExNavigationPage : NavigationPage {
        public ExNavigationPage(Page root) : base(root) {}
    }

    class MainPage : ContentPage {
        public MainPage() {
            //中央にラベル「MainPage」を配置する
            Content = new StackLayout {
                VerticalOptions = LayoutOptions.Center,
                Children = { 
                    new Label {
                        XAlign = TextAlignment.Center,
                        Text="MainPage"
                    }
                }
            };
        }
    }
}

レンダラー

レンダラー側のコードは、次のようになります。
NavigationRendererクラスの、ContextプロパティがActivityクラスなので、そこからActionBarオブジェクトへたどれます。
ActionBarオブジェクトのSetIconメソッドで、Android.Resource.Color.Transparentを指定すると、アイコンは非表示になります。

//ExNavigationPageRenderer.cs
using Android.App;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(ExNavigationPage), typeof(ExNavigationPageRenderer))] 
namespace App1.Droid{
    class ExNavigationPageRenderer : NavigationRenderer{
        protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e) {
            base.OnElementChanged(e);

            var actionBar = ((Activity)Context).ActionBar;
            actionBar.SetIcon(Android.Resource.Color.Transparent);
        }
    }
}

ActionBarオブジェクトのメソッドである、SetDisplayShowHomeEnabledやSetDisplayShowTitleEnabledは、この場合、有効に機能しません。

//このコードで、アイコンの非表示やタイトルバーの非表示はできない

actionBar.SetDisplayShowHomeEnabled(false);
actionBar.SetDisplayShowTitleEnabled(false);


参考資料

Remove icon from action bar from xamarin forms android project - Xamarin Forums
xml - Remove icon/logo from action bar on android - Stack Overflow



【 Xamarin 記事一覧 】