- 投稿日:2020-05-23T21:25:41+09:00
【Flutter】flutter_native_splashを使ったときに、Androidでスプラッシュ画面からアプリ画面に遷移する際、画面が真っ黒になる件の対応
経緯
こちらを参考にFlutterにスプラッシュ画面を導入してみたのですが、Androidでスプラッシュ画面からアプリ画面に遷移する際、一時的に画面が真っ黒になる事象が起きました。
本当はフェードインアニメーションでアプリ画面に遷移するはずなのですが...
(iOSは未検証)バージョンなど
Flutter:(Channel stable, v1.17.0, on Microsoft Windows [Version 10.0.18362.836], locale ja-JP) Dart:2.7 flutter_native_splash:0.1.9解決方法
android/app/src/main/AndroidManifest.xml
の<activity android:name=".MainActivity" ...>
タグの中に下記<meta-data>
タグを追加すると直りました。<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background" />
<meta-data>
タグ追加後のXMLはこんなイメージです。<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.appname"> <!-- io.flutter.app.FlutterApplication is an android.app.Application that calls FlutterMain.startInitialization(this); in its onCreate method. In most cases you can leave this as-is, but you if you want to provide additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> <application android:name="io.flutter.app.FlutterApplication" android:label="appname" android:icon="@mipmap/ic_launcher"> <activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- splash後に画面が黒くなる件の対応 ここから --> <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background" /> <!-- splash後に画面が黒くなる件の対応 ここまで --> </activity> <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data android:name="flutterEmbedding" android:value="2" /> </application> </manifest>参考
https://github.com/henriquearthur/flutter_native_splash/issues/54
5/5にlance-aurorさんが書いている内容をマネてみると直りました。(感謝です...)
- 投稿日:2020-05-23T16:58:42+09:00
[Flutter] Apple Watchの呼吸アプリのアニメーションを作ります。
こんにちは~Dreamwalkerです。
もう夏ですね。暑いです始める前に
呼吸アプリを利用している方いらっしゃいますか?
ストレス溜まっている時、とてもおすすめです。こんな形のアプリなんですが、
Use the Breathe app https://support.apple.com/en-us/HT206999この呼吸アプリのアニメーションをFlutterでやってみまいと思います。
コード
CustomPaint Widgetを使います。
CustomPaintの中にはPainterというのが必要です。
Painter
Animation
アニメーションはCurvedAnimationを活用します。
MixinとしてSingleTickerProviderStateMixinをState Classにつきます。
アニメーションを使いたいなら必要な事になります。あと、initStateのメソッドの中にインスタンス化します。
結果
1 2 3 4
- 投稿日:2020-05-23T15:49:19+09:00
AndroidStudioでHello Worldを表示させる
はじめに
新規アプリケーションで空のアクティビティを選択して準備が終わっていることが前提です。
Shift + F10を押すとコンパイルが開始されて仮想Androidが起動してアプリの実行が行われます。
正常に起動するとHello Worldと表示されます。Hello World!と表示されたのはなぜ?
空のアクティビティを実行すると画面中央に「Hello World!」と定番の表示が行われました。
これはエディタ画面の上側にあるactivity_main.xmlタブを開いて表示される中身に秘密があります。ホームページを作るときのHTMLのようにタグが並んでいて TextViewの表示の何行か下に「android:text="Hello World!"」という行があります。
つまりこれが表示されているわけです。
この部分を変更して実行すると画面に反映されるのがわかります。
基礎演算の理解と練習
エディタの上にあるタグ「activity_main.xml」をクリックします。
この部分はこれまで開発環境で開いたファイルが表示されていますが、間違って閉じた場合は画面左側のプロジェクト構成を表示しているツリーから
app/java/[プロジェクト名]にあるMainActivity.ktを開きましょう。レイアウトファイルのactivity_main.xmlのタブを閉じてしまった場合はapp/res/layout/にあるactivity_main.xmlを開きます。
activity_main.xmlを開くと表示されるXMLに1行「android:id="@+id/tvMain"」を追加して下記のようにしあます。
入力するのが大変に感じますが、実際は空行で「id」と入力されると補完されるのでEnterで確定、その後自動的に「@+id/」が候補に出るのでEnterを押した後 tvMain と入力するだけです。activity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/tvMain" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>次はMainActivity.ktの方を開きます。
MainActivity.ktを開くとすでに
MainActivity.ktclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }このようなプログラムが記述されています。
折角なのでこれを使ってAndroidやKotlinの動きを確認してみましょう。MainActivity.ktclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val tvMain = findViewById<TextView>(R.id.tvMain) tvMain.setText("Hello Kotlin World!") } }2行追加しました。これで実行すると Hello Kotlin World に変わったはずです。
表示が変わった理由
表示が変わったのはアプリが実行されると共に追加した2行が処理されたのが原因です。
Windowsアプリなら Formの Create やShowイベント。
マイコンのプログラムなら setupやmainに該当するのが
override fun onCreate(savedInstanceState: Bundle?) {
になります。ちょっと難しいけど説明
class MainActivity : AppCompatActivity() {
AppCompatActivity とクラスが他で定義されていてそれをMainActivityという名前で使います。override fun onCreate(savedInstanceState: Bundle?) {
AppCompatActivityには抽象クラスと呼ばれる onCreateメソッド(イベントのほうがわかりやすい)が定義されているので、AppCompatActivityを使った場合は onCreateを実装します。super.onCreate(savedInstanceState)
onCreate はクラスが生成されるときに発生するイベントですが、その他の処理は定義元のクラスにあるので、この1行を書いてそっちで初期化処理を済ませます。
他言語だとinherited とかですね。setContentView(R.layout.activity_main)
表示レイアウト R.layout.activity_main に従って画面を表示します。
R.layout.activity_main ってのはエディタ左側の app/res の resが Rになったと思えばわかりやすいです。追加した2行
追加した2行
val tvMain = findViewById(R.id.tvMain)
定数 tvMain を定義します。
定義後に初期化し、その初期値は
findViewById で該当するレイアウト上のパーツを探します。
そのパーツは「TextView」型なのであらかじめこの型が返ってくることを期待します。
R.id.tvMainにてtvMainという IDが付いたパーツ※先ほどXMLに追加したものを参照します。tvMain.setText("Hello Kotlin World!")
代入したtvMainはTextViewクラスなのでTextというプロパティを持ちます。
そのプロパティへ代入用のメソッド(関数)setTextを使用して値を代入します。アプリ起動時に生成されるクラスの生成イベントに割り込んで、元々の処理は上位クラスに任せた後、レイアウト上にある指定したID tvMainを持つパーツを検索して、そのパーツのテキストに指定したものを代入するといった処理が行われているのです。
この初期化処理に便乗して Kotlinの変数や基礎演算を覚えてしまいましょう。
- 投稿日:2020-05-23T02:19:38+09:00
マテリアルデザイン導入直後にやること
概要
Androidプロジェクトにマテリアルデザインを導入後にやることを書きます。
Androidプロジェクト作成した後のHello worldに対して行います。ライブラリ導入
マテリアルデザインを導入するために、
build.gradle
を以下のように記載します。build.gradle// material design api 'com.google.android.material:material:1.2.0-alpha06'styles.xmlの変更
res/values/styles.xml
を以下のように記載します。<!-- Base application theme. --> <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style>activity_main.xmlの変更
res/layout/activity_main.xml
を変更して表示を確認します。<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.google.android.material.button.MaterialButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="確認" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>結果
余談
res/values/styles.xml
を初期値の状態のままだと以下のエラーが表示されます。Error inflating class com.google.android.material.button.MaterialButton参考
- Developer tutorials
- Getting started with Material Components for Android
- 投稿日:2020-05-23T01:01:44+09:00
LiveDataとObservableFieldの違い
LiveDataとObservableField
どちらも何かの値を
監視
するために使用されるものである。
基本的な使い方に関しては下記の通りである。Class A { val liveData = MutableLiveData<String>() val observableField = ObservableField<String>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) liveData.observe(this, Observer { Log.d("livedata", "LiveData Changed ${it}") }) observableField.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable?, propertyId: Int) { Log.d("observableField", "ObservableField Changed ${(sender as? ObservableField<String>)?.get()}") } }) } override fun onResume() { super.onResume() liveData.value = "one" liveData.value = "two" liveData.value = "two" observableField.set("one") observableField.set("two") observableField.set("two") } }onResume()で値を更新する事によって、
LiveData
はobserve
内の処理が実行され、
ObservableField
はonPropertyChanged
の処理が実行される。LiveDataとObservableFieldの動作の違い
両者とも値を監視するものではあるが、何が違ってくるのか。
上記の処理のLogを表示してみる。D/livedata: LiveData Changed one D/livedata: LiveData Changed two D/livedata: LiveData Changed two D/observableField: ObservableField Changed one D/observableField: ObservableField Changed twoonResumeで
LiveData/ObservableField
両者に3回ずつ値を代入したのだが、
LiveData : 3回通知
ObservableField : 2回通知
と差がある。簡単に説明すると、
LiveData
: 値がセットされた時に通知が送られる
ObservableField
: 値が変更された時に通知が送られるという大きな違いがある。
今回の実行例だと、2/3回目に値として代入したものは
"two"
という値を連続で代入しているため、値としては同値として処理されるため、ObservableField
に通知が行かず、LiveData
に通知が行った。まとめ
LiveData
とObservableField
の両者とも扱いやすく、値を監視する手段としてよく使用されていると思われる。
最近はLiveData
がかなり普及してきているので、元々ObservableField
を使用していたプロジェクトもLiveData
を併用する場合もあるかもしれない。
片方のみ使用するのであれば特に問題は無いとは思うが、両者が混在しているプロジェクトに関しては、上記のような違いがあるということを念頭に置いて使用していきたい。参考資料
Android Developer LiveData
Android Developer ObservableField