- 投稿日:2021-03-14T22:07:30+09:00
SeekBarをカスタマイズする
UIパーツは基本的にシステムデフォルトのものをそのまま使いたい主義者なのですが、こういうデザインにしたいと要望を受ければ頑張らねばということで、SeekBarのカスタマイズってどうやるんだけっけ、をまとめてみます。
Androidのデフォルトのシークバーは、(プロジェクトのテンプレートから作成した場合の
Theme.MaterialComponents
系テーマを使ったものの場合、以下このテーマを前提にしています)
以下のような見た目をしています。テーマカラーの変更
単に色を変更するなら、なら
colorSecondary
で指定した色が使われるためこれで変更できます。<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <item name="colorSecondary">@color/purple_500</item>バーが伸びる前のところの色は、
Theme.MaterialComponents
なら、colorControlNormal
の色と、android:disabledAlpha
のα値を掛け合わせたものになっており、これで変更可能です。<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <item name="colorControlNormal">@color/purple_500</item> <item name="android:disabledAlpha">1.0</item>つまみの形状変更
移動させるところのつまみ部分はレイアウトXMLにて
android:thumb
でDrawableを指定することで変更可能です。<SeekBar android:thumb="@mipmap/ic_launcher"バーの形状変更
バーの形状変更はちょっと面倒です。DrawableをレイアウトXMLにて
android:progressDrawable
でDrawableを指定することで変更可能です。<SeekBar android:progressDrawable="@drawable/progress"ここで指定するDrawableは特定のIDを持つlayer-listである必要があります。
ベースの部分が@android:id/background
、プログレスの部分が@android:id/progress
です。
secondaryProgressが必要な場合は@android:id/secondaryProgress
を使いますが、これは今回扱いません。まずは単純に色を指定します。progressについては、この色で上書きするため
<clip>
で囲んで指定します。<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <color android:color="#000" /> </item> <item android:id="@android:id/progress"> <clip> <color android:color="#f00" /> </clip> </item> </layer-list>すると以下のようになります。
ちょっと思ってたのと違いますよねーちなみに、この大きさはドラッグ中のthumbの大きさと同じになっているみたいですが、thumbを他のDrawableにしたり、SeekBarにpaddingを設定しても変わらないようです
形状を角丸にしてみます。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <shape android:shape="rectangle"> <corners android:radius="16dp"/> <solid android:color="#000"/> </shape> </item> <item android:id="@android:id/progress"> <clip> <shape android:shape="rectangle"> <corners android:radius="16dp"/> <solid android:color="#f00"/> </shape> </clip> </item> </layer-list>角丸にはなりましたね。うーん、でも太い
XMLだけでなんとかする場合、layer-listのitemにはtop/bottomのマージンを設定できますね。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:bottom="8dp" android:top="8dp" > <shape android:shape="rectangle"> <corners android:radius="8dp" /> <solid android:color="#000" /> </shape> </item> <item android:id="@android:id/progress" android:bottom="8dp" android:top="8dp" > <clip> <shape android:shape="rectangle"> <corners android:radius="8dp" /> <solid android:color="#F00" /> </shape> </clip> </item> </layer-list>ってことでわずかに角丸になったSeekBarを作ることができます。
ただし、paddingで合わせるのは本来の方法ではないと思います。高さが変わったときなどに都度paddingの調整が必要になっていまいます。ですので、9-patchを使うほうが良いと思います。
9-patchを使ったバーのカスタマイズ
9-patchならさらに複雑な形状にもできますね。左右にぽっちがあるプログレスバーみたいなのを作りたいとします。
この場合、以下の2つの9-patch画像を作ります。
これをlayer-listから指定します。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <selector> <item android:drawable="@drawable/seek_background"/> </selector> </item> <item android:id="@android:id/progress"> <clip> <selector> <item android:drawable="@drawable/seek_progress"/> </selector> </clip> </item> </layer-list>以上です。
- 投稿日:2021-03-14T21:55:03+09:00
Flutterで普通のスワイプで画面をpopする
Flutterで
Navigator.push(context)
して遷移した先の画面で元の画面に戻るときにはNavigator.pop(context)
をアイコンに設置したりエッジスワイプしたりしますよね。
通常のスワイプでも戻したかったのでやり方を見つけるのに少々苦労しましたがなんとかできました。GestureDetector( onHorizontalDragUpdate: (details) { if (details.delta.dx > 18) { Navigator.pop(context); } }, child: ・・・, );このように
GestureDetector.onHorizontalDragUpdate
を設定して横方向のスワイプをトリガーにnavigator.pop(context)
を実行するようにしました。
details.delta.dx
はスワイプの速度で、正の向きが左→右です。しきい値として設定した18
を超えるとnavigator.pop(context)
を実行します。この値は実機で触りながら調整しました。参考
Flutter公式ドキュメント onHorizontalDragUpdate
https://api.flutter.dev/flutter/widgets/GestureDetector/onHorizontalDragUpdate.html
- 投稿日:2021-03-14T16:31:37+09:00
React/スマホアプリ開発未経験者がReact NativeでiOSアプリを作ってみた
JavaScript(フレームワーク未使用)業務経験1年・React/React Nativeもスマホアプリ開発のいろはも分からなかった私が、平日夜・休日の時間を使って、約2ヶ月(80時間)でReact NativeでiOSアプリを開発したときの流れや参考になった記事・動画を紹介します!
自分自身、何度も壁に打ち当たり、心が折れそうだったので、少しでも役に立てればと思います!
あと、少し込み入った問題になると、英語のサイトがかなり多くなってくるので、ポジティブに捉えるなら、良い英語の勉強になると思います!笑作った単語帳アプリlankiageはこちら
ソースコードはこちらそもそもReact/React Nativeとは?
Reactは、Facebook社が開発した、WebアプリケーションのUIを構築するためのライブラリです。
JavaScript三大フレームワーク・ライブラリとして、他にVue.jsやAngular.jsがありますが、世界的にReactが最も利用されているため、フロントエンドとしては最も身につけておくべきスキルだと思います。
- React公式サイト
- JavaScript経験ありReact初心者向け動画
【とらゼミ】トラハックのエンジニア学習講座 日本一わかりやすいReact入門一方、React Nativeは、JavaSciptでiOSアプリ・Androidアプリを、同時に、かつReactライクに開発できるフレームワークで、いわゆるクロスプラットフォームフレームワークの1つです。
従来は、OSに合わせて言語を使い分ける必要がありましたが、これによってコード1つで両方のアプリが開発できるわけです。
現在は、Flutterというフレームワークの方が注目されているという記事もありますが、DartというGoogleの独自言語ということもあり、React NativeはJavaScirpt経験者がさらっとスマホアプリに手を出すにはもってこいのライブラリだと思います。1. 開発環境構築編
1-1. 開発プロジェクトのインストール
coming soon...
1-2. Xcode
coming soon...
2. 実装編
実際に使用した主なライブラリについてご紹介します。
2-1. Realm(ローカルデータベース)
coming soon...
2-2. react-native-vector-icons(アイコン)
豊富なアイコンが使える便利なライブラリですが、かなりビルドに苦労しました。。。
特にこのステップを見落としがちなので、丁寧にやっていきましょう!
2-3. React Navigation(画面遷移)
画面間の遷移を扱うライブラリです。
似たライブラリとしてReact Native Navigationもありますが、設定ファイルであるAppDelegate.m
をいじる必要があった上に、使い勝手が良くないと感じたので、今回はReact Navigationを使うことにしました。3. アプリ申請編
coming soon...
4. 一通り開発をやってみて...
coming soon...
5. 実際に開発したアプリ
- 投稿日:2021-03-14T16:31:37+09:00
初めてReact NativeでiOSアプリを開発した時に参考にした記事・サイトたち
JavaScript(フレームワーク未使用)業務経験1年・React/React Nativeもスマホアプリ開発のいろはも分からなかった私が、平日夜・休日の時間を使って、約2ヶ月(80時間)かけてReact NativeでiOSアプリを開発したときの流れや参考になった記事・動画を紹介します!
自分自身、何度も壁に打ち当たり、心が折れそうだったので、少しでも役に立てればと思います!
あと日本語のドキュメントが少ないため、英語のサイトを見ることも多くなりますが、英語の良い勉強の機会だと思って、めげずに頑張りましょう!!作った外国語用単語帳アプリ"lankiage"はこちら(App Store)
ソースコードはこちら(Git Hub)そもそもReact/React Nativeとは?
Reactは、Facebook社が開発した、WebアプリケーションのUIを構築するためのライブラリです。
JavaScript三大フレームワーク・ライブラリとして、他にVue.jsやAngular.jsがありますが、世界的にReactが最も利用されているため、フロントエンドとしては最も身につけておくべきスキルだと思います。
- React公式サイト
- JavaScript経験ありReact初心者向け動画
【とらゼミ】トラハックのエンジニア学習講座 日本一わかりやすいReact入門一方、React Nativeは、JavaSciptでiOSアプリ・Androidアプリを、同時に、かつReactライクに開発できるフレームワークで、いわゆるクロスプラットフォームフレームワークの1つです。
従来は、OSに合わせて言語を使い分ける必要がありましたが、これによってコード1つで両方のアプリが開発できるわけです。
現在は、Flutterというフレームワークの方が注目されているという記事もありますが、DartというGoogleの独自言語ということもあり、React NativeはJavaScirpt経験者がさらっとスマホアプリに手を出すにはもってこいのライブラリだと思います。
※React Native勉強中にFlutterの勢いを知ったのはちょっとショックでした(汗)1. 開発環境構築編
1-1. 開発プロジェクトのインストール
coming soon...
1-2. Xcode
coming soon...
2. 実装編
今回、実際に使用したライブラリについてご紹介します。
2-1. Realm(モバイルデータベース)
世はクラウドブームですが、今回は全てデータをモバイル(ローカル)で保存することにしました。(バックエンドも開発するとなると、かなりの負荷があるので、やめました。)
その際に使用したライブラリがこのRealmです。
他にCoreDataやSQLiteというのがあるようですが、realmが話題のようなので、これを使いました。
※React Native Expoを使っている方は、Realmが利用できないので、ご注意ください。2-2. react-native-vector-icons(アイコン)
豊富なアイコンが使える便利なライブラリですが、かなりビルドに苦労しました。。。
特にXcodeの設定を変更するこのステップを見落としがちなので、丁寧にやっていきましょう!
2-3. React Navigation(画面遷移)
画面間の遷移を扱うライブラリです。
似たライブラリとしてReact Native Navigationもありますが、設定ファイルであるAppDelegate.m
をいじる必要があった上に、使い勝手が良くないと感じたので、今回はReact Navigationを使うことにしました。2-4. react-native-picker-select(セレクトボックス)
React Nativeのセレクトボックスライブラリは中々少ないようで、今回はこれを使いました。ちょっと微妙ですが。。
2-5. その他
その他、使用頻度は高くないかもしれませんが、今回使用したライブラリはこちらです。
- Google翻訳(translate-google-api)
- 多言語音声読み上げ(react-native-tts)
- スライド式のページネーション(react-native-slider)
- 裏表のあるカードのアニメーション(react-native-card-flip)
3. アプリ申請編
基本的には、[2020年版]AppleにiOSアプリを申請する方法を参考に進めましたが、
まず第一にやるべきは、Apple Developer Programへの登録です。
(年間税込¥12,980と高額ですが、目を瞑りましょう。。)
進めていく中で一番厄介だったのが、スクリーンショット登録だったので、それについて解説します。スクリーンショット登録
App Storeのプレビューに表示される、いわばそのアプリの顔ともなる重要な部分です!
下の「スクリーンショット」の3枚がこれに当たります。
画面のスクリーンショットを全面にしても申請は通りますが、そのアプリのメリットや特徴をできるだけ一目でわかるように工夫しましょう。
2021年3月現在、iPhone 6.5インチ、iPhone 5.5インチ、iPad 12.9インチの3サイズのスクリーンショットをそれぞれ3つ登録する必要があるので、厄介ですが丁寧にやりましょう!
では、どのように良い感じのスクリーンショットを作るかですが、、無料でやるのにあまり参考にできるサイトがなかったので、簡単に私が行ったやり方を共有します。
- 登録すべきスクリーンショットサイズのシミュレータ(3種類)を起動し、スクリーンショットを撮る。
- AppLauchPadでGLOBAL SCREENSHOTから1.で撮ったスクリーンショットを選択(5.5インチでやると、iPhoneのフレームに余白がありますが、無視して大丈夫です。)
![]()
- ダウンロード画面から、エクスポートしたいサイズのスクリーンを右クリックで「画像をコピー」
![]()
- keynoteでスライドサイズをスクリーンショットのサイズにカスタマイズし、3.でコピーした画像を貼り付けて、編集する。(keynoteのインスタントアルファ機能を使うと、背景の白地がなくなり、綺麗にフレームだけを切り出せます!)
4. 一通り開発をやってみて...
一番の感想は、ビルドのエラーが多すぎて辛いということ(泣)
こればかりは初心者のうちは仕方ないですかね、、
ただ徐々に耐性がついてきたので、ある意味エンジニアとしてスキルアップしたと思います。笑あとは、進めていく中でReactのコーディングのトレンドを知っていき、自分がいかにレガシーな書き方をしていたかということに気づかされました、、
途中で引き返しにくかったので、諦めましたが、最低限以下くらいは知っておいた方が良いと思います。
・TypeScript > JavaScript
・関数コンポーネント > クラスコンポーネント
・Stateの管理はReduxでやるべし
記事を調べる場合は、できるだけ新しいものに触れることをオススメします。【参考記事】
5. 実際に開発したアプリ
coming soon...
- 投稿日:2021-03-14T16:01:26+09:00
StateListAnimatorで色々なアニメーションを作る
最近StateListAnimatorをよく使っているので、備忘録がてらそのプラクティスを纏めてみようと思います。
StateListAnimatorとは
ViewのStateに合わせてプロパティアニメーションを定義できるものです。
ドキュメントは以下の通りです。
- StateListAnimator例)指の押し/離しでアニメーションを変えてみる
簡単な例を実装してみます。
ボタンを押した時にViewが沈み込み、離した時に戻るようなアニメーションです。click_animator.xml<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <!-- 押した時に縮小 --> <set> <objectAnimator android:duration="150" android:propertyName="scaleX" android:valueTo="0.5f" /> <objectAnimator android:duration="150" android:propertyName="scaleY" android:valueTo="0.5f" /> </set> </item> <item android:state_pressed="false"> <!-- 離した時に戻る --> <set> <objectAnimator android:duration="150" android:propertyName="scaleX" android:valueTo="1.0f" /> <objectAnimator android:duration="150" android:propertyName="scaleY" android:valueTo="1.0f" /> </set> </item> </selector>main_fragment.xml... <!- android:stateListAnimator に指定 --> <Button android:id="@+id/animation_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/holo_blue_bright" android:stateListAnimator="@animator/click_animator" android:text="animation"/> ...stateに合わせてObjectAnimatorを指定するだけで、簡単なアニメーションができました。
複数のStateの組み合わせ
Stateは組み合わせることもできます。
分かりやすい所で、CheckBoxに適応することで、ON->OFFとOFF->ONのアニメーションを変えてみます。check_box_animator.xml<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 押した時は共通 --> <item android:state_pressed="true"> <set> <objectAnimator android:duration="150" android:propertyName="scaleX" android:valueTo="0.5f" /> <objectAnimator android:duration="150" android:propertyName="scaleY" android:valueTo="0.5f" /> </set> </item> <!-- OFF->ON --> <item android:state_checked="false" android:state_pressed="false"> <set> <objectAnimator android:duration="150" android:propertyName="scaleX" android:valueTo="1.0f" /> <objectAnimator android:duration="150" android:propertyName="scaleY" android:valueTo="1.0f" /> </set> </item> <!-- ON->OFF --> <item android:state_checked="true" android:state_pressed="false"> <set android:ordering="together"> <objectAnimator android:duration="300"> <propertyValuesHolder android:propertyName="scaleX"> <keyframe android:fraction="0" android:value="0.5f" /> <keyframe android:fraction="0.65" android:interpolator="@android:interpolator/decelerate_cubic" android:value="1.5f" /> <keyframe android:fraction="1" android:interpolator="@android:interpolator/accelerate_cubic" android:value="1.0f" /> </propertyValuesHolder> </objectAnimator> <objectAnimator android:duration="300"> <propertyValuesHolder android:propertyName="scaleY"> <keyframe android:fraction="0" android:value="0.5f" /> <keyframe android:fraction="0.65" android:interpolator="@android:interpolator/decelerate_cubic" android:value="1.5f" /> <keyframe android:fraction="1" android:interpolator="@android:interpolator/accelerate_cubic" android:value="1.0f" /> </propertyValuesHolder> </objectAnimator> </set> </item> </selector>少々stateがどちらかを把握し難いですが、OFF->ONの方がバウンドするようなアニメーションに変更できました。
使用可能なStateは決まっています。
こちらに一覧がありますので、参考にどうぞ。
- StateListViewにStateを増やしてみる
前述の例で使っている
android:state_checked
は、Checkableインターフェイスを実装したViewで参照可能なStateです。
CheckBoxはこれを実装しており、ソース上でもisChecked
等のメソッドが使えるので、そちらで把握してる人の方が多いと思います。
つまるところ、Checkableインターフェイスを実装してしまえば、あらゆるViewで前述のような組み合わせのStateが使えます。Checkableインターフェイスの実装についてはこちらの記事などが分かりやすいので、ここでは割愛します。
- http://y-anz-m.blogspot.com/2012/02/drawablestate-viewgroup.html
- https://qiita.com/muran001/items/047b0e5ba9e10f4846ae例として、FloatingActionButtonを以下のように拡張して、同様のAnimatorを適応してみました。
ExFab.ktclass ExFab @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : FloatingActionButton(context, attrs, defStyleAttr), Checkable { private var isChecked: Boolean = false init { setImageResource(R.drawable.ic_favorite_off) stateListAnimator = AnimatorInflater.loadStateListAnimator(context, R.animator.check_box_animator) } override fun isChecked(): Boolean { return isChecked } override fun toggle() { setChecked(isChecked.not()) } override fun setChecked(checked: Boolean) { if (isChecked != checked) { isChecked = checked refreshDrawableState() setImageResource(if (checked) R.drawable.ic_favorite else R.drawable.ic_favorite_off) } } // Checkable実装に必要な処理 override fun onCreateDrawableState(extraSpace: Int): IntArray? { val drawableState = super.onCreateDrawableState(extraSpace + 1) if (isChecked()) { View.mergeDrawableStates(drawableState, CHECKED_STATE_SET) } return drawableState } companion object { private val CHECKED_STATE_SET = intArrayOf(android.R.attr.state_checked) } }CheckBoxの様に動き、各Stateでアニメーションが変わるFabを実装できました。
アニメーションのカスタマイズ
アニメーション内容はobjectAnimatorの範囲でカスタマイズ可能です。
前述の例で既に使っていますが、keyframeなどを使う事により細かいアニメーションも作れます。
![]()
(これらは一例であり、この様なアニメーションが良いかと言われると微妙ではありますが…。)
ドキュメントはこちらにありますので、これを参考にカスタマイズしてみると良いでしょう。
- https://developer.android.com/guide/topics/graphics/prop-animation#declaring-xml終わりに
設計思想によるかもしれませんが、View単体で単純なアニメーションが完結できると、使い回す上でかなり便利かと思います。
- 投稿日:2021-03-14T15:12:40+09:00
showModalBottomSheetで角が丸いContainerを表示する【Flutter】
はじめに
showModalBottomSheet()というメソッドを利用することで、「下からニョキっと出てくるウインドウ」のようなものを作ることができる。
今回は、それを使って「角が丸いContainer」を表示する。
以下の画像のような感じ。上辺の両端を見ていただけたらお分かりの通り、角が丸い。
角を丸くするためにはRoundedRectangleBorderウィジェットを利用するのだが、普通に使うとContainerとの相性が悪いようである。
それについては、後で解説する。コード
では早速、コードを全文載せておく。
main.dartimport 'package:flutter/material.dart'; void main() => runApp(MaterialApp( home: MyApp(), )); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ElevatedButton( child: Text('Push'), onPressed: () { showModalBottomSheet( context: context, isScrollControlled: true, //trueにしないと、Containerのheightが反映されない shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(15)), ), builder: (BuildContext context) { return Container( height: 540, ); }); }, ), ), ); } }showModalBottomSheetのプロパティとして、isScrollControlledとshapeを指定している。
それ以外は、いたって単純。
isScrollControlledはtrueに設定しないと、Containerのheight設定が反映されないので、注意。これで、ボタンを押すと画像のようなウインドウが表示されるアプリができた。
難点
Containerの色を変更したいときはどうすればよいのだろうか。
実はContainer内でcolorプロパティを変更しても、うまくいかない。試しにContainerウィジェット内をこう書き変えてみてほしい。
return Container( height: 540, color: Colors.red, );colorプロパティにColors.redを指定して、赤色にしようとしている。
実行すると、こうなる。角が四角い!!
これはガッカリした。
どうやら角を丸くしつつ、色を変更するためには、ContainerではなくshowModalBottomSheetそのものの引数に渡さないといけないみたい。というわけで、showModalBottomSheet内をこう書き変えると、うまくいく。
showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.red, //これを追加した shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(15)), ), builder: (BuildContext context) { return Container( height: 540, ); });実行すると、こうなる。
これで一件落着!
- 投稿日:2021-03-14T02:46:35+09:00
【個人メモ】個人的にAndroidアプリ開発をするときに役立った記事一覧
はじめに
この記事は、個人的にAndroidアプリ開発をした際に役立った記事についてまとめた個人的なメモです。
ですので、その点を踏まえて閲覧していただきますようお願いいたします。Fragmentが重なる
- フラグメントコンテンツがツールバーと下部ナビゲーションビューに重なる
- Fragmentの対象となるLayoutを、widthとheightを0dpにし、constraintBottom_toTopOfやconstraintTop_toBottomOfの設定を行う
- AppBarLayoutを使う時にlayout_behaviorをFragmentに持たせるとviewが重ねて表示される
- Containerに対し、app:layout_behaviorの設定を行う
BottomNavigationViewがキーボードの上に表示されてしまう
- adjustResizeセットでキーボードの下にあるBottomNavigationViewを非表示にする方法
- BottomNavigationViewを表示しているActivityに対し、マニフェストにてandroid:windowSoftInputMode="adjustPan"の設定を行う
キーボードを隠したい
- 【InputMethodManager】Kotlinでキーボードを閉じる方法
- InputMethodManagerを用いて行う
隠れるHeader
- 要点まとめ
- 以下のコードのように、CoordinatorLayout内にHeaderとなるAppBarLayoutとコンテンツを作成
- AppBarLayout内にCollapsingToolbarLayoutを作成、この中にHeaderに表示したいコンテンツとToolbarを作成
- CollapsingToolbarLayoutのlayout_scrollFlagsによってスクロール時のHeaderの動作が変化する
- Toolbarのlayout_collapseModeによって、Toolbarを上部に表示したままにできる
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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" android:fitsSystemWindows="true" tools:context=".~~Activity"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height" android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay"> <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:toolbarId="@+id/toolbar"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" app:layout_collapseMode="parallax" app:srcCompat="@drawable/ic_baseline_chat_24"/> <de.hdodenhof.circleimageview.CircleImageView android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_anchor="@id/app_bar" android:src="@mipmap/icon_180" android:layout_marginTop="100dp" android:layout_marginLeft="10dp"/> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"> <TextView android:id="@+id/titleText" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" android:textColor="@color/white" android:text="Title"/> </androidx.appcompat.widget.Toolbar> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <include layout="@layout/content_scrolling" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
- 投稿日:2021-03-14T00:30:08+09:00
Firebaseを利用したAndroidアプリをリリースしたお話
はじめに
初めてAndroidの個人アプリをリリースしたことを契機に、Qiita始めてみました。
この記事では、以下についてお話します。
- どんなアプリをリリースしたか
- リリースまでの稼働時間は?
- Androidアプリ開発に活用したサービスやツール
- Firebaseを採用する前に知っておきたかったこと
- その他、反省点
どんな個人アプリをリリースした?
冒頭に、作ったアプリの宣伝だけさせてください...!
匿名で質問を投稿できるアンケートアプリ「Anshare」をリリースしました
ぜひ使ってみてください。そして辛辣なご意見お待ちしてます。
アプリの機能
- ユーザは、2~4択の質問を投稿できる
- 質問の投稿者は、回答の統計データを知ることができる
- 回答の各割合
- 回答したユーザの年齢層
- 回答したユーザの地域
- ユーザは、質問に対して各ユーザは1回だけ回答できる
- 回答者は、回答の各割合を知ることができる
アプリのイメージ
こんな感じの画面、挙動です!!
投稿画面 回答画面 集計画面1 集計画面2 集計画面3 利用したFirebaseプロダクト
- Authentication
- Firestore
- Functions
- Remote Config
- Cloud Messaging
- Crashlytics
- AdMob
リリースまでの稼働時間は?
2020年から着手して、リリースまで1年2ヵ月掛かりました。
土日の隙間時間でコツコツという感じです。(2020年の8月から年末まで別の開発に浮気してたりしてます(笑))
実質7~8ヵ月といったところでしょうか。
2020 2021 Androidアプリ開発に活用したサービスやツール
個人アプリ開発で活用したサービスやツールをご紹介。(githubやandroid studioに付随するツール、adb等は省きました。)
僕が知らないだけで、世の中にはもっと作業効率を向上させるアイテムがあるはず。
(色々コメントで教えていただけると嬉しいです!)
サービス / ツール 利用用途 イメージ Adobe XD UI/UXデザインツール。
僕は画面構成だけでなく、サービスアイコンも全てXDで作りました(笑)Draw.io フローチャートやシーケンス図をオンライン上で作成できるサービス。
情報整理に最適。
僕は処理フローの整理(右の画像が実際のフローの1部)やFirestoreのデータ設計等で利用しました。Firebaseを採用する前に知っておきたかったこと
Firestoreの検索機能は弱い
Firestoreをデータストアとして利用しようと考えておられる方、こちら要注意です。
例えば、以下のような条件でデータを取得することができません。
- 自分(userA)が投稿したデータでない
- 3/2以降のデータ
// 下記のようなドキュメント構成を仮定 { title: '質問' category: 'game', uid: 'testUser' createdAt: '2021/3/3 12:00:00' }// Selectしてみる mFirestore.collection("questions") .whereNotEqualTo("uid", 'userA') .whereGreaterThan("createdAt", '2021/3/2 00:00:00') .get();// エラー...。 2021-03-03 22:59:49.807 25606-25606/com.crabsan.anshare.debug W/xxx: onError, Firestore select error. java.lang.IllegalArgumentException: All where filters with an inequality (notEqualTo, notIn, lessThan, lessThanOrEqualTo, greaterThan, or greaterThanOrEqualTo) must be on the same field. But you have filters on 'uid' and 'createdAt' at com.google.firebase.firestore.Query.validateNewFilter(Query.java:561) at com.google.firebase.firestore.Query.whereHelper(Query.java:431) at com.google.firebase.firestore.Query.whereGreaterThan(Query.java:204)そうなんです。Firestoreでnot検索をする場合、複数項目を検索条件に持つことはできません
この仕様を把握できていなかったので、色々と仕様を変更して制約とするハメになりました...。
他にも制約があったりするので、採用前は念入りに確認をオススメします。複合クエリにおいて、範囲(<、<=、>、>=)と不等値(!=)の比較は、すべて同一のフィールドに対するフィルタである必要があります。
改善策は、AlgoliaやElasticSearchといった別サービスと組み合わせる必要があるそうです。※1
お金を極力かけたくなかったため、こちらは断念しました。Firestoreの読み取り回数問題
例えば、下記
answerd_users
サブコレクションに入っているドキュメント数を取得したい場合です。
ドキュメント数を取得するにはサブコレクション配下を全件取得するしか方法がなく、読み取り回数は4request
となります。1request
ではないのです。
そのため、データ増加に沿ってリニア的に読み取り回数が増えることに...対応策としては
サブコレクション自体
にカウンターを持たせて対応しました。
ドキュメント追加時にカウンターをインクリメントすることで書き込み回数は増えますが、データ数取得時は1request
だけとなるためトータルで見ると読取コストを削減することができます
修正前 修正後 利用料金の上限リミッターを設定できない
FirestoreやFunctions等は、無料枠はありますが利用した分だけ青天井でお金を支払わないといけません。
プラットフォーム側で上限を設定可能と思っていたが、出来ないのです。
課金額が閾値を超えたらアラートを送信する機能はありましたが、機能を制限してを課金を止めるリミッターはありませんでした。
Firestore宛にメールで問い合わせてみましたが、結果は同じで要約すると以下のような回答でした。
- 制限機能は無い
- Firestoreのベストプラクティスの推奨事項に従うことをオススメする
対応策として、
RemoteConfig
を利用してアプリをメンテナンスモード(起動不可制御)に変更できるような機能追加をしました。
リリース後、収入 < 支出となった場合にはこの機能を活用してサービス停止するかもしれないです。(弱気)その他の反省点
デザイン・機能を固めてから開発すべし
Adobe XDでぼんやりUIを作って、後は開発しながら考えようと始めた結果、無駄な開発、修正をすることが多かったです。
事前にガッチリ計画する時間を取った方が、必要工数は間違いなく短縮できたと思ってます。下図は当初作ったUI/UXの1部。
画像の2択アプリを想定して投稿機能まで実際に開発したのですが、予算や休日1人で運用することを考えたら厳しいと実感して断念。今リリースした内容に方向転換しました。次は失敗しないぞ
KotlinやJetpack等、Android純ライブラリをもっと活用すべきだった
JavaオンリーでRxjava、butterknife等の外部ライブラリを活用しつつ開発していたのですが、下記のようなGoogleが用意してくれているライブラリで代替できる場合が多かった。
時代に取り残されている感がすごいので、次は積極的に使っていきたい。
- Kotlin(コルーチン)
- hilt
- databinding
- viewmodel
- livedata
- lifecycle
まとめ
リリースしたアプリは全てFirebase内に完結しており、Firebaseはとても便利であることを実感。
けれども、しっかりと事前調査しておかないと僕の二の舞になるということだけ覚えていただければと思います...!
他の個人アプリを作る機会があれば、この失敗を糧にしていきたい※ これを契機にTwitter始めてみました。横の繋がりを増やしていきたいのでフォローやコメントお待ちしてますw
https://twitter.com/takashikatt参考にさせていただいたドキュメント