Xamarin.Forms : アプリのターゲットをAndroid10にする
Android 10への対応を行わなければならないということ.そうだったのか...
- 2020/8/3以降 : 新規アプリはAndroid 10(API 29)以上を対象とする必要がある.
- 2020/11/2以降 : すべてのアプリのアップデートはAndroid 10(API 29)以上を対象とする必要がある.
なるほど.
コンパイルターゲットを変更する
ソリューションエクスプローラでAndroidプロジェクトで右クリックしてコンテキストメニューからプロパティを開いて,Application
で以下のようにCompile using Android version: (Target Framework)
をAndroid 10.0 (Q)
にする.
ターゲットAPIを変更する
引き続きAndroidプロジェクトのプロパティでAndroid Manifest
でTarget Android version:
という項目があるので,それをAndroid 10.0 (API Level 29 - Q
にする.
ビルドしたらエラーが出た
利用しているライブラリがXamarin.Android.Support.Compat
やXamarin.Android.v7.AppCompat
を必要だけど見つからない,ということなのでインストールする.
(20200715追記) どうやらこれはSyncfusionのライブラリがこれら必要としているため.
パッケージはAndroidプロジェクトだけにインストールすれば良いようだ.
Xamarin.Forms 4.5以降のアプリはここまででいいのかな?
Android Xに移行する
(注:Xamarin.Forms 4.7以降はAndroidXに対応しているので,特にすることはない)
James Montemagnoさんによると, Android10をターゲットにするならAndroidXライブラリへの移行も検討すべき,とのこと.
AndroidXライブラリは古いAndroidサポートライブラリを置き換えるもので,ターゲットをAndroid10にすると,Xamarin.FormsもXamarin.EssentialsもAndroidXを使うようになっている,とのこと.
何か作業する必要はあるのだろうか詳しくはこちら.
移行ツールを使う
Visual Studio 2019 Preview 16.5以上であること.
Visual StudioでTools > Options
を開く.
次にAndroid Settings
を検索する.
その中でEnable AndroidX Migrator(Experimental)
とあるので,チェックをつけて有効にする.Okボタンを押す.
ソリューションエクスプローラでAndroidプロジェクトを右クリックすると,コンテキストメニューにMigrate to AndroidX
という項目があるので,それをクリックする.
以下のような確認ダイアログが表示される.
「移行処理はキャンセルできないし,やり直しもできないけどいい?」
バックアップをとるなり,gitでコミットしておくなりして,対策してからYesを押す.
Google Play StoreにAndroidアプリを公開する メモ
このへんはこちらの記事がとても参考になりました.
「Google PlayにAndroidアプリを公開」を押します.
「アプリの作成」というダイアログが表示されるのでアプリ名などを入力していきます.
アプリの説明などを入力します.
アプリのスクリーンショットをアップロードする必要がありますが,画像のサイズやアスペクト比など条件があるのでちょっと手間です.
私はストア用のスクリーンショットをアップロード条件を満たすようにいい感じに調整してくれるサービスを使いました.
次にフィーチャーグラフィックという画像を作らなければならないが,これが1024×500指定なので面倒...
適当に画像を用意して,ペイント3Dでサイズを変更しました. こんなの↓
最終的にアップロードしてこんな感じです.
あとはアプリのタイプやカテゴリを指定して完了です.
今気づきましたが,左側のメニューを観ると入力しなければならない項目にチェックマークがついてわかるようになっています.まだまだやることはたくさんあります.
アプリのリリース
AAB(Android App Bundle)やAPKをアップロードする必要があります.
App Centerで最新のビルドからAABをダウンロードします.
「Download Bundle」というやつですね.zipファイルのダウンロードが始まるので,完了したら解凍して.aab
ファイルがあることを確認します.
今度はGoogle Play Consoleに移動します.
Google Play Consoleでアプリのリリース
を選択して,ベータ版や製品版などいくつかの種類のリリースがありますが,今回は製品版
でいきます.
製品版トラックの管理
を押します.
リリースを作成
を押します.
次の画面はアプリの署名鍵についてです.
App Centerで自動ビルドを行う手順の中で署名鍵を生成したと思ったんだけれど,Google Play Storeの署名鍵はこれを自動的に置き換えてくれるのだろうか?
ともかく,このGoogleが用意する署名鍵を使えば,鍵の管理が不要になるというメリットはある.
このあたりは以前の記事を確認すること
さて,先程の.aab
ファイルをアップロードします.
ファイルを選択
ボタンを押して先程の.aab
ファイルを指定します.
アップロードが完了すると,そのまま自動的にapkファイルの生成などが行われます.
後は保存して,ここでの手順は完了です.
レーティングの設定
年齢制限やなんかですね.
アプリのカテゴリとしては一番下のユーティリティなどなど
を選びました.
次のステップでは,アプリについていくつかの質問項目があるので回答します.
プライバシーポリシーを用意するのが面倒だったので「13才未満」は対象に含まないことにした.
すべてのチェックマークが点灯した.
公開の準備完了になった.
上のリストからアプリを選択して,次のページで右下の「公開の準備完了」というボタンをクリックします.
すると以下のダイアログが開くので,「リリースを管理」をクリックします.
次に「アプリのリリース」内の「製品版トラック」をみると,「公開されていない製品版のリリースがあります」とあるので,リリースを編集
ボタンを押します.
次のページで右下の「確認」を押します.
最後に「製品版として公開を開始」ボタンを押します.
以下のダイアログが表示されるので,もちろん「確認」ボタンを押します.
どうやら公開された?
「公開待ち」になっている.
Xamarin.FormsのAndroidアプリのアプリ名を変更する
20200714追記
「アプリ名を変更する」と書きましたが,この記事で変更するのはAndroidのホーム画面に表示されるアイコンのラベルでした.
この記事のとおりに作業してもアプリ名は変更されません.
アプリ名の変更は以下の記事を参考にしてください.
元の記事ここから
前回はXamarin.FormsのAndroidアプリのアイコンを変更しました.
しかし,アプリ名はプロジェクト名のまま(XFMyDecode2020)でした.
これをプロジェクト名ではなく,任意の名前にしたいと思います.
アプリ名を変更するにはどうしたらよいでしょうか.
ここでCovid19Radarの場合を見てみます.
これはAndroidプロジェクトのMainActivity.cs
です.
[Activity(Label = "@string/app_name", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme.Splash", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ScreenOrientation = ScreenOrientation.Portrait, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
Label = "@string/app_name"
と指定しています.
app_name
に割り当てられている文字列がここに入るようです.
ではこのapp_name
はどこで定義されているかというと,AndroidプロジェクトのResources > values
フォルダ内のStrings.xml
というファイルに記述されています.
中を見てみると,アプリケーション名が定義されています.
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">COVID-19 Contact App</string> </resources>
同じようにしてみます.valuesフォルダ内にStrings.xml
という名前のファイルを作成します.
アプリ名をMy de:code 2020
とします.
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">My de:code 2020</string> </resources>
次にMainActivity.csファイルのActivity属性を同じように以下のように@string/app_name
としました.
[Activity(Label = "@string/app_name", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
これで実行してみます.
無事,アプリ名も変わりました.
Xamarin.FormsでAndroidアプリのアイコンを変更する
こちらを参考にした.
アイコンのPNG画像を用意する.
用意の仕方は色々あるだろうけれど,今回はこちらのサービスで簡易なアイコンを作成した.
Launcher icon generatorをクリックする.
エディタが表示されるので,画像を使うか文字列を使うか,や,アイコンの形(四角,丸),背景色や文字色などいくつかのパラメータを設定する.
できたら,右上にあるダウンロードボタンを押してzipファイルをダウンロードする.
zipファイルの中にはmipmap-**hdpi
という複数のフォルダがあり,その中に各サイズの画像が入っている.
これらをまるっとAndroidプロジェクトにコピーする.今回,アイコンのファイル名はic_launcher.png
としています.
ここで一度ビルドする(重要).
次にAndroidプロジェクトのプロパティを開き,左側のメニューからAndroid Manifest
を選択してApplication icon
のドロップダウンリストで先程追加したアイコンを選択する.
最後にMainActivity.cs
ファイルを開き,MainActivityクラスについているActivity属性で,以下のようにIcon=
の部分に@mipmap/ic_launcher
と指定する.拡張子の.png
は不要.
[Activity(Label = "XFMyDecode2020.Android", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
改めて実行する.
配置されたアプリのアイコンが指定したアイコンになっていることが確認できる.
App Center ビルドスクリプト
前回の記事でApp Centerを使ってPushのタイミングで自動ビルド(releaseビルド)まで行いました.
しかし,Syncfusionのライセンスキーをコード内に記述していないため,アプリ起動後に何も表示されずにアプリを利用できませんでした.
ソースコードに直接記述すれば動きはしますが,そのままGitHubなどにPushしてしまうとライセンスキーが公開されてしまうリスクが生じます.
ライセンスキーやDBへの接続文字列,APIキーなどの機密情報はソースコード内に記述したくはありません.
モバイルアプリ開発における解決方法の1つは,App Centerでビルドが実行される前にこれらの機密情報をソースコードに差し込む処理を自動で行われるようにする,というものです.
例えば,Covid19Radarであれば,settings.json
ファイルにこれらの情報をまとめて記述しておき,実行時に取り出すようになっていますが,ソースコード上では以下のように実際のキーは記述されていません.APP_VERSION
やAPI_SECRET
,ANDROID_SAFETYNETKEY
といった部分がそれです.
{ "appVersion": "APP_VERSION", "apiSecret": "API_SECRET", "apiUrlBase": "https://API_URL_BASE/api", "supportedRegions": "440", "blobStorageContainerName": "c19r", "androidSafetyNetApiKey": "ANDROID_SAFETYNETKEY", "cdnUrlBase": "https://CDN_URL_BASE/", "licenseUrl": "https://covid19radarjpnprod.z11.web.core.windows.net/license.html", "appStoreUrl": "https://itunes.apple.com/jp/app/id1516764458?mt=8", "googlePlayUrl": "https://play.google.com/store/apps/details?id=jp.go.mhlw.covid19radar", "supportEmail": "SUPPORT_EMAIL" }
また,TurnipTracker(あつまれ動物の森のカブ情報をプレーヤー間で共有するためのアプリ)ではApp.xaml.cs
ファイル内にまとめられています.
public const string GetFriendsKey = "AC_GetFriendsKey"; public const string GetFriendRequestsKey = "AC_GetFriendRequestsKey"; public const string GetFriendRequestCountKey = "AC_GetFriendRequestCountKey"; public const string PostRemoveFriendRequestKey = "AC_PostRemoveFriendRequestKey"; public const string PostApproveFriendRequestKey = "AC_PostApproveFriendRequestKey"; public const string DeleteRemoveFriendKey = "AC_DeleteRemoveFriendKey"; public const string PostSubmitFriendRequestKey = "AC_PostSubmitFriendRequestKey"; public const string PutUpdateProfileKey = "AC_PutUpdateProfileKey"; public const string PutUpdateTurnipPricesKey = "AC_PutUpdateTurnipPricesKey"; public const string PostCreateProfileKey = "AC_PostCreateProfileKey"; public static bool IsStore => "AC_IsStore" == "true" ? true : false; const string AppCenteriOS = "AC_IOS"; const string AppCenterAndroid = "AC_ANDROID"; const string AppCenterUWP = "AC_UWP"; const string SyncFusionKey = "AC_SYNC";
このように,ソースコードでは実際の機密情報を記述せずに,「置換の目印となる文字列」を記述しておき,App Centerでビルド前にこれらの文字列を機密情報に置換するという方法をとっています.
App Centerではこのようにビルド前(後)などに何らかの処理を行うスクリプトを実行することができます.
こちらの動画はTurnipTrackerの実装についてJames Montemagnoさんが紹介しているものですが,その46分39秒あたりでスクリプトを使った仕組みについて触れています.
長くなりましたが,今回はSyncfusionのライセンスキーをビルドスクリプトを使ってビルド時に差し込む方法についてです.
Syncfusionのライセンスキーの取得についてはSyncfusion公式サイトや以前の記事が参考になるかと思います.
JSONファイルの読み込み
やり方はなんでも良いですが,基本的には対象のJSONファイル(appsettings.jsonなど)をEmbedded resource
に設定して,Assemblyから文字列(もしくはstream)として読み込むのが簡単かと思います.
例えばCovid19Radarだと以下のように読み込んでいます.
そして読み込んだJSONをパースして必要な情報を取得していくといった感じです.
var assembly = Assembly.GetExecutingAssembly(); using var file = assembly.GetManifestResourceStream("Covid19Radar.settings.json"); using var sr = new StreamReader(file); var json = sr.ReadToEnd(); var j = JObject.Parse(json); AppVersion = j.Value<string>("appVersion"); LicenseUrl = j.Value<string>("licenseUrl"); AppStoreUrl = j.Value<string>("appStoreUrl"); GooglePlayUrl = j.Value<string>("googlePlayUrl"); ApiUrlBase = j.Value<string>("apiUrlBase"); ApiSecret = j.Value<string>("apiSecret"); CdnUrlBase = j.Value<string>("cdnUrlBase"); BlobStorageContainerName = j.Value<string>("blobStorageContainerName"); SupportedRegions = j.Value<string>("supportedRegions").ToUpperInvariant().Split(';', ',', ':'); AndroidSafetyNetApiKey = j.Value<string>("androidSafetyNetApiKey"); SupportEmail = j.Value<string>("supportEmail");
もしくはGeneric Hostを使ってASP.NET Coreのような設定,DIの仕組みを使う場合は以下のような方法でJSONの読み込みとデータの取得をすることもできます.
appsettings.jsonの編集
appsettings.jsonファイルを用意し,以下のようにします.
{ "SyncfusionLicenceKey": "SYNCFUSION_LICENCE_KEY" }
App Center側でこのSYNCFUSION_LICENCE_KEY
という文字列を本物のライセンスキーに置換するようにします.
念の為,"SyncfusionLicenceKey"をキーにして値を取得できるか確認してみます.ここではIConfiguration
を使った方法でやっています.
取得できています.
App Centerのビルドスクリプトの作成
次にApp Center側でビルド前に実行するスクリプトを作成します.
ビルド前後の3つのタイミングで実行されるスクリプトをそれぞれ作成してリポジトリに含んでおくことで,App Centerでのビルド時にそれらが実行されます.
以下のタイミングでそれぞれ実行されます.
Post-clone(cloneの後) :
appcenter-post-clone.sh
-> リポジトリがcloneされた直後.Pre-build(ビルド前) :
appcenter-pre-build.sh
-> ビルドの直前.Post-build(ビルド後) :
appcenter-post-build.sh
->ビルドの直後.
これらのスクリプトは用意されているビルトインの変数だけでなく,任意の環境変数を作成して利用することができたり,基本的には何でもできます.
これらのファイルをソリューション,プロジェクトのどこに置くのかというと,ソリューションファイル(sln)やプロジェクトファイル(csproj)と同じ階層ということです.
Place the scripts with the format specified below next to the project-level (.xcodeproj, .csproj, .sln, or package.json) file or module-level (build.gradle) file that you've selected in the build configuration and we'll run them as custom build steps.
Androidプロジェクトの直下にappcenter-post-clone.sh
を配置しました.
TurnipTrackerのビルドスクリプトを参考にして以下のように一箇所だけ置換するスクリプトを書きました.
#! /usr/bin/env bash echo "Variables:" APP_SETTINGS_FILE=$BUILD_REPOSITORY_LOCALPATH/XFMyDecode2020/XFMyDecode2020/appsettings.json sed -i '' "s|SYNCFUSION_LICENCE_KEY|$SYNCFUSION_LICENCE_KEY|g" $APP_SETTINGS_FILE # print out for reference cat $APP_SETTINGS_FILE
$BUILD_REPOSITORY_LOCALPATH
はビルトインの環境変数(?)なのでしょうか,どうやらこのリポジトリのルートフォルダのパスを表すようです.
sed
コマンドで,指定したファイル(appsettings.json)内のSYNCFUSION_LICENCE_KEY
という文字列を環境変数SYNCFUSION_LICENCE_KEY
の値で置換する,という内容です.
あと,確認のために置換後にappsettings.jsonの内容を出力するようにしてあります.
App Centerに環境変数を追加する
App Centerを開き,以下のようにメニューのBuild
を開きます.
右上に🔧のようなアイコンがあるので,これをクリックすると,ビルドの設定画面が開きます.
ここで,Environment variales
をOnにして,SYNCFUSION_LICENCE_KEY
をキー,Syncfusionのライセンスキーを値とするペアを登録します.
Save
ボタンを押して変更を保存します.
ちなみにこの時点でBuild scripts
はNone
になっています.まだPushしてないので.
それではここまでの変更をGitHubにプッシュします.
プッシュの最中にビルドの設定画面を開くと,Build scripts
にPost-clone
が表示されていました.ビルドスクリプトは認識されたようです.
ビルドは成功しましたが,ビルドスクリプトは実行されませんでした.
どうやら,
- ビルドスクリプトを追加
- GitHubへPush
- App CenterがGitHubからリポジトリをclone
- ビルドスクリプトも認識されている
- でも,ビルドスクリプト認識後に設定が「保存されていない」
- ビルドスクリプトがない設定でビルドされる
ということのようです.
このように,ビルドスクリプトを追加してpushした後,App Center側で設定を「保存」しなおす必要がありました.
なので,ビルドスクリプト追加後の1回目のビルドはすぐにキャンセルしてしまえばよいと思います.
Xamarin.FormsのAndroidアプリのAPKファイルの生成
ストアに上げるにしてもテスト用にデバイスに配置(デバッグではなく)するにしても,APKに対して署名する必要があるようだ?
こちらも参考に.
で,テスト用のAPKを生成する際は「Ad hoc」を選ぶようだ?
ちょっとやってみよう.
Androidプロジェクトのプロパティを開く.
これはソリューションエクスプローラでAndroidプロジェクトを右クリックしてコンテキストメニューから「Properties」を選択する.
Android Manifest
左側のメニューからAndroid Manifest
を選ぶ.
アプリケーションのアイコンを指定する
Application iconでpngファイルを指定する.
ここにはAndroidプロジェクトのResources > drawable
やResources > mipmap-**
内に配置されたpngファイルが候補として表示される.
バージョンを指定する
バージョン番号
- これはビルドのたびにインクリメントされていく.
- ユーザーに見せるものではない.
- AndroidManifest.xmlに
android:versionCode
として記述される.
バージョン名
- ユーザーに見せる「バージョン」情報.
- ストアなどでも表示されるもの.
- 内部的には使われない.
- ユーザーが使っているバージョンの特定ができるのであれば,どんな文字列であってもよい.
- AndroidManifest.xmlに
android:versionName
として記述される.
Android Options
左側のメニューからAndroid Options
を選択する.
この設定はビルド設定毎に異なるので,対象のビルド設定を選択していることを確認する.今回はRelease
を選択している.
Code shrinker
サイズを小さくしてくれるのか?
r8
とかProGuard
とかわかんない.
James Montemagnoさんのアプリの設定がr8
だったので,とりあえずr8
にしておく
Code Generation and Runtime
これもよくわかっていない.
JamesさんのアプリではEnable Startup Tracing
とUse the concurrent garbage collector
の両方にチェックがついているので,そのようにした.
わかった.DotNetConf2020 Xamarin回のメモに書いてた.
事前コンパイラ(AOT)を有効にするオプションだった.
Linker
静的な解析によって,実行時に使用されるアセンブリ,型,メンバーなどを検出し,それ以外の使用されないものをパッケージから除外する.
デフォルトでSdk Assemblies Only
が選択されているので,そのままにする.
このあたりの設定は,James Montemagnoさんのアプリの設定を参考にするとしよう.これはストアにも公開している有料アプリで,そこで使われている設定なので大いに参考になるはず.
てか,ビルド設定の種類がたくさんあるなぁ.
とりあえずこのへんで.
Archive化する
ソリューションエクスプローラで,Androidプロジェクトで右クリックしてコンテキストメニューからArchive
を選択する.
もしくはVisual StudioのメニューからTools > Archive Manager
を選択する.
Archive Manger
タブが開く.
Archive化が実行され,以下のように表示されるので,完了するまで待つ.
成功したら,Distribute...
ボタンを押す.
このようなダイアログ(Distribution Channel )が表示される.
ここでは2つの選択肢がある.
Androidデバイスにインストール(サイドローディング)可能な署名されたAPKをローカルに保存する.
詳しくはこちら.
Google Playに署名されたAPKを発行する.
詳しくはこちら.
Ad Hoc
Ad Hoc
ボタンを押すと次の画面(Signing Identity)が表示される.
APKを発行するためには署名キー(または証明書)で署名する必要がある.
既存の署名キーがある場合はImport
ボタンを押す.
新規作成する場合は+
ボタンを押す.
ここでは新規作成する.
以下のダイアログが開く. Visual Studio opens dialog as shown in the next screenshot.とかdialog is displayedとか.
Androidアプリへの署名に必要な情報をここで入力する.
AliasとPasswordを設定し,後半の情報は少なくとも1つ入力すれば良いので,Organization(組織)だけ入力した.
Aliasはまあこの署名キーを区別するための名前だと思うので,適当に決めた.
Createボタンを押す.
すると以下のように,リストにアイテムが追加された.
harusoft
という名前で署名キーが作成されたということか.
この署名キーの保存場所は,以下のとおりである.
C:\Users\{USERNAME}\AppData\Local\Xamarin\Mono for Android\Keystore\{ALIAS}\{ALIAS}.keystore
この{ALIAS}
の部分が,先程設定したAliasの文字列になる.つまりさっきAliasにharusoft
という文字列を指定したので,私の場合は以下のようになる.
C:\Users\hoge\AppData\Local\Xamarin\Mono for Android\Keystore\harusoft\harusoft.keystore
注意!
この署名キーをなくさないこと! ぐぬぬ.どこに保管しておくのが良いのだろうか. プライベートなリポジトリか?
続いて,その署名キーを選択した状態で,ダイアログ下部のSave as
ボタンを押す.
APKファイルの保存先を指定するダイアログが開くので,好きな場所を指定する.今回はデスクトップにした.
Save
ボタンを押す.
パスワードを訊かれるので,先程設定した署名キーのパスワードを入力する.
OKボタンを押すと,指定したディレクトリに以下のようにAPKファイルが作成される.
APKのサイドローディング
署名されたAPKファイルが無事生成できたので,これをAndroid端末にインストールする.
AndroidアプリのサイドローディングについてはGoogleの公式サイトを参考にする.
このページの説明によると,サイドローディングとは、Google Play などのアプリストアを使用せずに手動でアプリをデバイスにインストールすることを指します,とのこと.
なるほど.
PCとAndroid端末をつないで,Android内のいずれかのフォルダにAPKファイルをコピーし,Android側でそのファイルを開けば,インストールが始まる.
色々警告されるが,自分で作ったものなので自信を持って進んでいけばよい.
dot net conf 2020 xamarin メモ
サンプルコード github.com
Keynote : Xamarin All The Things
AndroidのProfiled AOT
Profiled AOTはStartup Speedが速く(AOTについで)でAPK SizeがAOTよりも小さい(Normalよりは大きい).
バランスがいい.
Type | Startup Speed | APK Size |
---|---|---|
Normal | 2s914ms | 16.1 MB |
AOT | 1s18ms | 34.6 MB |
Startup Tracing | 1s518ms | 20.1 MB |
Visual StudioでAndroidプロジェクトのProfileを開く.
左ペインからAndroid Options
タブを選択し,Enable Startup Tracing
という項目にチェックをつける.
試してみた.
- チェックなし.8.3MB.
- チェックあり.8.4MB.
Xamarin.Formsデモ
FlyMeアプリケーション.
All Things Xamarin.Forms Shell
Shellは
- アプリケーションの構造を宣言的に記述できる
- ナビゲーションをシンプルにする
- カスタマイズ性に優れる
- 作りたいアプリを素早く開発できる
Shellの見た目を設定する
細かく変更できる.
ドロワーメニューの見た目を設定する
DataTemplateをResourceDictionary内に定義して,それを使う.
StyleClass (Xamarin.Forms 4.6以降)
Shellに限らない.すべての要素に有効な機能.
HTML,CSSのclassのようにスタイルを適用できる. 複数適用できるのが特徴.
追記
ShellのFlyoutItemに関するスタイルの指定については以下のShell Fly-out Styling
の項目を参照する.
Tabの見た目を設定する
Shellのナビゲーション(遷移)
- 遷移先に名前をつける.
<Tab Title="Details" Route="Details"....
- どこからでも遷移できる
await Shell.Current.GoToAsync("//Details");
- ページとRoutingを登録しておける
Routing.RegisterRoute("ModalPage", typeof(ModalPage));
- Deep link ナビゲーション
await GoToAsync("//Details/ModalPage");
文字列でナビゲーションできる.
アプリから離脱しても,その時のURLを記録してさえ於けば,復帰も簡単.
モーダルページ
Shell.PresentationMode="Modal"
await Shell.Current.GoToAsync("ModalPage");
Search Handler
これ使いたいな.
BackButtonBehavior
TextOverrideで戻るボタンのテキストを変更できる IconOverrideで戻るボタンのアイコンを変更できる
Shellの特徴
ContentPage,MasterDetailPage,NavigationPage,TabbedPageをまとめてShellで置き換えることができる.
Shell.TitleViewでTitleBarのUIを作成できる.
Shellの今後
- 3rdパーティ製のMVVMサポート(Prism,FreshMVVM,RxUIなど)
- TitleViewの修正
- すべてをテンプレート化
- デュアルスクリーンをサポート
- パフォーマンスの向上
QA
GoToはAsyncだけれど,OnNavigatedなどはAsyncではない.
Spectacular Components for Xamarin Apps
MacOSもXamarin.Formsで?
- Expressive
- MVVM Atom
- HttpTracer
- AnyBind
- TinyInsights
- Shiny
Shinyの紹介
- Background Task
- Notification
Resizetizer
通常,様々なディスプレーサイズに合わせて,各画像ごとに複数のサイズを用意しなければならない.
このライブラリは画像を一つ用意しておけば,それらを自動的に生成してくれるもの?
Control Vender製のコントロールについて紹介
Communityによって提供されるコントロール
- Magic Gradients
- Color Picker Control
- XamAnimation
- Sheet Control
- Xamlly
- PancakeView
DebugRainbowsの紹介
開発のための機能がある - 各コントロールに色をつけて領域をわかりやすくする - 格子を表示して,コントロールの位置をわかりやすくできる <- Xamarin.Forms.DebugRainbowsだ.これ
PancakeViewの紹介
FrameやImage的なものの枠を三角形にしたり1つの角だけ丸めたり,色々できる.
SkiaSharp
SkiaSharpを使ったライブラリの紹介
- Balloony
- Magic Gradients
- Microchart
- Mapsui
- Aurora Highlights
- eliteKit
- Draw2D
Sharpnado
- CollectionViewのアイテムの入れ替えなどができる
Task Loaderとは?
Building Beautiful Apps with Xamarin.Forms
おしゃれなアプリとは?
- とても主観的なもの
- トレンドに左右される
- 無意識に
- 重要なものの不足を補えない
- すごい開発者がすごいデザイナとはいえない
なんで評判がよくない?
- シンプルなデータ入力アプリに向いているというマーケティングを行った
- 過去の経験
- 「Forms」という言葉の印象
おしゃれUIの要素
- グラデーション
- SkiaSharp
- MagicGradients
- PancakeView
- Built-in Xamarin Forms Gradients(そのうち)
- 影
- iOSでは好きなようにできる
- Androidでは制限がある
- フォント,アイコン
- Google Fonts
- FontAwesome などなど
- 色
- 色(の組み合わせなど)を生成するツールを使う
- 形
- 動き,アニメーション
- Micro Animation
- Lottie
- Xamarin.Forms built-in animation
- Xamanimation
- Page Transitions
- Xamarin.Plugin.SharedTransition
おしゃれさのためだけではない
Styleを使おう!
UIの再利用性を高めるためにStyleを使う.
Button,色,フォントなどなどを個別のファイルに分けてStyleを定義しておく.
練習の仕方
- オンラインで色々探してインスピレーションを得る
- 実際に試してみる
- 後はやるだけ
具体的には
- デザインをまとまりごとに分ける
- どのように構成したらいいかを考える
サンプル
github.com/sthewissen/FocusOnXamarin
GridでRowDefinition
作れなければ,それっぽく見せればいい
If you can't make it, fake it
おすすめのサイト,パッケージやフォントなどのリソース
Xamarin Productivity to the Max
Code Build Deploy Iterate この繰り返しを迅速に行うには.
- IntelliCode
Xamarin.Forms.Visual.Materialとは. iOSとAndroidで同じMaterialDesignな見た目に統一することができる.
完全ではない.
Testing Your Xamarin Apps
テストの階層は上から順に以下のとおり.
UI Test エンドユーザーの視点でのテスト.Viewに対して行う.
Automated Service Test Serviceが期待どおりに動作して,正しいデータを返すかをテストする.
Automated Unit Test 機能の動作を確認するためのテスト.ViewModelに対して行う.
FIRSTの原則
Fast テストは遅いものだけれども,遅くならないように書かなければならない.
Independent 他のテストを呼び出してはいけない.
Repeatable アクセストークンのような定数を使ってはいけない.MockサーバーやMockサービスを使う
Self-validating Assertsを使う
Timely 複雑にならないようにする.そのためにはアクションを複数のテストに分ける必要があるかもしれない.
Unitテストの基本的な構成
AAAパターン
で書く.
- Arrange
- Act
- Assert
テスト対象
public int MySum(int a, int b) { return a + b; }
テストコード
[Test] public void TestMySum() { //Arrange int a = 5; int b = 7; //Act int result = MySum(a, b); //Assert Assert.AreEqual(12,result); }
テスト条件を設定(Arrange)して,実行(Act)して,判定(Assert)する.
なぜテストをしなければならないのか
テストしよう
Mock(Moq)サービスを使ってインターフェースのインスタンスを作成する.
ViewModelを使っている場合,テストするときにBinding,JSON parsers,PluginsやXamarin.Formsの機能などXamarin開発チームによってテストされているものについては考える必要はない.
自分のコマンドやロジックをViewModel越しにテストする.
選択したテスティングフレームワークに従ってテストを書くこと.
IntelliTestが役に立つよ!
すべてにInterfaceを
アプリに様々なプラグインやパッケージをインストールするよね.Xamarin.Essentialsとか.
例えばXamarin.Essentialsを使うなら,IXamarinEssentialsを作ってプロパティとメソッドだけを公開すると,柔軟性が高くなる.
Unit Test Demo
UI Test
BDDアプローチ
Behavior Driven Development
技術的な言葉を使わずに誰もがわかる言葉でテストを記述する.(例えば,Gherkinのようなドメイン記述言語など)
例示を用いて議論を行うことを通じて,どのようなソフトウェアを作るのかについて共通の理解を構築するためのアプローチ.
例えば:
As a [Role] I want [Feature] So that [Benefit] Roleとして Benefitを得るために Featureがほしい
つまり,「誰が何のためにどんな機能がほしい」のかを明確にするということか.
これに従うと:
As a [Registered User] I want [to be able to login] so that [I can see the Application] Scenario 1: User is able to log in Given that I am a registered user, when I enter the username 'Codrina' and password 'ladybug', then I should see the first screen of the app.
UI Test Demo
UIテストをどうするか.
Going Reactive with Reactive Extensions & UI
ReactiveUIについて.
よく見るこのイベントハンドラ.
SearchBar.TextChanged += (sender, args) => { };
textChangedEvents .Throttle(TimeSpan.FromMilliseconds(750),RxApp.TaskpoolScheduler) .Select(x => x.NewTextValue.Trim()) .DistinctUntilChanged() .Where(x => !string.IsNullOrWhiteSpace(x)) .ObserveOn(RxApp.MainThreadScheduler) .InvokeCommand(this, x => x.ViewModel.Search) .DisposeWith(ViewBindings);
ロジックのすべてをコンストラクタにまとめることができる.
テキスト
github.com/kentcb
イベントを普通に使うのであれば役に立つよ.
All Things Xamarin.Forms Shell
Developing Performant Xamarin Apps
Improve Xamrin.Forms App Performance という動画があるのでチェックするように.
Performance Optimizations
- 起動時にすべてのServiceを開始/登録しないこと.可能な限りLazyロードする.
- montemagnoさんのTurnip TrackerでLazy使ってる.
- 起動時にすべてのデータをダウンロードしない.
- User Experienceについて考える
- Lottieつかったり
- iOS: Launch Storyboard
- Android: Splash Screen Activity
Androidの起動時について
- AOT with startup tracingを使う
- Custom Profiles with Startup Tracingを使う.
Async/Awaitのベストプラクティスを使う
- 同時に実行できるところで,処理を順次行わないようにする.
- すべてのTaskが完了することを不必要に待たないようにする
- ループの中で
Task.Run
を使わない - Cancellation tokensを使うこと
順次処理と並行処理
- 例1:
Okay Codeではループの中でawaitを使って処理を一つずつ完了している.
Better CodeではWhenAll
を使ってすべての処理を同時に処理している.
- 例2:
Okay CodeではWhenAllを使ってすべてのリクエストの結果が得られるまでawaitして,結果のリストからオブジェクトを取得している.
Better CodeではWhenAnyを使って,結果が得られたものから直ちにオブジェクトを取得.
こちらの方が効率が良いとのこと.
Cancellation tokens
Taskのキャンセルに使用する.
tokenを作って,それをTaskの実行時に渡す.そして,Cancel()メソッドを実行すると,Taskの実行を終了させることができる.
var cts = new CancellationTokenSource();
var token = cts.Token;
Task.Run(async() => await someLongRunningAction(),tokens);
cts.Cancel();
しかし,Cancelのハンドリングは以下のように並行処理の中にtokenを介してThrowIfCancellationRequested()
を記述しておく必要がある.
Cancelされた後に,この記述の位置に来たときにTaskがキャンセルされる.
var cts = new CancellationTokenSource(); var token = cts.Token; Task.Run(async() => await { for (int i = 0; i<3; i++){ token.ThrowIfCancellationRequested(); } },tokens); cts.Cancel();
参考:
Event Handlers
public class Subscriber :IDisposable { readonly Publisher publisher; EventHandler handler; public Subscriber(Publisher publish) { publisher = publish; handler=(sender,e)=>{ Debug.Writeline("The publisher notified the subscriber of an event."); }; publisher.MyEvent+= } public void Dispose() { publisher.MyEvent -=handler; } }
public class Publisher { public event EventHandler MyEvent; public void OnMyEventFires() { if(MyEvent != null){ MyEvent(this,EventArgs.Empty); } } } public class Subscriber: IDisposable { readonly Publisher; public Subscriber(Publisher publish) { publisher=publish; publisher.MyEvent+=OnMyEventFires; } void OnMyEventFires (object sender,EventArgs e) { Debug.Writeline("..."); } public void Dispose () { publisher.MyEvent -= OnMyEventFires; } }
データのロード
- ローカルデータをまず読み込む
- Webとのデータのやり取りを最小限に抑える.
- ただのラベルやタイトルといった静的なデータをバインドしないこと.
Profilerを活用しろ
- 確認する点
- メモリーリーク,
- 大きな画像
- 大きなオブジェクトグラフ
- 広いスコープ
相互参照
計測する点
- 起動時間
- 実行時間
- メモリ消費
- CPU利用状況
- ネットワーク状況
- I/O状況
Icon, Image, Asset
ディスプレイの解像度ごとに画像の解像度も最適化するのが良い.
でも,それを手動で行うのは手間なので,ResizetizerNTというNugetパッケージがある.
レイアウトについて
フォントについて
- フォントを
- 画像を使わずに,アイコンフォントを使うこと
- 非表示のタブページの扱いに気をつけること
参考: