メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

Flutter製AndroidアプリできちんとSplashを設定する

Pluginのマイグレーション作業をしてたら気になって調べたのでまとめます。

情報はFlutter v1.17.5のものです。

Splash

Androidのときにまとめましたが、Flutterでも同じです。

sakebook.hatenablog.com

Flutterの場合、公式にドキュメントが用意されており、Splash用のViewも使えるように提供されています。

設定できる箇所

公式で推奨されているのでそれに則ると、

  • Activityに直指定するtheme(Launch Theme)
  • meta-dataとして指定するView(Splash View)
  • meta-dataとして指定するtheme(Normal Theme)

があります。それぞれを設定する・しないことで何が起きるかを解説します。

Activityに直指定するtheme(Launch Theme)

  • AndroidManifest.xml
<activity
    android:name=".MyActivity"
    android:theme="@style/LaunchTheme"
    // ...
    >

ここで指定するthemeは起動した瞬間から、FlutterActivityでthemeを設定するまで見えるものです。

何も設定していなければ、次のようになります。

これはFlutterActivityの上にFlutterのレンダリングエンジンで描画されています。Activityのthemeの指定でToolbarを取り除いていないため、Toolbarより下の部分のみFlutterで描画されているわけです。

@android:style/Theme.Black.NoTitleBar を親にしたthemeを設定することでToolbarを取り除き、全画面描画域にできます。

では次のようなthemeを指定してみましょう。

  • res/drawable/launch_background.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/holo_green_light" />
</layer-list>
  • res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <item name="android:windowBackground">@drawable/launch_background</item>
    </style>
</resources>

Toolbarの問題は解決されましたね。ちょっとパッと表示されるのが気になります。

meta-dataとして指定するView(Splash View)

  • AndroidManifest.xml
<activity
    ...
    <meta-data
        android:name="io.flutter.embedding.android.SplashScreenDrawable"
        android:resource="@drawable/splash_background" />
  • res/drawable/splash_background.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/holo_orange_dark" />
    <item>
        <bitmap
            android:gravity="center"
            android:tileMode="mirror"
            android:src="@mipmap/ic_launcher" />
    </item>
</layer-list>

ここで指定するDrawableは、FlutterActivityが起動してからFlutterのUIが描画され始めるまで表示されます。

パッと切り替わったのがなくなりました。ここで追加したViewは、アニメーションと共に除去されます。

実はここだけ、内部的にはtheme設定ではなくViewとして利用しているので、Drawableリソースで表現できるものであればだいたい利用できます。

meta-dataとして指定するtheme(Normal Theme)

  • AndroidManifest.xml
<application
    ...
        <activity
            ...
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
  • res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
        <style name="NormalTheme" parent="@android:style/Theme.WithActionBar">
        <item name="android:windowBackground">@android:color/holo_red_light</item>
    </style>
</resources>

ここで指定するthemeはFlutterActivityが起動してからFlutterのUIが描画され始めるまで見えるものです。

この指定で、はじめに説明していた「Launch Theme」を上書きしています。わりと長い間表示されるように見えますが、「Splash View」と合わせることでほとんど見えなくなります。画面回転時などにも見えるので、FlutterのUIに設定している色に近いソリッドな背景色を使用することが公式でオススメされています。

組み合わせ

では3つとも設定した場合にどうなるのかを見てみましょう。

「Normal Theme」は全く見えなかったと思います。「Launch Theme」が一瞬見えて、「Splash View」が表示され、その間にFlutterのUIが描画され、アニメーションで消えるとともにFlutterrのUIが自然と画面に表示される流れになります。

ではちょっといい感じになるように設定してみます。

アプリの背景色をSplashと揃えてTwitterのようにアイコンを入れてみました。今回は前後しましたが、本来はアプリの背景色にSplashを揃えるのが望ましいです。

次のように設定してます。

  • AndroidManifest.xml
<application
    ...
    <activity
         android:theme="@style/LaunchTheme"
         ...
            <meta-data
                android:name="io.flutter.embedding.android.SplashScreenDrawable"
                android:resource="@drawable/splash_background" />

            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
            ...
  • res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <!-- 同じものを使いまわしてる -->
        <item name="android:windowBackground">@drawable/splash_background</item>
        <!-- status barを透過させて色の急激な変化を抑えている。フルスクリーンにするのもあり。 -->
        <item name="android:windowTranslucentStatus">true</item>
    </style>
    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
        <!-- 色は適当な色 -->
        <item name="android:windowBackground">@color/bg</item>
    </style>
</resources>
  • res/drawable/splash_background.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 色は適当な色 -->
    <item android:drawable="@color/bg" />
    <!-- Material Design Iconsから引っ張ってきたもの -->
    <item
        android:drawable="@drawable/ic_android_white"
        android:gravity="center"/>
</layer-list>

iOS

Androidは3つの設定がありましたが、iOSLaunchScreenというものがあるので、そちらを変更するだけです。

Xcodeを開き、LaunchScreen.storyboardにViewが設定されています。

このViewの色を変えたり、Assetsに画像を追加してViewに乗っけたりすれば反映されます。

シミュレーターで試していて変更が反映されない場合は、シミュレーターを初期化すると反映されます。

試しにこんな感じにしてみました。

この画像はSVGです。Material Design Iconsのものだとそのままでは使えなかったのでSF Symbolsのものを利用してます。

アニメーションについて

各OSで実装することも可能ですが、どちらもOSでも起動した後にViewを操作してアニメーションしてるように見せるものです。なので、FlutterのUIとの切り替えや遅延表示などカスタムする必要が出てきます。よほどの事情がなければ、起動時のOSの差分を受け止めた後の話なのでFlutter側で実装するのがいいと思います。

Rive(旧Flare)などを使うとリッチなものになるでしょう。

まとめ

Flutterを触っていても、FlutterのUIが立ち上がるまでは各OSの世界の話になります。少しの手間で丁寧なアプリの印象を与えられるので取り組んでおいた方が良いと思います。

参考

Adding a splash screen to your mobile app - Flutter

https://developer.apple.com/documentation/xcode/creating_custom_symbol_images_for_your_app

https://developer.apple.com/forums/thread/110295

Rive - Bring your apps and games to life with real-time animation.

電動式昇降デスクを設置する上でのTips

電動式昇降デスクを導入しました。これまではIKEAのデスクを部屋に置いていました。

知ってる人はもう少ないかもですが、昔あったLivlisというあげたいものと欲しい人をつなげるサービスで頂いた思い出のあるものです(新卒で上京してきたときに頂いてそこから今まで使ってた)。

しかし、最近は天板がしなってきたのと、デスクの上が散らかってしまっていて作業はソファーなどで行うことが専らでした。

そこに加えてリモートワークだったので、改善したいと思い今回電動式昇降デスクを導入することにしました。

先に現在の環境を貼るとこんな感じになっています。

f:id:sakebook:20200710011116p:plain

導入にあたり工夫した点や、ここ改善の余地あるなというところを共有していきます。


電動式昇降デスク

選んだのはFlexispotのE3の白色です(E3W)。E6だとセーフティモーション機能というのがついているのと、天板がセットであるものは一部組み立てがなされているみたいです。

E6に目をつけてたのですがすぐには手に入らず、気持ちが抑えられなくなってE3を購入しました。

E3は天板を、Flexispot以外の物を取り付けることができます。E3は組み立てされていないので、横幅の調節が可能だからです。

自分はFlexispotで合わせて天板を購入しました。E3でも合わせて天板を購入するメリットは、いくつかねじ穴があらかじめ開けられていることです。Flexispotの天板でいいやって思ってる人はE6を買った方が組み立てをしなくて良いのでオススメです。組み立ての苦労分安く済ませたいのであればE3でも良いと思います。自分は途中、お金払ってでも組み立てて欲しい気持ちになったのでE6があったらそっちの方がよかったなと思います。

組み立ての注意点

初めに公式動画を参考にしました。

www.youtube.com

2分もない動画で、結構簡単に作れるなーと思ったのですがこれは罠でした。この動画では、天板を上に乗っけて下から穴を開ける方式を取ってるのですが、このやり方だと電動ドリルがないとほぼ無理です。

前述のねじ穴を利用しようとした時、予めデスクの幅を調整する必要があるのですが、これがぶっつけ本番のようになるのでお勧めしません。ねじ穴を利用しない場合は動画のやり方でも良いと思います。

ちなみに測ったところ、天板の左右それぞれ7.5cm内側に穴が空いてるので、125cmに足を調節すればうまくいくかもしれません。自分は初めから140cmに合わせてしまい、乗っけたタイミングで気付きました。その後解体してやり直しました。。

説明書通りのやり方であれば、電動ドリルはなくても可能ですが、結構きついです。女性の場合は必須だと思った方が良いです。

天板の高さを調節するコントローラーの設置場所ですが、デフォだと手前の左右どちらかになっています。個人的には横につける方がよかったなと感じました。というのも、椅子のアームレストが引っかかってしまうからです。個人の使い方によりますが、現状アームレストがデスクの下に入り込むような使い方をしている人は、コントローラを横につけた方が良いと思います。ちなみにそうなる人は、あまり良くない座り方です。

モニターアーム

31.5インチのモニターが完全に娯楽用になっていたので、どちらでも利用できるようにしました。

利用しているモニターはこちらです。PHILIPSの328P6AUBREB/11というものです。

USB-C対応と、LANケーブルも対応していてなかなかコスパはいいと思ってます。今なんか安いみたいです。自分が買ったのは2年前でしたが、4K対応してたらもっと良かったなと思います。

このモニターにHDMIでNexusPlayerを繋いでいました。

娯楽用としても引き続き使いたかったので、モニターアームを導入して作業中にはふさわしい位置に移動できるようにしました。

今回導入したモニターアームはこちらです。同僚から、使ってるけど具合がいいと聞いていたのでエルゴトロンにしました。

いい感じになりました!初めてのモニターアームだとちょっとテンション上がると思います。

しかし使ってて、2点改善点が見つかりました。

一つは、31.5インチの場合はモニターアームの腕が短いと回転するための高さが足りない場合があります。自分の今の環境だと、最大限に手前に伸ばせばデスクからはみ出た位置にモニターがくるので、その位置でのみ回転が可能です。

f:id:sakebook:20200712205136p:plain

ベッドから横になりながら利用する分には問題ないですが、わかっていたら長身ポールの方を選んでいました。インチ的には対応していたのですが、それはあくまで重量的な意味合いみたいです。

もう一つは、中央に取り付けると縮めた時にモニターアーム本体が邪魔してちょっと幅を取ってしまいます。説明書には自分の真正面に設置するようになっているのでそれに従ったのですが、もっと奥に追いやりたい場合は左右どちらかの位置に取り付けて、アームを伸ばしつつ奥に追いやる方がいいと思います。車のワイパーのようなイメージです。動かして自身の正面にもってくるようにすると後ろのモニターアームが幅を取らず、奥に追いやることができます。

f:id:sakebook:20200712211515p:plain

今の自分の環境だと、壁から20cm手前の位置にモニターがあります。モニター自身の厚みもありますが、もうちょっと奥に行ってほしい気持ちがあります。

モニター接続環境周り

自分はMBPを使っているので、USB-Cで接続してモニターに接続しようとしました。

オフィスで似たようなことをやっていたのでできることはわかっていたのですが、いざやってみるとうまくいきませんでした。原因は、使っていたUSB-Cのケーブルが映像に対応していないことでした。PD対応してるので悪くはないと思っていたのですが、映像の場合はまた別のものがあるみたいです。

こちらを購入して見たところ、うまく接続できました。映像出力というキーワードを付けて探すと良いです。

MBPをシュッと起きたかったのでパソコンスタンドを導入しました。自分の場合、PCの裏に薄いスタンディング用のアタッチメントを付けていたので、通常より厚みがある状態になっていました。

こちらのスタンドが、値段の割に質感もよく、幅の調整もできたので良かったです。ブランド名がOBENRIっておもしろわかりやすいですね。

HDMIが一つしかないモニターだったので、DisplayPortを活用してHDMIを開けておこうと考えました。そこでHDMIを出力にしてDisplayPortを入力にするケーブルを利用したのですが、うまくいきませんでした。どうやら、HDMIを出力にしてDisplayPortを入力というのはできないみたいです。なので今回は、給電機能付きの変換器を使うことで実現させました。

利用したのはこちらです。Cable Mattersというブランドのものです。

これでHDMIを余らせておくことができました。Switchとかに使いたいなと思ってます。

モニターの出力の切り替えですが、スリープになっている状態でどれかをアクティブすると勝手に切り替えできるので、モニターのボタンをぽちぽちする必要はないです。

複数アクティブにした場合は手動で切り替えが必要になると思いますが、現状の自分の使い方ではそのような事態は起きてないので不自由なく利用できてます。

ケーブル周り

ケーブルトレーを導入しました。紹介されてるようなものは売り切れだったので、天板の色に近そうな物を選んでみると、予想以上に色がマッチして大正解でした。Flexispotでマホガニーの天板を購入する人にはぜひおすすめです。

f:id:sakebook:20200712205754p:plain

写真右上のあたりです。

電源タップ2本用意して、一つはケーブルトレーに、もう一つは裏に磁石を貼り付けて天板裏の鉄部分に貼り付けました。こちらの商品です。電源タップを天板付近に用意することで、壁際のコンセントと直接触れる部分がなくなるので、足元がスッキリします。

ケーブルダクトがFlexispot公式に売られてて、欲しかったのですがこれまた品切れだったのでとりあえずで100均のケーブルチューブを購入したのですが収まりは良かったので結果的に良かったです。自分のものは白の半透明で色が中途半端でダサいですが、黒などはAmazonで売ってるようです。

f:id:sakebook:20200712212432p:plain

左側に垂れてる壁と接地しているケーブルはLANケーブルです。家の都合上無線ルーターをデスクに乗っけてます。

ケーブル束ねているのは、100均の結束バンドです。こんな感じでまとめた後に天板裏にくっつけてやると見えなくなっていい感じになると思います。ちょっとそのあたりは手を抜いています。

なんだかんだデバイスを充電したい時が来るので、充電コーナーを設けました。ここも少し手を抜いていて、ケーブルクリップは100均です。利用する頻度を考えたら、普段から見える必要はないのでこれでいいやと思ってます。

f:id:sakebook:20200712210325p:plain

UCB-C, Lightning, Fitbit, Micro USBを揃えてます。こちらのUSBハブが便利です。AnkerのPowerPortです。こちらはDisplayPortの給電にも利用してます。

スタンディング

電動式昇降デスクを導入する以前から、足元にマットを引いて疲れを取るようにしています。あんまり良さがわかってないですが、こちらを使っています。Bauhutteのものです。ですが、柔らかいスリッパとかでも良い気はしてます。

f:id:sakebook:20200712224458p:plain

自分にあった高さを探すのに、こちらが参考になります。

www.bauhutte.jp

座った状態のものもあります。

www.bauhutte.jp

まとめ

電動式昇降デスクを購入するか迷ってるなら買ったほうが良いです。もしも売り切れとかで、待てる人はE6の方をおすすめします。いろんな先人のブログに目を通しておくと、見えてない視点での気付きやTipsがあるので色々目を通してみると良いです。

次のまとめは参考になるものが多いのでオススメです。

note.com

Gradleタスクでadbを実行してアプリを起動する

ちょいちょいターミナルからビルドしてアプリを立ち上げたりとかしたくなるときがあり、都度一発でできてなくて面倒なところがありました。

そうすればいいのかーって思ったのでメモと、ちょっと汎用的にしたものを共有。

こちら見かけたやつです

  • app/build.gradle
task startDemo(type: Exec) {
    dependsOn 'installDebug'
    def adb = new File("${System.env.ANDROID_HOME}", "platform-tools${File.separator}adb")
    commandLine adb, "shell", "am", "start", "-n", "com.google.maps.android.utils.demo/.MainActivity"
}

Execタスクで実行するコマンドは、PATHを通してないとGradleタスクとして実行できないです。

PATHを通すにはコマンドをファイルで渡してあげることで存在確認しつつ実行可能になります。

これを、Kotlinでかつ汎用的に書き直してみました。

環境は

  • Android Gradle Plugin v4.0.0
  • Gradle v6.1.1

です。

  • app/build/gradle.kts
tasks {
    val sdkDir = project.android.sdkDirectory
    val adb = file("${sdkDir.path}${File.separator}platform-tools${File.separator}adb")
    project.android.buildTypes.forEach { buildType ->
        val typeName = buildType.name.capitalize()
        create("run$typeName", Exec::class) {
            dependsOn("install$typeName")
            setCommandLine("bash", "-e", "-c", """
                $adb shell am start -n ${'$'}(adb shell pm dump ${project.android.defaultConfig.applicationId}${buildType.applicationIdSuffix} | grep -A 2 android.intent.action.MAIN | head -2 | tail -1 | awk '{print ${'$'}2}')
            """.trimIndent())
        }
    }
}

SDKのPATHは、環境変数やlocal.propertiesを使わなくてもAGPで取得できます。なので任せてしまうのがいいです。

project.android.sdkDirectory

実際の環境だと、debugとreleaseなど複数のBuildTypeがある場合がほとんどだと思います。なのでBuildTypeごとにタスクを作成しています。今回の例だと runXXX という名前で作成してます。

create("run$typeName", Exec::class)

installXXXタスクに依存させることで、アプリのビルドとインストールはできている状態にしています。

dependsOn("install$typeName")

アプリを起動させる部分ですが、起動させるにはパッケージ名プレフィックス付きのコンポーネント名を指定します。

コンポーネント名を取得するのは、AndroidManifest.xmlに定義してあるのでそこからXmlSlurperで取り出す例を見かけますが、今回はアプリをビルドしてインストールも済んでいるので、adbコマンドを使って取り出します。

adb shell pm dump ${project.android.defaultConfig.applicationId}${buildType.applicationIdSuffix} | grep -A 2 android.intent.action.MAIN | head -2 | tail -1 | awk '{print ${'$'}2}'

debugを開発版としている場合、パッケージ名を被らせないようにしていると思うのでsuffixをつけて対応させます。

取得したコンポーネント名を、再度adbに渡してアプリを起動させています。

参考

GitHub - googlemaps/android-maps-utils: Maps SDK for Android Utility Library

Multiple bash commands in single Gradle Exec task vs. multiple Gradle Exec Tasks each with a single command - Stack Overflow

AndroidのGradleでbuildTypes毎にtaskを作成する方法 - 混沌とした備忘録

Android Debug Bridge(adb)  |  Android デベロッパー  |  Android Developers

Launch application from command line. · GitHub