- 投稿日:2019-12-14T22:23:46+09:00
AndroidアプリからTVを電源ONして、しかも入力切替をしたい
この記事はAndroid Advent Calendar 2019の14日目の記事です。
はじめに
自社でAndroid端末を開発していてかつ、その端末がTVにつながるようなサービスでAndroidアプリを作っているという、ニッチ中のニッチみたいな人たちに向けた記事になります。
Android7.1.1で動作の確認をしています。実現したいこと
自社で独自のAndroid端末を製造していて、かつ、その端末がTVに接続するような端末の場合、結構な確率で下記のようなことがやりたくなります。
- Android端末からSW的にTVの電源をつけること
- Android端末からSW的にTVの入力切替でその端末の入力に切り替えること
昔々のTVと映像入力機器がコンポジット端子(黃赤白の3端子)で接続されていた頃には無理でしたが、最近のスタンダードである、HDMI端子では標準規格として上記のようなことが実現できるようになってきています。
HDMI CEC
HDMI規格には、実は、映像と音声信号以外にも信号を送ることができるように規格が作られています。その中にHDMIケーブルで接続された機器同士を連動して動作させるためのCEC(Consumer Electronics Control)信号があります。
CECという名称は一般的にはあまり表に出ることは有りませんが、この規格をベースに各社が独自拡張したものが一般的には広く認知されています。
メーカー 名称 パナソニック ビエラリンク シャープ AQUOSファミリンク ソニー ブラビアリンク 東芝 レグザリンク 三菱電機 リアリンク この規格を使用することで、TVにHDMI接続された映像機器からTVの電源ONと入力切替を実現することができます。
さらに、CEC機能を用いることで、HDMIケーブルで接続された入力機器から、TV電源のONなどの基本的な制御に始まり、音量の上げ下げや、TV側のコントロールメニューを出すなどのかなり拡張した動作までできる場合があります。
また、機器同士と言っている通り、必ずTVが受信側である必要がなく、TVから接続された映像機器等へ信号を流すことができます。
TVのリモコンでBlu-rayレコーダーが操作できたりしますが、あれはBlu-rayレコーダーがTVリモコンの赤外線受信機能持っているわけではなく、TVからリモコンの入力をCEC機能を用いて受け取るという方法で実現されています。HDMI CECを用いることで可能な挙動一覧についてはこちらのページにまとまっています。
CEC-O-MATICAndroidでのHDMI CECサポート状況
Androidが動くSTB(Set Top Box)がで始めたAndroid4.xの頃からCECを使う事ができるSTBはいくつか有りました。
しかし、この頃のAndroidでのCECサポート状況は無いと行っても良いような状況でそれぞれのメーカーが独自で実装しているような状態でした。潮目が変わったのは、Android TVが出たAndroid5からです。
まるでBluetoothみたいですね
下記の比較表のように標準のコードにHDMIをコントロールするコードが入りました。
from https://source.android.com/devices/tv/hdmi-cecこれにより、フレームワークのAPIをコールすることでCEC信号をAndroidアプリから接続した機器へ流せるようになりました。
実際に動かしてみる
前準備
今回使用するAPIはHide APIのため、そのままでは使用することができません。
そこで、アプリを動かしたいAndroidバージョンのAOSPソースコードをフルビルドすることで手に入るframework.jarが必要になります。
ビルドするなり、有志が公開しているビルド済みを貰うなりし、入手します。
実際の入手方法については割愛します。framework.jarを使えるようにする
framework.jarが手に入ったら、
app
フォルダの下にframework
フォルダを作り、その中に手に入れたframework.jarを入れます。
このまま通常のライブラリと同様にimplementationしても使うことができますが、そうしてしますと、ビルド後のApkファイルにframework.jarが含まれることになってしまいます。
ビルド方法にもよりますが、framework.jarは20M~のファイルサイズのため、Apkの肥大化やビルド時間の悪影響がひどいので、コンパイル時に参照するだけにします。
build.gradledependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) compileOnly fileTree(dir: 'framework', includes: ['*.jar']) ~ }build.gradleのdependenciesでの指定方法をcompileOnlyにすることで、ビルド時の依存関係解決はできるが生成Apkにはframework.jarを含まない形でビルドできます。
CEC Permissionの追加
アプリからCEC関係APIを使用する場合にはパーミッション宣言を必要とします。
AndroidManifest.xml
に追加します。AndroidManifest.xml<uses-permission android:name="android.permission.HDMI_CEC" />正しく準備が行えているか確認する
上記の用意が終わっている場合には、下記のクラスがアプリのソースコードから使用できる様になっているはずです。
アプリのソースコードで下記のクラスをimportし、確認します。android.hardware.hdmi.HdmiControlManagerandroid.hardware.hdmi.HdmiPlaybackClientandroid.hardware.hdmi.IHdmiControlServiceもしimportできない場合には、どこかがおかしいので再度チェックします。
よくあるのはビルドしたframework.jarがフルビルドになっておらず必要クラスが含まれていない場合なので、framework.jar内を検索するなどで確認します。jar内のクラスの検索などは
jar tf [jar file]
コマンドを使います。➜ framework git:(develop) ✗ jar tf framework.jar META-INF/ META-INF/MANIFEST.MF android/ android/permissionpresenterservice/ android/permissionpresenterservice/RuntimePermissionPresenterService$1.class ・・・ android/hardware/hdmi/IHdmiControlService$Stub$Proxy.class android/hardware/hdmi/IHdmiControlService.class android/hardware/hdmi/HdmiRecordSources$AnalogueServiceSource.class android/hardware/hdmi/HdmiControlManager$HotplugEventListener.class android/hardware/hdmi/HdmiPlaybackClient$2.class android/hardware/hdmi/HdmiTvClient$1.class android/hardware/hdmi/HdmiClient$1.class ・・・コードからHDMI CECを用いてTVの電源ONと入力切り替えを行う
AndroidアプリからTVを操作するには下記の手順を踏みます。
1.HdmiControlService
のインスタンスを取得
2. 1で取得したHdmiControlService
インスタンスを用いてHdmiControlManager
のインスタンスを取得
3. 2で取得したHdmiControlManager
インスタンスに対して、行いたい操作をインプット上記を踏まえコードからHDMI CECを用いてTVの電源ONと入力切り替えを行うコードは下記の用になります。
Sample.ktprivate fun setTvOn() { val hdmiControlService: IHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(Context.HDMI_CONTROL_SERVICE)) ?: return val hdmiControlManager = HdmiControlManager(hdmiControlService) val hdmiPlaybackClient: HdmiPlaybackClient = hdmiControlManager.playbackClient ?: return hdmiPlaybackClient.oneTouchPlay { Log.d(TAG, "success Tv On") } }上記のメソッドを呼び出すことで、AndroidアプリからTVをつけることができます。
注意点
基本的にPermissionやAPIの観点等々から、Systemアプリでの実行が基本になると思います。
このコードが含まれるAPKは、Platform署名を付けて/system/priv-app
以下にインストールするが良いと思います。
またこのコードを走らせると、何も問題ない場合TVの状態によらずTVの電源が入り入力が切り替わります。
ユーザーが意図した状態では問題有りませんが、意図せずにこのような挙動をさせるとユーザーに非常に不信感を与えるので、この機能を走らせるタイミングのUXについては慎重に検討する必要があります。下記は参考資料です
AndroidでHDMI CECを使用する際の落とし穴
Androidフレームワーク自体でサポートされたとはいえ、下記のダイアグラムを見ていただければわかるのですが、Androidフレームワーク上には抽象化されたAPIの口のみ用意されており、その下の実際のドライバ層や、それとAndroidフレームワークへのつなぎ込みはデバイスメーカーが行う必要があります。
from https://source.android.com/devices/tv/hdmi-cecこれは、カメラなどのHWを使う機能全般に言えることですね。
また、Androidに限った話ではないのですが、TV自体のHDMI CECの実装に大きな課題があります。
まず、日本以外のメーカー製TVのCECサポート率が低いです。
CECはHDMIに求められる必須機能ではなく、そこまで使用される機能でもないため、メーカーとしては不要な機能を削りコストダウンに努めるのは当然なので、仕方のないところではあります。
また、日本メーカーでも、オリオンや、DXアンテナといったメーカーのTVもサポートしていなことが多めです。逆にわざわざCEC+αの機能を作り、独自のブランド名をつけている日本メーカーにしても、時々明らかにバグっぽい挙動をすることが有り、デバッグに非常に苦労をさせられることがあります。
さらに、TV側がCECに対応している場合でもユーザーが設定でCEC連動設定をoffにしている場合も当然ながらCEC系の機能を動かすことはできません。
このCEC連動系の設定場所がTVメーカー・機種によりまちまちで、更に設定項目の名称もバラバラ、更に初期値も統一感が無いという非常にひどい状況なので、この機能をサービスの絶対必須条件にするのは辞めたほうが良いです。
正直統一規格なんだからそこら辺も統一しろよというお気持ち
- 投稿日:2019-12-14T20:03:01+09:00
Flutter でアプリ開発してみる
はじめに
個人的にスマホアプリをいくつか作りリリースしているのですが、作ったアプリを友達などに見せると、必ず言われるのが、
「iPhoneで動かないの?じゃいいや」
でした。
自分の周りでは、iPhoneを使っている率が高い気がしますが、実際のところ Android と iOSの日本でのシェア率は、ほぼ半々ぐらいのようです。そのような状況なので、モバイルアプリをリリースする場合は、Android, iOS の両対応が必須です。
ただ、Android と iOS はまったく別物なので、Native なアプリを作るとなると、工数は倍近く掛かってしまいます。
ということで、クロスプラットフォーム対応のフレームワークがいくつかあります。
たとえば、 Xamarin や PhoneGap、うちの会社なら Qt もその一つだと思います。その中で、最近注目され、実際に使われ始めているものに Flutter があります。
この記事では、その Flutter について紹介致します。Flutter って?
Flutter は、Google が、これまで蓄積してきたモバイルアプリ作成の知見を基に、新たに作ったクラスプラットフォーム対応のフレームワークです。
使用する言語は Dart です。Java に比較的近い言語なので、Java を理解していれば、容易に使えるようになるかと思います。
パフォーマンスは AndroidでもiOSのそれぞれ、Native code にコンパイルして実行するらしく、Nativeアプリに比べても遜色は無いパフォーマンスだと思います。
UI周りでは、マテリアルデザインの UI Widget が揃っているので、マテリアルデザインに沿った アプリを、Android Native で作成するよりも比較的簡単に実現できるかと思います。
Flutter の導入事例はすでに多くあり、Google だと、Google Assistant で使用されています。
https://flutter.dev/showcaseまた、個人的雑感ですが、Android アプリをJavaで開発するのは、そろそろ限界に来ていると思います。これまで、スマホの使われ方の進歩に併せて機能拡張をして来た結果の構造的欠陥があちこちに出てきていると思います。
よって、Android だけにリリースするアプリでも、Flutter で作るのが良いと感じています。開発環境
iOS アプリとして動作させる場合は、最終的に Mac が必要になりますが、開発自体は、Windows, Mac, Linux のどれでも大丈夫です。
開発環境としては、Android Studio か、VS Code を使用します。
https://flutter.dev/docs/get-started/install
に従って進めてゆけば、開発環境の構築は完了します。
もちろん全て無料で開発できます。Flutter の特徴として、HotReload というものがあります。
エミュレータや実機などで開発中のアプリを動作させておくと、ソースコードを書いて保存した瞬間に、その挙動が反映されます。
ビルドやアプリの再インストールが不要なため、結果を見ながらのコーディングが可能です。
特にUI周りの挙動などのコーディング時に役立ちます。入門
公式にサンプルやチュートリアルが豊富に用意されているので、それから始めるのがお勧めです。
またcodelabs を順に実施すると、基本的なところから学習できます。
https://flutter.dev/docs/codelabsFlutter のデメリット?
英語が必須
正式版のリリースから1年経った今でも頻繁に update されるので、公式ドキュメントを参照する場面がよく出てきます、ただ、公式ドキュメントはすべて英語なので、そこが多少の障壁になってしまうかと思います。
Dart のエラーメッセージが不親切
エラーメッセージがちょっと分かりにくい場合があります。
例えば、以下のようなソースだと、void main() { double x; double y = 0.2; double z = x + y; print ("answer = $z"); }実行時に
The method '+' was called on null. Receiver: nullのようなエラーメッセージが表示されます。
これは、x を初期化していないことが原因ですが、ちょっと分かりにくいかな?と思いました。UI Widget の挙動を変えるのが大変
実際のアプリを作成する際、用意された UI Widget を使って実装するわけですが、UI の挙動を変えたい場合など、苦労することがあります。
以前、Tar bar を使ったアプリを作ったのですが、TabView の追加、削除はサポートされていなかったので、それを実現するために、結構泥臭い実装が必要になりました。で、作ってみたアプリ
お試しでアプリを Flutter で作成し、リリースしてみました。
消費税計算機です。
https://play.google.com/store/apps/details?id=com.gmail.kazutoto.works.flutter_consumption_tax
ちなみに、Flutterで作ったので iOSでも動くのですが、Apple税を払うの辞めてしまったので、公式にリリースはしていません。
- 投稿日:2019-12-14T18:44:54+09:00
Android Code Searchを使ってみた
Android Code Searchとは
AOSP(Android Open Source Project)のソースコードを閲覧するためのサービスです。
2019/12/10のAndroid Developers Blogの記事によると、AOSPのソースを閲覧するためのサービスが公開されたようですのでさっそく使ってみました。使ってみました
URLはこちら:https://cs.android.com/
AndroidだけじゃなくAndroidXのソースも見れるようです。
BluetoothGattServer
で検索してみました。
Bluetoothのソースコードはframeworksというディレクトリ下に入っているんですね。
左上のブランチを指定すると、各Androidのバージョン毎のソースが見れます。
ソースコード間をジャンプすることもできるようです!
画面下の
History
を押すと、コミット履歴が見れます。
最初のコミットは2009年なので10年も前ですね。。。。歴史が感じられます。
アプリ開発をしているとAOSPのコードを見る機会は少ないですが、Androidのバージョン毎の挙動の変化を
知りたい時や、単純に興味本位で眺めることもあるので今後はAndroid Code Searchを活用していきたいと思います。参考
- 投稿日:2019-12-14T18:18:41+09:00
[Android Studio] Layout Editorを活用してレイアウトを作成する
本記事はand factory Advent Calendar 2019 14日目の記事です。
はじめに
はじめまして。Qiita初投稿です。
and factory株式会社でAndroidエンジニアをやっているmyoshitaと申します。本記事の内容
Android Studioを使ってレイアウトを作成する際、どうやって作っていますか。
多分、xmlファイルにコードを書いていくのが一般的なのかなと思います。
Android StudioのLayout Editorは割と使い勝手がいいので、GUIによる操作だけでレイアウトをほとんど完成させることもできます。
本記事では、個人的に便利だと思うLayout Editorの機能をご紹介したいと思います。Layout Editor
Layout Editorは、ドキュメントによると以下の通りです。
Layout Editor では、手動でレイアウト XML を記述する代わりに、UI 要素を視覚的に編集できる Design Editor にドラッグすることにより、レイアウトをすばやく作成できます。
ドキュメントはこちら
https://developer.android.com/studio/write/layout-editor?hl=JA使ってみる
使用環境はWindows10, Android Studio 3.5です。
Palette
左上にある部分がPaletteです。ここにあるViewを左下のComponent Treeか、中央のDesign Editorにドラッグアンドドロップすることで任意のViewを追加することができます。
私はレイアウトを作る最初に、デザインを見て必要になるであろうViewをPaletteから一気に移動させています。
ただし、Palette上にないアイテム(Custom Viewなど)もあるので、そういったViewを使いたい場合はxmlに直接書きます。
あと、地味な機能ですがViewを右クリックするとMaterial Guidelineに飛べるのでデザインで迷ったときには見に行ってみるのもいいと思います。
Component Tree
Paletteでも登場しましたが、Component Treeではレイアウトの階層が確認できます。
たまに使うのが、ViewのConvertです。
例えば、LinearLayoutをConstraintLayoutに変換したいときなど、右クリック-> Convert LinearLayout to ConstraintLayoutで変換することができます。
idがlinearLayoutになるので最初は変換されてないと思うかもしれませんが、ちゃんとConstraintLayoutになっています。
もちろん、ConstraintLayoutからLinearLayoutへの変換も可能ですし、TextViewからImageView、EditTextなどの相互変換も可能です。(右クリック -> Convert View... から実行できます。)
Attributes
右にあるタブで、選択したViewの属性を確認・入力することができます。
idやlayout_width, heightなどをここで設定するという方もいらっしゃるのではないかなと思います。
私は、ほぼすべての属性をAttributesで書きます。
上にある検索ボタンを押すことで任意の属性を検索することができるので、効率的に属性を入力していくことができます。
また、右端にある楕円のインジケータを押すとProjectのresourceから選択できるので、(gifだと見切れてますが)colorやdimen、styleなどを使いたい場合でも選択が簡単です。
ちなみに白く塗りつぶされた状態はresourceから選択されている状態を示します。
ConstraintLayoutとの連携
Layout Editorと非常に相性がいいのがConstraintLayoutだと思います。
ConstraintLayoutはコードで書くと、書くことが多くて大変なのですがLayout Editorを使うと直感的にレイアウトを作成できます。Constraints
Design Editorでアイテムをクリックして円形の部分を制約をつけたい箇所にドラッグアンドドロップすることでConstraintsを設定できます。
この方法も便利には便利なのですが、ひとつひとつ制約をつけていくのが面倒だと感じる場面もあります。
そこで、更に便利なのがAlignです。
Viewを選択したときDesign Editorの上にAlignというボタンが表示されるのでそれを使えば様々な制約を一気に設定することができます。
例えば、複数のViewを垂直均等に配置したい場合は、対象のViewを複数選択した状態でAlign -> Verticallyを押すと一発でConstraintsを設定してくれます。
ただしViewの位置によっては上手く働いてくれない場合もあるので、触って試してみてください。
bias
biasを加えたいときもAttributesタブから設定できます。
Constraint Widgetにあるバーを動かすことでリアルタイムにViewの位置を確認しながらbiasを設定できます。
Chain
個人的に使うことは少ないですが、chainの設定もできます。
Constraintsを設定した状態でViewを右クリック -> Cycle Chain modeで各Chainに切り替えることができます。
さいごに
Layout Editorをもっと使いたいと思っていただけたなら幸いです。
また、こんな使い方してるよ!とかもっと便利な機能があるよ!とかありましたら教えていただけると嬉しいです。
- 投稿日:2019-12-14T18:18:41+09:00
【Android Studio】Layout Editorを活用してレイアウトを作成する
本記事はand factory Advent Calendar 2019 14日目の記事です。
はじめに
はじめまして。Qiita初投稿です。
and factory株式会社でAndroidエンジニアをやっているmyoshitaと申します。本記事の内容
Android Studioを使ってレイアウトを作成する際、どうやって作っていますか。
多分、xmlファイルにコードを書いていくのが一般的なのかなと思います。
Android StudioのLayout Editorは割と使い勝手がいいので、GUIによる操作だけでレイアウトをほとんど完成させることもできます。
本記事では、個人的に便利だと思うLayout Editorの機能をご紹介したいと思います。Layout Editor
Layout Editorは、ドキュメントによると以下の通りです。
Layout Editor では、手動でレイアウト XML を記述する代わりに、UI 要素を視覚的に編集できる Design Editor にドラッグすることにより、レイアウトをすばやく作成できます。
ドキュメントはこちら
https://developer.android.com/studio/write/layout-editor?hl=JA使ってみる
使用環境はWindows10, Android Studio 3.5です。
Palette
左上にある部分がPaletteです。ここにあるViewを左下のComponent Treeか、中央のDesign Editorにドラッグアンドドロップすることで任意のViewを追加することができます。
私はレイアウトを作る最初に、デザインを見て必要になるであろうViewをPaletteから一気に移動させています。
ただし、Palette上にないアイテム(Custom Viewなど)もあるので、そういったViewを使いたい場合はxmlに直接書きます。
あと、地味な機能ですがViewを右クリックするとMaterial Guidelineに飛べるのでデザインで迷ったときには見に行ってみるのもいいと思います。
Component Tree
Paletteでも登場しましたが、Component Treeではレイアウトの階層が確認できます。
たまに使うのが、ViewのConvertです。
例えば、LinearLayoutをConstraintLayoutに変換したいときなど、右クリック-> Convert LinearLayout to ConstraintLayoutで変換することができます。
idがlinearLayoutになるので最初は変換されてないと思うかもしれませんが、ちゃんとConstraintLayoutになっています。
もちろん、ConstraintLayoutからLinearLayoutへの変換も可能ですし、TextViewからImageView、EditTextなどの相互変換も可能です。(右クリック -> Convert View... から実行できます。)
Attributes
右にあるタブで、選択したViewの属性を確認・入力することができます。
idやlayout_width, heightなどをここで設定するという方もいらっしゃるのではないかなと思います。
私は、ほぼすべての属性をAttributesで書きます。
上にある検索ボタンを押すことで任意の属性を検索することができるので、効率的に属性を入力していくことができます。
また、右端にある楕円のインジケータを押すとProjectのresourceから選択できるので、(gifだと見切れてますが)colorやdimen、styleなどを使いたい場合でも選択が簡単です。
ちなみに白く塗りつぶされた状態はresourceから選択されている状態を示します。
ConstraintLayoutとの連携
Layout Editorと非常に相性がいいのがConstraintLayoutだと思います。
ConstraintLayoutはコードで書くと、書くことが多くて大変なのですがLayout Editorを使うと直感的にレイアウトを作成できます。Constraints
Design Editorでアイテムをクリックして円形の部分を制約をつけたい箇所にドラッグアンドドロップすることでConstraintsを設定できます。
この方法も便利には便利なのですが、ひとつひとつ制約をつけていくのが面倒だと感じる場面もあります。
そこで、更に便利なのがAlignです。
Viewを選択したときDesign Editorの上にAlignというボタンが表示されるのでそれを使えば様々な制約を一気に設定することができます。
例えば、複数のViewを垂直均等に配置したい場合は、対象のViewを複数選択した状態でAlign -> Verticallyを押すと一発でConstraintsを設定してくれます。
ただしViewの位置によっては上手く働いてくれない場合もあるので、触って試してみてください。
bias
biasを加えたいときもAttributesタブから設定できます。
Constraint Widgetにあるバーを動かすことでリアルタイムにViewの位置を確認しながらbiasを設定できます。
Chain
個人的に使うことは少ないですが、chainの設定もできます。
Constraintsを設定した状態でViewを右クリック -> Cycle Chain modeで各Chainに切り替えることができます。
さいごに
Layout Editorをもっと使いたいと思っていただけたなら幸いです。
また、こんな使い方してるよ!とかもっと便利な機能があるよ!とかありましたら教えていただけると嬉しいです。
- 投稿日:2019-12-14T18:01:34+09:00
Codemagicの設定手順&ハマりそうなところをまとめてみた
はじめに
- ネイティブアプリを開発したことがない方
- Flutterを触っている、もしくは触る予定の方
へ向けた記事になります。
私自身、ネイティブアプリの開発がなく、Flutterに入門しており、リリースの時にあたふたしたため、同じような境遇の方のためになればと思い執筆しました。
Codemagic とは
Flutter専用のマネージド CI / CD サービスです。
登録から最初のビルドまで簡単に出来てしまう大変便利なサービス。Codemagic に登録
任意のリポジトリサービスを選択して簡単にSignUpすることができます。
任意のリポジトリを選択し、即ビルド
ビルドが終わると APK ファイルが生成されています。
めっちゃ簡単。。
実際にアプリをリリースビルドして自動でアップロードするには Android / iOS 共に設定が必要になります。
それぞれ分けて説明します。Android に関する設定
Keystore ファイルの作成
Keystore ファイルは Google Play に公開するアプリの署名の際に必要になります。
Keystore ファイルは紛失するとアプリのアップグレードが出来なくなりますし流出すると成りすまされてアプリを公開されてしまう可能性があるので取り扱いには注意しましょう。Ksystore生成コマンドkeytool -genkey -v -keystore keystorename.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000Keystore のパスワードと Key のパスワードの2種類入力しなければならないので覚えておいてください。のちに使用します。
Codemagic 上での Code Signing
Android Code Signing
基本的に公式のサイトにまとまっています。Keystore ファイルのアップロード
Workflow 内の Publish の項目の
Android code signing
に、作成した keystore をアップロードします。
その際に、
- keystore password
- Key alias
- Key password
も合わせて入力しておきます。
環境変数の設定
公式にもある通り、Keystore に関する情報を環境変数で与えなければいけません。
- FCI_KEYSTORE_PASSWORD = myKeystorePassword
- FCI_KEY_ALIAS = MyReleaseKey
- FCI_KEY_PASSWORD = myKeypassword
- FCI_KEYSTORE_FILE = myKeystoreEncodedByBase64
最後の
FCI_KEYSTORE_FILE
は最初何を設定するのか分かりませんでしたがどうやら Base64 に encode した Keystore 文字列を突っ込む必要があるらしい。先程の項目で Keystore アップロードしていたので必要ないと勝手に思い込んでました。ドキュメントちゃんと読まないとですね。Base64_encode$ base64 -i /path/to/keystore -o output.txtPost-clone script の追加
Post-clone script
に以下のコードを追加します。#!/usr/bin/env sh set -e # exit on first failed commandset echo $FCI_KEYSTORE_FILE | base64 --decode > $FCI_BUILD_DIR/keystore.jksPlay Console でのアプリケーション作成
Google Play の Developer 登録が済んでいない人は公式サイトから登録しましょう。
SignInしたらアプリの作成を押して新規アプリを作成します。
Service Acount の作成
Codemagic から APK ファイルを自動アップロードするために Service Account を作成する必要があります。
手順に関してはこちらのサイトが参考になるかと思います。Codemagic 上での Google Play の設定
Workflow の
Publish
の項目のGoogle Play
を開きます。Service Account の作成の項目で生成した Service Account の APIKey ( JSON ) を
Credentials
にアップロードし、APK ファイルをどの Track にアップロードするか選択します。※ 注意
App を作成した後に、一度手動で APK ファイルをアップロードする必要があります。
Android に関しては以上になります。
iOSに関する設定
iOS Code Signing
公式で手順が解説されています。
順を追って解説していきます。Apple Developer Portal と連携
User Settings
もしくはTeam Integration
にある Developer Portal の connect ボタンを押して連携します。
連携できると以下のように緑色に変わります。Apple Developer Portal で App ID ( Bundle ID ) を生成
Apple Developer Portal へアクセスし、
Account
=>Certificates, Identifiers & Profiles
の順にクリックします。
+
ボタンを押してこちらを参考に App ID を生成しますApp Store Connect でのアプリ作成
Apple Developer Portal へアクセスし、
Account
=>Certificates, Identifiers & Profiles
=>App Store Connect
の順にクリックします。左上の
+
ボタンから新規 App
を押すと以下の画面になります。
- 名前 ( アプリ名 )
- プライマリ言語 ( 第一言語選択 )
- バンドルID ( 先程作った Bundle ID )
- SKU ( アプリを識別するための ID )
を入力して作成を押します。 SKU に関しては変更ができないのでよく考えて入力する必要があります。
Codemagic 上での Code Signing
iOS は
Autmatic
という項目があり、自動で設定することができます。めっちゃ便利ですね!※ Pre-build scriptの追加 ( Option )
Build flavor を設定している場合、
Pre-build
script に xcconfig ファイルを上書きする処理を記述しなければいけません。詳しくは monoさんの記事をご覧ください。Codemagic 上での Apple Store Connect の設定
Workflow
のPublish
内にある App Store Connect を開きます。
- Apple ID ( アカウントの Email Address )
- App-spcific password (App 用パスワード )
- App ID ( App Store Connect で作成した App の ID )
App-specific password を作成するには、
Apple ID Service にアクセスします。アカウントの管理のセキュリティ
の項目があるのを確認します。
パスワードを生成...
をクリックし、任意のラベルをつけて作成します。その際に表示されるパスワードをメモしておきます。パスワードはこれ以降確認できないので注意が必要です。
また、2ファクタ認証
が有効になっていない場合は App 用パスワードを生成することができないので済ませておきましょう。App ID は App Store Connect へアクセスし、作成したアプリをクリックして確認することができます。
全般の設定
Build triggers の設定
- Trigger on push
- Trigger on pull request update
- Trigger on tag creation
の3つの Trigger を設定できます。
ワイルドカードを使って特定の Branch を対象にすることも可能です。
下の画像では、feature
かfix
で始まる Branch から PullRequest が出された時に実行するパターンです。連携の設定
Slack
Publish もしくは Team integrationに画像のような項目があるので connect を選択
Slack の integration の設定ページに飛ぶので許可する
その後、Codemagic でビルドを開始して終了すると Slack に Artifact のリンクを含んだメッセージが post されます。終わりに
Flutter 自体もとっつき易く、新しく始めるのに敷居が低いので嬉しい限りです。
Flutterを触り始めてから Swift や Kotlin でも開発してみたいなと思うようになったので挑戦しようかなと目論んでいます。
ありがとうございました!
- 投稿日:2019-12-14T15:54:43+09:00
Androidエンジニアにコンバートして学んだ、厳選Tips集
Android#2 Advent Calendar 2019 21日目の記事です
どうも、iOSエンジニア→Androidエンジニアにコンバートして丸一年が経った@orimomoです。
思えば、ある日突然Androidエンジニア(それも、社内唯一の…)になってから色々ありました。
Android Studioとはなんぞやというところから始まり、iOSとの違いに苦しみ、チョットデキルようになったと思ったらライフサイクルの罠にハマり、あれよあれよという間にAndroid10がリリースされて対応に追われ…。そんな辛くも楽しい一年を振り返り、実際にハマったり、ベテランエンジニアにレビューで指摘を受けたことを中心にTips集としてまとめてみました
これから本格的にAndroid開発を始める方にとって、少しでもお役に立てば嬉しいです。
ライフサイクルについてのTips
皆さんご存知、この図です。
「iOSのライフサイクルと同じようなものでは?」と思っていた私が躓いた点を挙げておきます。
(ここではFragmentのライフサイクルに絞って話をします)
フラグメント | Android デベロッパー | Android Developers■ライフサイクルメソッドは対になっている
onAttach()
とonDetach()
onCreate()
とonDestroy()
onCreateView()
とonDestroyView()
のように、ライフサイクルメソッドの中には対になっているものがあります。(裏を返せば対になっていないものもあるのですが、ここでは省きます)
これはつまり、
onCreate()
でした設定はonDestroy()
でキャンセルしないといけないし、
onCreateView()
で設定した値はonDestroyView()
でnullを詰めたりして破棄しないといけない、ということです。間違えて対になっていないメソッドの中で後処理したりすると、「呼ばれない」などの意図しない不具合が発生するので注意が必要です。
これに関しては下記記事がわかりやすいので一読されることをオススメします。
あの日挫折した Android 初心者へ戻るためのショートカット 〜ライフサイクル編〜 - Qiita■画面遷移でライフサイクルメソッドが呼ばれる順番に注意
画面Aと画面Bがあったとして、A→Bに遷移するとき、ライフサイクルメソッドが呼ばれる順番はどうなるでしょうか?
画面Aで全てのライフサイクルメソッドが呼ばれた後に、画面Bのライフサイクルが始まってくれると一番わかりやすいのですが、
実際は、画面Aのライフサイクルメソッドがまだ残っているうちに、画面Bのライフサイクルがスタートしてしまう場合があります。私が遭遇したのは、遷移前の画面の
onDestroyView()
よりも、遷移後の画面のonViewCreated()
のほうが先に呼ばれてしまうことにより、インスタンスが正常に破棄されないという不具合でした。
この手の不具合はコードレビューでは見つかりづらいので、ログ出力などを使い、意図したタイミングでメソッドに入ってるかを確認しながら実装するのが良いと思います。レイアウトについてのTips
■ConstraintLayoutで階層を浅くする
RelativeLayout
の中にRelativeLayout
があって、さらにその中にLinearLayout
があって…というようなネストの深いレイアウトは、ビューの描画に時間がかかるため、ユーザーに優しいレイアウトとは言えません。イマイチな例<RelativeLayout> <ImageView /> <TextView /> <RelativeLayout> <TextView /> <LinearLayout> <TextView /> <RelativeLayout> <EditText /> </RelativeLayout> </LinearLayout> </RelativeLayout> </RelativeLayout>2016年のGoogle I/Oで発表された
ConstraintLayout
を使うことでレイアウトの階層をフラットにすることができるので、可能な限りConstraintLayout
を使うなどして、ネストが深くならないように工夫できると良さそうです。
ConstraintLayout
を使うことのメリットやAndroidの描画処理については下記記事がわかりやすかったです。
Google Developers Japan: ConstraintLayout がもたらすパフォーマンスのメリットを理解する■
ConstraintLayout
内のビューでは、match_parent
ではなく0dp
を使う
ConstraintLayout
では、従来使われていたmatch_parent
が廃止されたため、代わりに「制約に合致(Match Constraints)」を意味する0dp
を指定する必要があります。悪い例<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/parent_layout" android:layout_width="match_parent" android:layout_height="wrap_content"> <!--layout_widthのmatch_parentを、0dpに変える必要がある--> <View android:id="@+id/view" android:layout_width="match_parent" android:layout_height="80dp" android:layout_marginTop="10dp" android:background="@color/white"下記の通り、公式ドキュメントにもしっかり記載されています。
ちなみにmatch_parent
を使っても動くは動くのですが、下記記事にあるように想定外の挙動をする場合があるので、素直に使わないのが吉です。
ConstraintLayoutでMATCH_PARENT利用時の想定外の挙動 - Qiita
ConstraintLayout
はiOSのAuto Layoutに似ており、雰囲気で使ってしまいがちだからこそ注意が必要だなと感じています。LiveDataについてのTips
■LiveDataをobserveするときは、
this
ではなくviewLifecycleOwner
を使う下記コードのように、FragmentでLiveDataをobserveするときに
this
を渡す(Fragment自身をLifecycleOwnerとして設定する)のにはリスクがあります。悪い例override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.liveData.observe(this, Observer { // 処理 }) }このObserverが破棄されるタイミングは、主にFragmentの
onDestroy()
が呼ばれたタイミングです。
しかし、上に貼ったライフサイクルの図を再度見てもらうと分かる通り、
FragmentのonDestroy()
が呼ばれずに、複数回onCreateView()
が呼ばれる(→Fragmentのインスタンスは生き残り、Viewだけが破棄される)ことがあり、そうすると前のObserverが破棄されずに残ってしまいます。
結果、アクティブなObseverの数がどんどん増えていくことになり、メモリリークや意図しない不具合の原因になります。それを防ぐために、FragmentにはView用のLifecycle、
viewLifecycleOwner
が用意されているので、それをLifecycleOwnerに設定してあげると良いです。良い例override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.liveData.observe(viewLifecycleOwner, Observer { // 処理 }) }これにより、FragmentのViewが破棄されると毎回LiveDataも破棄されるようになるため、複数のObserverが登録されるのを回避できます。
この問題については色々な記事が出ていますが、個人的には下記2つの記事がわかりやすかったです。
Android Architecture Componentsで犯しがちな5つの間違い【翻訳】|ふぉれ$Cooking Programmer|note
FragmentとgetViewLifecycleの話 - stsnブログおわりに
今ではAndroid開発と、スマートに書けるKotlinが大好きなので、引き続き深めていきたい気持ちです。
今回書ききれなかったTipsもまだまだあるので、また別の機会に紹介できたらと思います。来年もモバイル開発、やっていくぞ
- 投稿日:2019-12-14T15:28:12+09:00
WeChat SDKを用いたシェア機能の作り方
はじめに
この記事は、ZOZOテクノロジーズ #4AdventCalendar2019の記事です。
昨日は@inductorさんの「Kubernetesをぶち壊す10の奇妙な方法 (後編)」の記事でした。ZOZOテクノロジーズでは、他にもAdventCalenderを書いている方がいらっしゃるので、よかったらみていってください!
- ZOZOテクノロジーズ #1AdventCalendar2019
- ZOZOテクノロジーズ #2AdventCalendar2019
- ZOZOテクノロジーズ #3AdventCalendar2019
- ZOZOテクノロジーズ #5AdventCalendar2019
はじめまして、普段はAndroidエンジニアをやっている@zukkeyです。
今日は、WeChatSDKを用いたシェア機能の作り方について書いていこうと思います。
- 想定している読者
- WeChat SDKの利用が初めての方
- WeChat SDKを用いたシェア機能でできることを知りたい方
まずはじめに、導入に入る前にWeChat SDKとは何か、どういったことができるのかについて紹介していこうと思います。
WeChat SDKとは
WeChat(中国を中心に使用されているメッセージングサービス)アプリに、シェア機能、支払機能、ログイン機能などをモバイルアプリに組み込むことができるSDKです。
今回は、シェア機能を作成したのでシェア機能の紹介となります。導入準備
まず最初に、アプリケーションIDの発行を行う必要が有ります。アカウントを作成して、下記リンクよりアプリケーションIDを発行してください。
Create an application | Mobile App DevelopmentアプリケーションIDの発行が出来たら、build.gradleにて次のようにしてdependenciesを追加します。(※コードブロック中の...は省略を意味します。以後同様です。)
build.gradledependencies { ... implementation 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:5.4.0' ... }難読化を用いている場合は、proguardに次のようにルールを追加する必要があります。
proguard.pro-keep class com.tencent.mm.opensdk.** { *; } -keep class com.tencent.wxop.** { *; } -keep class com.tencent.mm.sdk.** { *; }他には、機能によってはAndroidManifestに権限を追加する必要があるので、注意してください。
導入準備に関しては、これで終わりです。次は、実際に実装してシェア機能を作成していきましょう。
シェア機能を作成する
シェア機能を作成するには次の4つの作業が必要です。
- WXEntryActivityを指定ディレクトリに作成する
- WeChatアプリに自身のアプリを登録する
- WeChatアプリからのコールバックを受け取れるようにする
- WeChatアプリにシェアをリクエストする
まずは、
WXEntryActivityを指定ディレクトリに作成する
から見ていきましょう。WXEntryActivityを指定ディレクトリに作成する
WeChatSDKを利用したシェア機能の作成を行う場合、最初に指定されたディレクトリに指定されたActivity名のクラスを作成する必要があります。
package名+wxapi
というディレクトリの配下にWXEntryActivity
という名前のActivityを作成しなくてはなりません。
コードで示すと次のようになります。
コードブロック中のhogehoge.hugahuga
はパッケージ名を示しています。WXEntryActivity.ktpackage hogehoge.hugahuga.wxapi class WXEntryActivity : AppCompatActivity() { ... }これを行わないと、後述するSDKからのコールバックをうまく受け取ることができません。
次に、インターフェースを通して
WeChatアプリに自身のアプリを登録する
処理を見ていきましょう。WeChatアプリに自身のアプリを登録する
WXEntryActivity
が作られたタイミングでWeChatアプリに登録することでSDKの機能を利用できるようになります。
実際の処理は次の通りです。WXEntryActivity.ktclass WXEntryActivity : AppCompatActivity() { lateinit var api: IWXAPI ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... setUpWeChat() } private fun setUpWeChat() { api = WXAPIFactory.createWXAPI(this, WECHAT_APP_ID, true) api.registerApp(WECHAT_APP_ID) ... } }
IWXAPI
は自身のアプリとWeChatへ通信するインターフェースです。
WeChatアプリに自分のアプリを登録したり、WeChatアプリ側にリクエストを投げたりしたいときに利用します。
setUpWeChat
の中で、WXAPIFactory.createWXAPI
でIWXAPIのインスタンスを取得します。WECHAT_APP_ID
には自身で発行したアプリケーションIDを入れてください。
api.registerApp
でアプリケーションをWeChatに登録します。次に、
WeChatアプリからのコールバックを受け取れるようにする
処理を記載します。先に後述するとした、WXEntryActivityをこのディレクトリに作成した理由になります。WeChatアプリからのコールバックを受け取れるようにする
コールバックを受け取るには、
IWXAPIEventHandler
というinterfaceが用意されているので、WXEntryActivity.kt
に持たせます。実際の処理は次のようになります。
WXEntryActivity.ktclass WXEntryActivity : AppCompatActivity(), IWXAPIEventHandler { lateinit var api: IWXAPI ... override fun onResp(response: BaseResp?) { val errorCode = (response as SendMessageToWX.Resp).errCode // errorCodeは0:成功、-1:キャンセル、-2:失敗 if (errorCode == -1 || errorCode == -2) { finish() } } override fun onReq(request: BaseReq?) = Unit override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... setUpWeChat() } private fun setUpWeChat() { ... api.handleIntent(intent, this) // registerAppの後に書く } }
IWXAPIEventHandler
は、onResp
とonReq
の二つのメソッドを持っているので、これらを実装します。
onResp
の中には、SDKからのレスポンスが返ってきます。メッセージを送った場合は、SendMessageToWX.Resp
が返ってきて、その中のerrCode
が成功、失敗、キャンセルの状態を持っています。WeChatアプリにリクエストを投げた場合に、WeChatアプリが立ち上がり、ログイン画面に遷移しますが、ここで通信が失敗したり閉じてキャンセルした場合などでonRespに流れてきます。WXEntryActivityはWeChatアプリとの仲介の役割を持っているため、キャンセルや失敗した場合はfinishさせて元の画面に戻るなどの処理を入れると良いと考えて、今回はfinishさせています。
最後に忘れてはならないのが、
setUpWeChat
の中にあるapi.handleIntent
です。
これを記載することで初めて、IWXAPIEventHandlerのonRespに流れてくるようになります。コールバックを受け取れるようになったので、次は
WeChatアプリにシェアをリクエストする
について見ていきましょう。WeChatアプリにシェアをリクエストする
WeChatでシェアすることができるものは、テキスト、画像、音楽、ビデオ、Webページ、およびアプレット(ミニプログラム)になります。
シェアするには、メッセージの内容とリクエストを作成して、IWXAPIインターフェースを通じてWeChatアプリに投げるだけで実装できます。まずはじめに、メッセージの内容を作成します。
WXEntryActivity.ktclass WXEntryActivity : AppCompatActivity(), IWXAPIEventHandler { ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... shareWeChat(bitmap, webUrl) ... } private fun shareWeChat(bitmap: Bitmap?, url: String) { val mediaMessage = WXMediaMessage( WXWebpageObject().apply { webpageUrl = url } ).apply { title = "Title" setThumbImage(bitmap) } ... } }
WXMediaMessage
は、メッセージオブジェクトでメッセージのタイトルやサムネイルだけでなく、メッセージにどのようなものを含めるかを定義する役割を持ちます。
メッセージオブジェクトの種類は、WXTextObject(テキスト)、WXImageObject(画像)、WXMusicObject(音楽)、WXVideoObject(ビデオ)、WXWebpageObject(ウェブページ)、WXMiniProgramObject(ミニプログラム)などがあります。今回のサンプルでは、ウェブページをシェアする
WXWebpageObject
を採用しています。
WXWebpageObject
のwebpageUrl
でシェアしたいウェブページのURLを設定します。WXMediaMessageのtitleに表示したいタイトルを入れて、setThumbImageでサムネイル画像を指定することができます。
次に、WeChatに送るためのリクエストを作成し、送信する部分を作成しましょう。
WXEntryActivity.ktclass WXEntryActivity : AppCompatActivity(), IWXAPIEventHandler { ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... shareWeChat(bitmap, webUrl) ... } private fun shareWeChat(bitmap: Bitmap?, url: String) { val request = SendMessageToWX.Req().apply { transaction = System.currentTimeMillis().toString() message = mediaMessage scene = SendMessageToWX.Req.WXSceneSession } api.sendReq(request) ... } }
SendMessageToWX.Req()
をインスタンス化し、WeChatアプリに送信するリクエストを作成します。
SendMessageToWX.Req()
のtransactionは、トランザクションIDと言われるもので、Respに応答するときに対応するIDを決めます。ユニークであればなんでも良いかと思います。
SendMessageToWX.Req()
のmessageには、先ほど作成したメッセージオブジェクトを入れます。リクエストにメッセージを含める役割を持ちます。
最後に、SendMessageToWX.Req()
のsceneは、WeChatアプリのどこにリクエストを送りたいかを定義します。
sceneの種類は、WXSceneSession、WXSceneTimeline、WXSceneFavoriteなどがあります。順に、ダイレクトメール(個人かグループ宛)、タイムライン(モーメント)、お気に入りです。今回は個人宛に送ろうと思って、SendMessageToWX.Req.WXSceneSession
を指定しています。リクエストを作成し終えたら、最後にIWXAPIを用いて、
api.sendReq(request)
で作成したリクエストをWeChatに送信することでWeChatアプリ側で受け取りシェアをすることが可能になります。おわりに
WeChatSDKを用いれば、WeChatアプリに対して様々な種類のシェア機能を実装することができます。
導入さえきちんと行えば簡単にできたので、ぜひ試してもらえたら嬉しいです。また、1週間後にお会いしましょう。
参考にしたリンク
- 投稿日:2019-12-14T14:32:53+09:00
Jetpack Compose基礎
FUN Advent Calendar 2019 Part1,14日目の記事です.
昨日はふぁるくんの世界料理学会 in Hakodateに参戦してきた話でした.
今年はじめてAdvent Calendarというものに参加してみました.
テーマが自由だったので,好きなことを書きます.
色々迷いましたが,某LT会での記事の続き的なことを書こうと思いました.
某LT会から結構時間が経ったのですが,最近触れていなかったのでJetpack Composet周りを軽くまとめてみようかなと思いました概要
以前記事を書いた時には,AOSP(Android Open Source Project)からダウンロードして利用するしか方法がありませんでした.
しかし,まだプレアルファ版ですが,Android Studio 4.0からライブラリとしてJetpack Composeが使用できるようになりました(プレアルファ版なので,製品版には使用しないでくださいという注意書きがあります).
そこで,Android Studio 4.0 Canary4を利用してJetpack Composeを使用していこうと思います.
ちゃんと書こうとするとModelやStateとかも出てきてしまうのですが,今回はシンプルなUI作成の紹介をしていきます.余談
Android Studio4.0 Canary4のタイトル部分(?)がかっこよくなってますね.
Android Studio3.5から背景に模様が入っていましたがここまで変えてくるとは...導入
今回は,自分で新たにプロジェクト作成して行いました.
jetpack composeを自分のプロジェクトで利用する方法はこちらの手順で行いました.
現状では,プロジェクト作成時にjetpack composeを使うか否か選べないようです.
まだプレアルファ版なので無いだけで,そのうち追加されそうですね
自分がプロジェクト作成する際に見落としているだけでした.
画像のような「Empty Compose Activity」を選んでプロジェクトを作成すればできるっぽいです.Let's try Jetpack Compose
基礎的なUIの表示方法を軽くまとめていきます.
前提
説明に入る前に...
下記のようにsetContent
の中にUI部分を書いていきます.
今回のコードは全て,setContent
の中に入れる関数として作成していきます.
※小見出しに従来のCompoment名で書いているのでご注意ください.override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { createGreetingUI() } }TextView
やっぱり初めはJetpack Composeにご挨拶しなきゃですよね(笑)
てことで,文字列の表示から行っていきます.
Text
というCompomentに,text = {表示させたい文字列}
としてあげれば文字列が表示されます.@Composable fun createGreetingUI() { greetingView("JetpackCompose") } @Composable fun greetingView(name: String) { MaterialTheme { Center { Text(text = "Hello $name!") } } }このように
Hello JetpackCompose!
と表示されます.この例では,
text
以外の設定をデフォルトから変更していません.
もちろんフォントやフォントサイズの変更もできます.
下のように,TextStyleを設定することでTextを好きなようにカスタマイズできます.fun originalTextViewStyle() = TextStyle(color = Color.Red, fontSize = 30.sp, fontFamily = FontFamily.Cursive)Text(text = "Hello $name!", style = originalTextViewStyle())この例では.フォント,フォントサイズ,フォントカラーを変更しています.
今回はFontFamilyにもともと入っているフォントを使用していますが,各自で追加したものでもできます.ImageView
次は,画像の表示を行っていきます.
画像には,DraweImageを使用し,image = {画像}
で表示させます.@Composable fun createImageUI() { MaterialTheme { Center { Container(height = 300.dp, width = 300.dp) { DrawImage(image = +imageResource(R.drawable.droidkun)) } } } }実際に表示させるとこのような感じになります.
DrawImageのみだと画像のサイズ通りに表示されてしまうため,Containerで表示させる範囲を指定しています.EditText
従来とは,だいぶCompoment名が変わりましたが,
TextField
を使用します.
value
部分は表示される文字列,onValueChange
は入力された文字列となる.
そのため,onValueChange
で+state
関数に入力された文字列に更新し,value
で+state
関数を代入することで入力された文字列が更新されます.@Composable fun createEditTextUI() { val state = +state { "" } TextField( value = state.value, onValueChange = { state.value = it }, editorStyle = EditorStyle( textStyle = TextStyle( fontSize = (20.sp) ) ) ) }このようになりました.
現状では従来のHint
はなく,下線が表示されていません.
Hint
に関しては調べてみると無理やり実装している例も見つけましたが,そのうち追加されそうなので省きます.従来通りキーボードの設定もできるので,今回は
NumberPassword
を試してみました.keyboardType = KeyboardType.NumberPasswordあれっ!?
キーボードの設定をPasswordにしたら,表示される文字は*
になるべきではないのかと思いました.
このままでは各自で実装する際に,変換する必要がありそうですが,きっと修正されますよね?AppBar
次に,AppBarを表示させていきます.
まず,既存の方法でデフォルトのActionBarを非表示にします.
下記のようにparentをTheme.AppCompat.Light.NoActionBar
に変更します.stymes.xml<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">これで,ActionBarを非表示にできたので,AppBarを追加していきます.
AppBarには,TopAppBarを使用し,icon = {アイコン}
とtitle = {タイトル}
で表示できます.@Composable fun createAppBarUI() { MaterialTheme { Column(mainAxisAlignment = MainAxisAlignment.Start) { TopAppBar( title = { Text(text = "cuntomAppBar", style = originalAppBarTextStyle()) }, color = Color(0xFF339933), navigationIcon = { VectorImageButton(R.drawable.ic_home) { /** * iconタップ処理 */ } } ) } } } fun originalAppBarTextStyle() = TextStyle(color = Color.White) @Composable fun VectorImageButton(@DrawableRes id: Int, onClick: () -> Unit) { Ripple(bounded = false) { Clickable(onClick = onClick) { VectorImage(id) } } } @Composable fun VectorImage(@DrawableRes id: Int, tint: Color = Color.Transparent) { val vector = +vectorResource(id) WithDensity { Container( width = vector.defaultWidth.toDp(), height = vector.defaultHeight.toDp() ) { DrawVector(vector, tint) } } }上記のコードではこのような表示になります.
上記コードのiconを表示させる部分では,VectorImageButtonでRippleとClickを付与し,VectorImageでVectorの範囲指定と表示を行っています.
使ってみた感じいくつか注意すべき点があるように感じました.
MaterialTheme
で囲まないとNo colors found!と怒られます.
これは,内部的には指定しているため,color指定するしないに関わらず怒られます.Column
などで場所を指定したあげないとフルスクリーンで表示されます.- ステータス部分の背景色は,
(この場合)AppTheme
のcolorPrimaryDark
が自動で設定されます.
しかし,AppBarの背景色はcolorPrimary
が設定されずデフォルトの0xFF6200EEが設定されます.
指定しない場合,colorPrimary
がAppBarの背景色に設定されればスッキリするように感じました.ListView
何か配列のものをリスト状に表示させることはよくあることです.
しかし,現状ではCompomentとして用意されていないようです.
そのため,ListVIew的なもののを作成してみます.フルーツの配列を作ってリスト状に表示させていきます.
ただColumに追加していってもスクロールできず,下の方が見えなくなるためVerticalScroller
の中に作っていきます.
さらに,Listの要素分だけListItem
を追加することで,リスト状に表示できます.
区切り線が必要な場合には,Divider
で表示させてください.@Composable fun createListUI(context: Context) { val fruitList = arrayOf("りんご", "いちご", "みかん", "なし", "パイナップル", "スイカ", "メロン", "さくらんぼ", "バナナ", "キウイ", "ぶどう", "レモン", "グレープフルーツ", "モモ", "ゆず") MaterialTheme { FlexColumn { flexible(flex = 1f) { VerticalScroller { Column { for (fruit in fruitList) { ListItem( text = fruit, onClick = { Toast.makeText(context, fruit, Toast.LENGTH_SHORT).show() } ) /** * 区切り線の表示 */ Divider(color = Color(0x44666666)) } } } } } } }今回は,文字列のみのリストですが
ListItem
では,iconなども設定できるっぽいのでもう少しちゃんとしたものも作れそうです.まとめ
この場で全てを紹介するなんてのは無理なので開発の中でよく使いそうなものの一部を紹介しました.
まだ,未完成の状態なので今後の追加次第で今回紹介したものもより応用を効かすことができそうです.Jetpack Compose自体はReact, Flutterなどを触ったことがある人ならある程度理解しやすいのかなと思います.
自分はあまり触ったことがないので慣れるまでは苦労しそうだなと思いました.
もう少し色々試してみようと思ってはいたのですが,時間がなくあまり試せませんでした.
やることが落ち着いたらPart2でも書こうかなと思います.Jetpack Compose用に作成した個人のリポジトリ
下記のリンクに今回紹介したコードを含めたアプリのレポジトリを載せました.
よかったら参考程度にでも.....
https://github.com/mattukouta/JetpackCompose参考サイト
- Use Android Studio with Jetpack Compose : https://developer.android.com/jetpack/compose/setup
- Tutorial Jetpack Compose Basics : https://developer.android.com/jetpack/compose/tutorial
- Jetpack ComposeでListViewっぽいの作ってみた : https://qiita.com/Yu-shun/items/d24514d9b196ce00830b
- compose-samples : https://github.com/android/compose-samples
- 投稿日:2019-12-14T13:45:55+09:00
ぬるぬる動いていい感じのアニメーションが簡単にできるらしいから触ってみた
Lottieという、おもしろそうな物を見つけたので色々やってみようと試行錯誤した記録です。
調べると自分が知らなかっただけなのか、結構記事があったので、あまり詳しいことは書かないと思います。Lottieとはなんぞや
- iOS、Android、Webと幅広くつかえる
- お手軽にいい感じのアニメーションがつけられる
- Apache License 2.0
- 色んな人のアニメーションが公開されているので探すとたくさんでてくる
- 自作する場合はAdobeのAfter Effectsとbodymovinという拡張機能が必要
とりあえずクリスマスも近いことだし、アニメーションでカップルを爆発させてみようかと考える午前三時
テンションでひらめいて、勢い任せにみんな大好きいらすとやで画像を集めてアニメーション作っていく(しっぱいする)
1.お好きな画像を用意してIllustratorでレイヤーを分けて保存する
2.After Effectsで保存したファイルを読みこんでアニメーションを付ける
3.bodymovinでアニメーションをjson形式で出力する
bodymovinが導入できていれば、メニューのエクステンションにbodymovinがあります
Renderを選択するとjsonが出力される。
Androidに導入していく
- gradleに追加していく
build.gradledependencies { //・・・ implementation 'com.airbnb.android:lottie:3.3.1' //・・・ }3.LottieAnimationViewを配置する
activity_main.xml<com.airbnb.lottie.LottieAnimationView android:id="@+id/animation_view" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:lottie_autoPlay="true" app:lottie_loop="true" app:lottie_rawRes="@raw/couple"/>いよいよ実行だ!!!!!
アニメーションが動かず、いろいろ調べてみると、画像を含めたjsonファイルはアニメーションしてくれないような情報を発見したところで、これ以上は時間的に諦めることに。。。
諦めてクリスマスっぽいアニメーション探して入れてみる
1.公式でいい感じのアニメーションを探してjsonファイルをダウンロードする
-> こちら をお借りしました。2.自分で作成したjsonファイルと同じようにファイルを追加する
activity_main.xml<com.airbnb.lottie.LottieAnimationView android:id="@+id/animation_view" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:lottie_autoPlay="true" app:lottie_loop="true" app:lottie_rawRes="@raw/snowman"/>実行してみると
うごいた!カワイイヤッター!!!!!!!
使ってみての感想
すごい高いクオリティのアニメーションがとってもお手軽に導入することができたので、今度はきちんとアニメーションから作ってみたいと思いました。
また、AndroidやiOSもwebでも同じアニメーションが使えるため、使い回せて便利だなと思いました。
現在は、まだ使えないアニメーションなどがありますが、今後どう進化していくのか注目して行きたいです。
- 投稿日:2019-12-14T12:46:49+09:00
Firebaseの無料枠でふぁぼを拡張した
「神絵師のツイートをふぁぼってブックマークしていたのにTwitterのAPIは3600件しか遡れない...」という悲しい気持ち、誰しも一度は抱いたことがあると思います。僕は抱きました。そこで、自前でふぁぼを保存することにしました。
概要
- Firebaseにツイートを保存する仕組みを作った。
- スマホに保存しているといつ死ぬか分からないので、Firebaseを利用してオンラインで保存することにした。
ただし、いろいろ問題が発生するので添付された画像や動画は保存しません。(直リンクは保存する)
構成
クライアントサイドは、ちょうど自前でAndroid用Twitterクライアントを作って使っていたので、そこにボタンを追加してサーバーに投げる処理を実装しました。
サーバー側は普通に借りるとお金がかかりますが、無料でも十分使えるFirebaseという神サービスがあるのでこれを使うことにしました。
- 無課金に優しい
- Cloud functions(簡単にAPIを設置できるやつ) 12.5万/月が無料
- Cloud firestore(簡単にDBを設置できるやつ) 容量1 GiB/月、数万アクセス/日が無料
- その他いっぱい無料 https://firebase.google.com/pricing?hl=ja
- Firebaseというフレームワークの中で、最小限のコーディング量でAPI、DBの設置を含む大抵のことができる
- 任意のプラットフォーム向けのSDKが充実している
神サービスです。
実装
DB
Cloud Firestoreを使いました。ボタンをポチポチするだけで定義できます。
コレクションはドキュメントの集合です。
1ツイートのデータを1ドキュメントとして保存するコレクションpics
と、保存総数をカウントするためのドキュメントを1つだけ持つコレクションinfo
を設定しました。
API
Cloud Functionsを使いました。コマンドを打つだけですぐに外部に公開できます。Cloud Firestoreへのアクセスも簡単です。
ただ保存するだけでなく、同じアカウントのツイートを何件ふぁぼっているかをクライアントへ返すようにしました。
これにより、「...こんなに素晴らしい絵を描く人なのにまだ2件しかふぁぼできていない...?」という気づきを生み、すぐに遡りを開始することができます。const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp(); const db = admin.firestore(); // エンドポイント /save を設置 exports.save = functions.https.onRequest((request, response) => { const {url,tweet_id,user_id,text,tweet_link,screen_name,status} = request.body //認証 if(request.get('Authentication')!=="*********"){ response.send("none") return } //ツイートが保存されるコレクション const pics = db.collection('pics') //保存総数をカウントするためのコレクション const statistics = db.collection('info').doc('statistics') //tweet_idで検索し、既に保存していないか確認する pics.where('tweet_id','==',tweet_id).get() .then(snapshot=>{ //同じユーザーの保存済みツイート数を確認する pics.where('user_id','==',user_id).get().then(user_pics =>{ if(snapshot.size!==0){ // 既に保存済みならその旨を「同じユーザーの保存済みツイート数」とともにメッセージとして返す response.send(`既にあります。全${user_pics.size}件`) }else{ //追加 pics.add( { url:url, tweet_id:tweet_id, tweet_id_str:status.id_str, tweet_link:tweet_link, user_id:user_id, text:text, status:status, screen_name:status.user.screen_name, timestamp: admin.firestore.FieldValue.serverTimestamp(), }) statistics.update("count",admin.firestore.FieldValue.increment(1)) // 「同じユーザーの保存済みツイート数」を返す response.send(`追加しました。${user_pics.size+1}件目`) } return; }).catch(err=>{console.log(err)}) return }).catch(err=>{console.log(err)}) });クライアント
まずふぁぼってから、上記のAPIにデータを投げ、レスポンスを表示します。
public static void save_status(Context context, account, TwitterStatus status){ // とりあえずふぁぼる account.favorite(status, true, context); // 通信用スレッドを立ち上げてサーバーに投げる Handler handler = new Handler(); new Thread(()->{ try { // データの準備 JSONObject json = new JSONObject(); json.put("url",new JSONArray(status.getMedia().getURLs())); json.put("tweet_id",status.getId()); json.put("user_id",status.getUser().getId()); json.put("text",status.getTextWithoutMediaLink()); json.put("tweet_link",status.getLink()); json.put("screen_name",status.getUser().getScreenName()); json.put("status",status.getJSON()); // /save へ送信 URL url = new URL("https://us-central1-********.cloudfunctions.net/save"); //用意したエンドポイント HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setRequestProperty("Authentication","************"); connection.setRequestProperty("Content-Type","application/json"); connection.setRequestMethod("POST"); PrintStream ps = new PrintStream(connection.getOutputStream()); ps.print(json.toString()); ps.close(); //レスポンスを表示 String response = new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine(); handler.post(()->{ Toast.makeText(context, response+"\n"+description, Toast.LENGTH_SHORT).show(); }); } catch (MalformedURLException ignored) { } catch (IOException ignored) { } catch (JSONException ignored) { } }).start(); }ウェブ
DBをの中身を一覧表示して自分で見るためのウェブサイトをCloud Hostingしましたが卒論がやばいので割愛します。
その他
Cloud Firestoreは、ドキュメントをそのフィールドを使って検索したり、並び替えたりすることができます。
以上です
- 投稿日:2019-12-14T12:30:34+09:00
Android で OpenCV のサンプルを試す
AndroidStudio で OpenCV をインポートする
の続きです。概要
Android 用 OpenCV には、
8個のサンプルが同封されている。
これを試す。
- tutorial-1-camerapreview
- tutorial-2-mixedprocessing
- tutorial-3-cameracontrol
- image-manipulations
- 15-puzzle
- color-blob-detection
- camera-calibration
- face-detection
tutorial-1-camerapreview
基本となるサンプルなので、別の記事で解説する。
Android で OpenCV のサンプル camerapreview を試す
tutorial-2-mixedprocessing
OpenCVの Java API と Native API の2つの方法で、
カメラからの画像を変換して、表示する。Native API を使うサンプルなので、別の記事で解説する。
Android で OpenCV のサンプル mixedprocessing を試す
tutorial-3-cameracontrol
OpenCV の Java API を使って、
Androidアプリでカメラを操作する方法を示す。下記の機能がある。
- カメラの Color Effect を有効にする。
- カメラのプレビュー解像度を変更する。
- カメラ画像をキャプチャしてファイルに保存する。カメラの Color Effect には、下記の選択肢がある。
- none
- mono
- negative
- solarize
- sepia
- posterize
- whiteboard
- blackboard
- aqua注意:
これらの機能は API 21以降で、廃止予定 (Deprecated) になった旧来の Camera API により実現される。サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv43Camera2 API を使って同様の機能を実現する方法は、
下記の記事を参照ください。Android の Camera2 API を使って Color Effect する
image-manipulations
複数のフィルターをサポートし、色空間の変換とヒストグラムの操作を示す。
下記の選択肢がある
- Preview RGBA
- Histograms
- Canny
キャニー ( Canny) 法によりエッジを検出して描画する
- Sobel
ソーベル ( Sobel) 法によりエッジを検出して描画する
- Sepia
- Zoom
- Pixelize
モザイク処理
- Posterize
階調変更サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv4515-puzzle
メニューは下記の2つ
- Show/hide tile numbers
- Start new game画像の分割には、Mat#submat を使用する。
タイル画像の中央に赤い文字でタイル番号。
タイル画像の境に緑の線サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv44color-blob-detection
Blobは、いくつかの共通のプロパティ(グレースケール値など)を共有する画素の集合です
このサンプルは、タッチされた場所の色と同じ色の領域を検出し、
領域に輪郭を描画する。InRange で、同じ色の領域を検出する
findContours で、輪郭を検出する
drawContours で、輪郭を描画するサンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv46camera-calibration
カメラの歪みを較正する
このサンプルは、下記を基にしている。
Camera calibration With OpenCV使い方
OpenCV非対称円グリッドパターン11x4 を使用する。
https://github.com/opencv/opencv/blob/3.4/doc/acircles_pattern.pngグリッドパターンを検出すると、ハイライト表示される。
グリッドパターンを様々な距離、角度から画面をタッチして、キャプチャーする。
メニューバーの「calibrate」ボタンを押す。
結果として、5つの歪み係数が得られる。プレビューモードは3通りある。
- Calibration グリッドパターンを検出してハイライト表示する
- Undistortion 歪みを較正した画像を表示する
- Comparison 元の画像と歪みを較正した画像を並べて表示する
サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv47face-detection
別の記事で解説する。
https://qiita.com/ohwada/items/d4cae2924aabe18cc9c4
- 投稿日:2019-12-14T11:44:33+09:00
Androidアプリ開発初心者が1ヶ月でアプリをリリースするまでにやった8つのこと
はじめに
こちらはDeNA 20 新卒 Advent Calendar 2019の18日目の記事です。
はじめまして、@yayamochiです。
10月末からAndroidアプリの開発の勉強を始めました。
Android端末をほぼ8年ぶりに触るところから始めて、
12月の半ばくらいまでにアプリを2つ開発し、1つはリリースまで行うことができました。この記事では
- どうやってAndroidアプリ開発の勉強をしたのか
- 大学院生の私が修士論文と闘いながら、どのように個人開発を継続していったのか
ということについてそれぞれ4つずつ、合わせて8つのことを紹介していきたいと思います。
対象読者?
- Androidの開発にチャレンジしてみたい人
- 個人開発が続かない人
- ある程度プログラミング経験がある人
私の事前知識?
- 研究でJavaを使っている、Kotlinはほぼ知らない
- iOSアプリの開発経験はあり
- 過去に開発したものたち
今回つくったもの?
レーダーチャートメーカー
その名の通りレーダーチャートの一覧を簡単にスマホで作れるアプリです。
こちらが今回リリースまで行ったアプリです。
来年度に引っ越しの予定があり、家を決める際に簡単に評価のメモができれば良いと思い作成しました。【使用ライブラリ】
公式のもの
- Android Architecture Components(LiveData・Room・ViewModel・Lifecycle)
- Navigation Architecture Component
- RecyclerView
- kotlinx.coroutines
依存性注入
- Koin
ログ解析
- Firebase
グラフ描画
- MPAndroidChart写真メモ帳 フォトメモ
こちらはAndroid開発経験者と2人で開発したもので、写真でメモするメモ帳です。
commit量的には3割くらいお手伝いしています。
もともとiOS版をリリースしていて、インストール数もぼちぼち増えていたのでAndroid版も作成することになりました。
現状、大体完成しており、細かいところを改善中です。【使用ライブラリ】
公式のもの
- Android Architecture Components(LiveData・Room・ViewModel・Lifecycle)
- Navigation Architecture Component
- RecyclerView
- kotlinx.coroutines
- ViewPager
- CameraX
依存性注入
- Koin
ログ解析
- Firebase
ロギング
- Timber
画像ライブラリ
- Picasso
パーミッション許可
- PermissionsDispatcher
メモリリークチェッカー
- LeakCanaryAndroid開発の勉強法?
1. Kotlin Koans でKotlinならではの書き方に触れる
私は普段からJavaを使って研究のコードを書いていたので、簡単にKotlinの文法を確認しました。
30分で覚えるKotlin文法その後はこちらのサイトでKotlinならではの書き方を勉強していました。
Kotlinの問題をWeb上で実行して確かめることができます。2. Android Kotlin Fundamentals Course で一通りの開発の流れを学ぶ
Android Kotlin Fundamentals Course
KotlinでのAndroid開発の基礎を教えてくれる無料のコースです。
全10章で、非常でボリューミーな内容です。なぜ無料なの…!
- IDEのインストール方法・便利な使い方
- 基本的なUIの作成方法
- 画面遷移の作成方法
- ライフサイクルの理解
- DB操作
などを学ぶことができます。
内容がアップデートされることもあり、個人的には本を買うよりは新しい内容が書かれているのでやりやすいと思います。
少し大変な点は、すべて英語で書かれているという点です。
しかし、丁寧なソースコードもあるので翻訳しながら読んでいけば読み進められると思います。
本当に本当にオススメのコースです!ちなみにChromeのブラウザを使っているのであれば、こちらの翻訳ツールが使いやすくて便利です。
カーソルで選択してアイコンをクリックすれば、すぐに翻訳結果をみることができます。
ImTranslator: Translator, Dictionary, TTS - Chrome Web Store私はこのコースを7章までやりました。
あとは作りたいアプリを作る!→バグにぶちあたる→解決してまた作る!のサイクルを繰り返してどんどん足りない知識を補っていきましょう。3. DroidKaigiのYouTubeやスライドをチェックしよう
AndroidのカンファレンスであるDroidKaigiのYouTubeチャンネルがあります。
DroidKaigi YouTubeチャンネル
少し難しめの内容も多いかもしれませんが、面白そうな発表がたくさんアップロードされています。
- ぼくのかんがえた最強の Usecase の作り方
- Navigation Architecture Component によるアプリ内遷移の管理
- 今日から始める依存性の注入
などの発表をみました。
今の知識では少し難しい話でも、後々知識がつながることがあるので興味があれば是非聴いてみましょう。4. Twitterやブログを購読して毎日情報収集をする
この1ヶ月の間に修士論文の審査が入っており、開発の時間がとれない日もありました。
しかし毎日技術には触れたい!ということでTwitterで情報収集のためのリストを作成し、それだけは毎日眺めるようにしていました。リスト表示やキーワード検索の表示には以下のアプリがおすすめです。
- PCならTweetDeck
- iOSアプリならBuncho
- Androidアプリならツイタマ個人的にオススメのタグは「#KotlinTips」です。
When debugging, you can modify the variable value directly: #KotlinTips pic.twitter.com/48itDVZ6zg
— Kotlin (@kotlin) December 10, 2019Kotlinの公式アカウントだけではなく、色んな人が日々情報を流してくれているので、初心者にはとても参考になる情報ばかりです。
個人開発の継続のコツ?
5. 最低限つくる機能と、あったらより良い機能をわける
これはレーダーチャートメーカーを作る際にUIの概要をざっくりメモしたイラストです。(超汚い?)
黒が最低限の機能、赤が余力があれば追加したい機能です。
最初から機能を盛り盛りにしてしまうと、先が果てしなくなり、どんどんやる気がなくなっていきます。
少し難しそうな機能の実装は最低限の機能の完成後に回しておくことで、挫折せずに開発を進めることができます。6. 開発開始日は時間が取れる日を選ぶ
最初のたたき台を時間のある日に一気に作ってしまいましょう。
私は12月1日(日)にレーダーチャートメーカーの開発を始めましたが、
その日に大まかなアーキテクチャの構成、DBの設定、UIのベース作成、できる範囲で画面遷移の作成をガッツリやってしまいました。
大変な所を先にやっておくことで、時間のない平日にも「このUIだけ作っちゃおうか〜」と思うことができるのでオススメです。7. できる限り毎日コードを書く
とても当たり前のことなのですが、結構難しいですよね…。
5分でもいいので、毎日コードを書くかそれに関わる話に触れましょう。
ベテランの方は知識が染み付いているので良いですが、
初心者の段階だと基礎が身についておらず、忘れるスピードも早くなり、余計やる気がなくなってしまいます。
なお私は一週間近くAndroidアプリの開発ができない期間があり、サッパリわすれてしまいました。8. とりあえずリリースしてみるという考えをもつ
私は今まで3本のiOSアプリを作りました。
自分では「このアプリ絶対イケる…!!!」と思っても、全然インストールされないなんてザラです。
ツラいですが現実です。
私は初めて出したiOSアプリはあんまりインストールされずに、「あんなに大変だったのに…!」とがっかりしてしまった記憶があります。笑
だったら最初に最小限の機能だけ作ってしまってリリースしちゃいましょう。
出してみないと本当にニーズがあるアプリなのかわかりません。
一度リリースしてみて、ユーザーがいることが確認できれば、アップデートするやる気も湧いてきます。まとめ
この記事では私のAndroidアプリの開発の勉強方法と、リリースするまでアプリを作り続けるコツについて紹介しました。
あまり時間がとれないない中でも、単純なアプリですが作り切ることができました。
Androidアプリ開発をしたいけど、何からしたら良いかわからない、個人開発しようとしても作り切ることができない…!という方の参考になっていただけたら嬉しいです。
- 投稿日:2019-12-14T01:57:53+09:00
星1個のアプリなんて作りたくない
「 Labomartの軌跡。構想から運用まで 」 @unotovive
↑ ゆめみ Advent Calendar 2019 の前回の記事はじめに
Android アプリ開発者のみなさん、Google I/O 2018 の下記のセッションは見ましたか?
ちなみに、私は、、、、、、見てないです
セッションでは、Play Store で星1レビューのついた原因分析を公開して
原因の42% は「アプリのクラッシュを含む安定性の問題」だったみたい。
出典: Youtube (Improve app performance and stability with Firebase (Google I/O'18))星1レビューもらいたくない
テストを書きましょう笑
よくテストを書く時間がないと聞きますが、、
たぶん仕様調整やテストの時間がないだけです笑
きちんと仕様調整やテストの時間をもらいましょう
![]()
そして、プログラムや仕様のバグを洗い出すのです
千里の道も一歩から
テストを書くことは思っているより簡単。
まずは1つテストを書いてみましょう!!
テストって?
テストの種類には下記のようなものがあって
- 単体テスト
- 結合テスト
- UI テスト
今回は単体テストに焦点を当てて話をしていきます!
Android の単体テスト
Android の単体テストには下記のようなものがあります
- Local Unit Test
- Instrumented Unit Test
何が違うん??って感じですが、簡単にいうと実機やエミュレーターが必要かどうかです。
今回は、実機やエミュレーターが必要ない Local Unit Test をやっていきます!
強い意志でテストを書こう
テストを書くのに強い意志は必要ありませんw
なんか物足りなかったので付けました笑
さて、テストを書いていこうと思いますが
せっかくなので最新の技術を使っちゃいます
※ Truth は assertThat 使うために入れてます。ただそんだけですw
準備
ここからやっとコード書いていきます〜
下記の変更をして、Sync Now しちゃえばおk
※ ExampleUnitTest.kt は要らないので削除してね
app/build.gradleandroid { ... + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { ... - testImplementation 'junit:junit:4.12' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2' + testImplementation "org.junit.jupiter:junit-jupiter-params:5.5.2" + testImplementation 'com.google.truth:truth:1.0' }テストするもの
今回は下記のような String の拡張関数作って
よくあるパスワードチェック処理をテストしていきます!
extensions/StringExtension.ktpackage {自分のパッケージ名}.extensions /** * パスワードの条件 * * 半角英小文字・大文字・数字をそれぞれ1種類以上含む * 8文字以上10文字以下 */ fun String.isPassword(): Boolean { val regex = Regex(""" ^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]{8,10}${'$'} """.trimIndent()) return matches(regex) }テスト実行!!!!
test パッケージの下に下記のファイルを作成します!
extensions/StringExtensionKtTest.ktpackage {自分のパッケージ名}.extensions import org.junit.jupiter.api.Test import com.google.common.truth.Truth.assertThat internal class StringExtensionKtTest { @Test fun isPassword() { val password = "1234abcdAB" assertThat(password.isPassword()).isTrue() } }そして、isPassword() メソッドの左にある実行マーク押して
テストを動かしてみてください!
テストが成功すると気持ちいいですよね!
※ テストが成功する度に緑のチェックマークがクセになっていきます
複数パターン一気にテストしたいよ
さっきのテストでは1つしかテストできてない。。
一気に10個とかやっちゃいたい、、どうすれば、、、
はい、そんなときのために ParameterizedTest があります
使ってみましょ〜
extensions/StringExtensionKtTest.ktpackage {自分のパッケージ名}.extensions import com.google.common.truth.Truth.assertThat import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource import java.util.stream.Stream internal class StringExtensionKtTest { @ParameterizedTest @ArgumentsSource(TestCase::class) fun isPassword(password: String, expected: Boolean) { assertThat(password.isPassword()).isEqualTo(expected) } private class TestCase : ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> = Stream.of( arguments("1234abcdAB", true), arguments("abcdAB1234", true), arguments("1234abcdab", false) ) } }はい、JUnit5 最高
これで複数ケース簡単にテストできますね♪
じゃんじゃんテストケース追加していってください
実は、、
テストが失敗するケースがあります笑
みなさん見つけられましたかね?
見つけられていない方はテストケース追加しまくってくださいね
笑
おわりに
ちゃんとテストを書いて、少しでもバグを減らして
星5のレビューたくさんもらえるようになりましょ〜
![]()
明日は @ihcamonoihS さんの記事
![]()
参考文献
- 投稿日:2019-12-14T00:45:50+09:00
【Android】ConstraintHelperの使い方
本記事はAndroid #2 Advent Calendarの22日目の記事になります。
![]()
![]()
初の参加で若干緊張しています。。。。![]()
はじめに
現在、AndroidのUI実装では
ConstraintLayout
を利用することがほとんどだと思います。
今回はConstraintLayout
の一つの、ConstraintHelper
について紹介したいと思います。なお、今回使用するバージョンは、ベータ版の
ConstraintLayout 2.0.0-beta2
を使用しているので、後ほど内容が変更になる可能性がありますので、その点はご了承ください。
また、変更がかかった際は、再度こちらの記事にも更新をかけようと考えています。
ConstraintHelper
とは複数のViewに対して同じ操作を行うためのものと認識しています。
ConstraintHelper
自体の実装はView
を拡張した抽象クラスであり、このクラスを独自に拡張することもサポートされているようですので、非常に便利です。
ConstraintHelper
へ反映させたいViewを指定するときは、app:constraint_referenced_ids
にidをコンマ区切りで格納することで指定できます。これは基本的にどのConstraintHelper
でもこの方式を用いており、共通仕様となっております。独自で拡張するまでもなく、すでに
ConstraintHelper
を拡張したクラスとして、以下が存在します。すでにご存知のものも多いと思いますが、一つずつ軽く説明します。(
VirtualLayout
は抽象クラスですので飛ばします。)
Barrier
下のように、二つの並んだViewに基準線をもうけます。これがバリアーと呼ばれる
ConstraintHelper
であり、そこに Constraint をつけることで、片方の大きさが変わっても、基準線は変動してViewが重なるということは無くなります。これは毛っここう使ったことのある人が多いのではないでしょうか。
Flow
特定のViewを指定した方向に並べます。方向としては、HorizontalとVerticalが存在し、それぞれの並び方を以下に示します。こちらは最近リリースされた機能になるので、初めての方も多いと思います。
Horizontal
Vertical
Group
まとめてViewのVisibilityを変更したい時に使用します。
GroupのVisibilityを変更すると、それに応じて、中身のViewのVisibilityも一括で変更されます。
こちらはかなり有名なので、愛用している人も多いと思います。
Layer
Viewをまとめて操作したい時に使用します。
今回はLayerを使用して特別なことはしていませんが、
app:constraint_referenced_ids
にViewを追加してみると、下のようにまとめてくれましたので、今までLayoutをネストしてUI実装してた箇所が不要になると思います。
MotionHelper
主に
MotionLayout
で使用するConstraintHelper
のようです。
今回の記事ではConstraintHelper
がメインですので、割愛します。
ConstraintHelper
の拡張上でも書いた通り、
ConstraintHelper
は拡張することを公式にサポートしています!
ここでは、実際に拡張しながら使い方を見ていきたいと思います。独自実装時に継承するメソッド(変更される可能性あり)
独自実装をする時によく継承するメソッドを洗い出しておきますが、全て継承する必要はありません。
init(attrs: AttributeSet)
- その名の通り、初期化メソッドです。
- attrsで属性の内容を取ることができます。
updatePreLayout(container: ConstraintLayout)
- 初めの
onMeasure
が呼び出される時に実行される。updatePostLayout(container: ConstraintLayout)
onLayout
が呼び出される時に実行されるupdatePostMeasure(container: ConstraintLayout)
onMeasure
が呼び出されるたびに実行される。updatePostConstraints(container: ConstraintLayout)
- constraintsに変更があるたびに呼び出されます。
実装例
今回は指定したViewの背景色を一括で変更するための
ConstraintHelper
を作りましょう。
名前はBackgroundHelper.kt
とします。
attr.xml
はトピックから外れるので隠しておきます。
attr.xml
attr.xml<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="BackgroundHelper"> <attr name="backgroundColor" format="color" /> </declare-styleable> </resources>
BackgroundHelper.ktclass BackgroundHelper @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintHelper(context, attrs, defStyleAttr) { private lateinit var backgroundColor: Color override fun init(attrs: AttributeSet?) { super.init(attrs) // 設定する背景色を取得する val array = context.obtainStyledAttributes( attrs, R.styleable.BackgroundHelper ) backgroundColor = array.getColor( R.styleable.BackgroundHelper_backgroundColor, -1 ).let { Color.valueOf(it) } array.recycle() } override fun updatePreLayout(container: ConstraintLayout?) { super.updatePreLayout(container) // はじめに`app:constraint_referenced_ids`に詰められた値をsetIdsでセットする (2.0.0-beta3からは不要) if (mReferenceIds != null) { setIds(mReferenceIds) } } override fun updatePostLayout(container: ConstraintLayout?) { // `app:constraint_referenced_ids`の個数をmCountで取得できる。 // mIdsには`app:constraint_referenced_ids`で指定したIDが配列でつめられる mIds.take(mCount).mapNotNull { id -> container?.getViewById(id) }.forEach { view -> // それぞれのViewの背景色を指定した色へ変更する view.background = backgroundColor.toDrawable() } } }
mIds.take(mCount)
の部分はtake
せずに単純にmIds
のforEach
だけでも良いのでは?と思っているのですが、他の
Group
やBarrier
でも同じ手法で実装されているので、毎回これを使っています。![]()
実行
この
BakgroundHelper
を先ほどのTextView二つに適応させると以下のようになります。(色は#D81B60
で指定しています。)
ConstraintHelper
で個人的に一番感動したポイントなのですが、このようにbackgroundColor
を変更すると、リアルタイムでその変更が適応されます。(画面左側)最後に
今回は
ConstraintHelper
というConstraintLayout
の一部について紹介しました。
ConstraintLayout
だけでは表現しきれないUIも、これを使えば実装ができるかもしれませんし、非常に将来性のある機能だと思っています。2.0.0がstableになるのが待ち遠しいですね!
![]()
余談
ConstraintLayoutの最新版として、
ConstraintLayout 2.0.0-beta3
があるのですが、ConstraintHelperがうまく動いてくれませんでしたので、しぶしぶ今回はConstraintLayout 2.0.0-beta2
を使用しています。。。
やはりベータ版だと、ところどころバグがあるので、stableになるまで待ったほうがよさそうですね参考
- 投稿日:2019-12-14T00:45:50+09:00
【Android】ConstraintLayoutの使い方
本記事はAndroid #2 Advent Calendarの22日目の記事になります。
![]()
![]()
初の参加で若干緊張しています。。。。![]()
はじめに
現在、AndroidのUI実装では
ConstraintLayout
を利用することがほとんどだと思います。
今回はConstraintLayout
の一つの、ConstraintHelper
について紹介したいと思います。なお、今回使用するバージョンは、ベータ版の
ConstraintLayout 2.0.0-beta2
を使用しているので、後ほど内容が変更になる可能性がありますので、その点はご了承ください。
また、変更がかかった際は、再度こちらの記事にも更新をかけようと考えています。
ConstraintHelper
とは複数のViewに対して同じ操作を行うためのものと認識しています。
ConstraintHelper
自体の実装はView
を拡張した抽象クラスであり、このクラスを独自に拡張することもサポートされているようですので、非常に便利です。
ConstraintHelper
へ反映させたいViewを指定するときは、app:constraint_referenced_ids
にidをコンマ区切りで格納することで指定できます。これは基本的にどのConstraintHelper
でもこの方式を用いており、共通仕様となっております。独自で拡張するまでもなく、すでに
ConstraintHelper
を拡張したクラスとして、以下が存在します。すでにご存知のものも多いと思いますが、一つずつ軽く説明します。(
VirtualLayout
は抽象クラスですので飛ばします。)
Barrier
下のように、二つの並んだViewに基準線をもうけます。これがバリアーと呼ばれる
ConstraintHelper
であり、そこに Constraint をつけることで、片方の大きさが変わっても、基準線は変動してViewが重なるということは無くなります。これは毛っここう使ったことのある人が多いのではないでしょうか。
Flow
特定のViewを指定した方向に並べます。方向としては、HorizontalとVerticalが存在し、それぞれの並び方を以下に示します。こちらは最近リリースされた機能になるので、初めての方も多いと思います。
Horizontal
Vertical
Group
まとめてViewのVisibilityを変更したい時に使用します。
GroupのVisibilityを変更すると、それに応じて、中身のViewのVisibilityも一括で変更されます。
こちらはかなり有名なので、愛用している人も多いと思います。
Layer
Viewをまとめて操作したい時に使用します。
今回はLayerを使用して特別なことはしていませんが、
app:constraint_referenced_ids
にViewを追加してみると、下のようにまとめてくれましたので、今までLayoutをネストしてUI実装してた箇所が不要になると思います。
MotionHelper
主に
MotionLayout
で使用するConstraintHelper
のようです。
今回の記事ではConstraintHelper
がメインですので、割愛します。
ConstraintHelper
の拡張上でも書いた通り、
ConstraintHelper
は拡張することを公式にサポートしています!
ここでは、実際に拡張しながら使い方を見ていきたいと思います。独自実装時に継承するメソッド(変更される可能性あり)
独自実装をする時によく継承するメソッドを洗い出しておきますが、全て継承する必要はありません。
init(attrs: AttributeSet)
- その名の通り、初期化メソッドです。
- attrsで属性の内容を取ることができます。
updatePreLayout(container: ConstraintLayout)
- 初めの
onMeasure
が呼び出される時に実行される。updatePostLayout(container: ConstraintLayout)
onLayout
が呼び出される時に実行されるupdatePostMeasure(container: ConstraintLayout)
onMeasure
が呼び出されるたびに実行される。updatePostConstraints(container: ConstraintLayout)
- constraintsに変更があるたびに呼び出されます。
実装例
今回は指定したViewの背景色を一括で変更するための
ConstraintHelper
を作りましょう。
名前はBackgroundHelper.kt
とします。
attr.xml
はトピックから外れるので隠しておきます。
attr.xml
attr.xml<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="BackgroundHelper"> <attr name="backgroundColor" format="color" /> </declare-styleable> </resources>
BackgroundHelper.ktclass BackgroundHelper @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintHelper(context, attrs, defStyleAttr) { private lateinit var backgroundColor: Color override fun init(attrs: AttributeSet?) { super.init(attrs) // 設定する背景色を取得する val array = context.obtainStyledAttributes( attrs, R.styleable.BackgroundHelper ) backgroundColor = array.getColor( R.styleable.BackgroundHelper_backgroundColor, -1 ).let { Color.valueOf(it) } array.recycle() } override fun updatePreLayout(container: ConstraintLayout?) { super.updatePreLayout(container) // はじめに`app:constraint_referenced_ids`に詰められた値をsetIdsでセットする (2.0.0-beta3からは不要) if (mReferenceIds != null) { setIds(mReferenceIds) } } override fun updatePostLayout(container: ConstraintLayout?) { // `app:constraint_referenced_ids`の個数をmCountで取得できる。 // mIdsには`app:constraint_referenced_ids`で指定したIDが配列でつめられる mIds.take(mCount).mapNotNull { id -> container?.getViewById(id) }.forEach { view -> // それぞれのViewの背景色を指定した色へ変更する view.background = backgroundColor.toDrawable() } } }
mIds.take(mCount)
の部分はtake
せずに単純にmIds
のforEach
だけでも良いのでは?と思っているのですが、他の
Group
やBarrier
でも同じ手法で実装されているので、毎回これを使っています。![]()
実行
この
BakgroundHelper
を先ほどのTextView二つに適応させると以下のようになります。(色は#D81B60
で指定しています。)
ConstraintHelper
で個人的に一番感動したポイントなのですが、このようにbackgroundColor
を変更すると、リアルタイムでその変更が適応されます。(画面左側)最後に
今回は
ConstraintHelper
というConstraintLayout
の一部について紹介しました。
ConstraintLayout
だけでは表現しきれないUIも、これを使えば実装ができるかもしれませんし、非常に将来性のある機能だと思っています。2.0.0がstableになるのが待ち遠しいですね!
![]()
余談
ConstraintLayoutの最新版として、
ConstraintLayout 2.0.0-beta3
があるのですが、ConstraintHelperがうまく動いてくれませんでしたので、しぶしぶ今回はConstraintLayout 2.0.0-beta2
を使用しています。。。
やはりベータ版だと、ところどころバグがあるので、stableになるまで待ったほうがよさそうですね参考
- 投稿日:2019-12-14T00:19:51+09:00
Flutter Interact 2019のまとめ
はじめに
この記事はFlutter #2 Advent Calendar 2019の11日目分です。
今年もすぐに枠が埋まってしまっていたのでFlutter Advent Calendarに参加する予定はなかったのですが、ふとカレンダーを覗いていみると11日枠が投稿されずに空いていたのでちょうどFlutter Interactが開催されたので急遽この記事を書くことにしました。
Flutterの最新情報に興味がある方はFlutterウィークリーもよかったらご覧ください
https://qiita.com/tags/flutterweeklyFlutter Interact 2019とは
https://developers.google.com/events/flutter-interact
Flutter Interact is a day dedicated to creation and collaboration with the world, inspired by the makers of brilliant experiences.
一言でいうとGoogle公式のFlutter年次イベントです。12月11日にニューヨークのブルックリンで開催されました。昨年はFlutter Live 2018が同じ時期に開催されました。来年はまた名称が変わっているかもしれませんね。
セッション一覧
Vision Keynote (Flutter Interact '19)
https://youtu.be/NfNdXgJZfFo
ビジョンキーノートを努めたのはMatias Duarte、以前はPalmのVPで、2010にGoogleに入社しAndroid User Experienceのディレクターをやっていて最初にリリースしたのはHoneycombと呼ばれるAndroid 3.0のデザインになります。次にMaterial Designを手掛けたという輝かしい経歴の彼が、現在はFlutterに取り組んでいます。GoogleがFlutterにかける意気込みもこの辺りから伝わってきますね。マテリアルデザインを世界中のスクリーンに広めるためにFlutterに取り組んでいると言っても良さそうです。キーノートでも言及されていますが、マテリアルデザインコンポーネントは専門のエンジニアがFlutterチームにいて新しいコンポーネントのドキュメントが出来るよりも先にFlutterで実装されていることも多いそうです。Google FontsもFlutterから簡単に使えるようになりました。
https://pub.dev/packages/google_fonts
Gogole Fontsを使えば.ttf
ファイルをアセットに組み込むことなく自由に1000ものフォントを利用することが出来ます。使い方は
Text( 'This is Google Fonts', style: GoogleFonts.lato(), ),や
Text( 'This is Google Fonts', style: GoogleFonts.lato( textStyle: TextStyle(color: Colors.blue, letterSpacing: .5), ), ),のように指定するだけでGoogle Fontsを利用可能です。まだバージョン0.2.0のベータ版ですが利用することが出来ます。
Very Early Experimental
アダプティブUIもシングルコードベースで実現できるようになりました。Flutter Webやマルチサイズのスクリーンに簡単に対応できるようにするには必要な機能ですね
開発中のレイアウトコードエディタ。レイアウトの細かな調整もプレビューで確認しながらデザイナーが調整しそのままコードを出力できるようになります。
Product Keynote (Flutter Interact '19)
a portable UI framework for an ambient computing worldFlutterはモバイルアプリのクロスプラットフォームとして知られていますが、様々なスクリーンが増え続ける中すべてのスクリーン向けのアプリを開発できるようにすることを目指してFlutterの開発が続いています。
Flutter 1.12リリース
ダークモードのiOS対応
新しいCupertinoウィジェット
Android Xがデフォルト
既存アプリへの追加
Googleフォント
Flutterギャラリー
StadiaをFlutterで開発
eBayがFlutterでアプリを作り直した
https://play.google.com/store/apps/details?id=com.ebay.mobile
Flutter on Desktop
Flutter on the Web
Dart
Dartpad
https://dartpad.dev/
Web上のPlaygroundでFlutterも動くようになりましたCodelabs
https://flutter.dev/docs/codelabs
コードラボもそのままFlutterの動作を確認できるようになりましたFlutter outline Editor
レイアウトエクスプローラー
Adobe XD
キーノート以外のセッション
Welcome to the Sparkle Party (Flutter Interact '19)
https://youtu.be/1AxXF038-lYMaterial Theming with Flutter (Flutter Interact '19)
https://youtu.be/stoJpMeS5aYDesigning for the Web with Flutter (Flutter Interact '19)
https://youtu.be/tot-R_q5-0oBuilding in Accessibility with Flutter (Flutter Interact '19)
https://youtu.be/bWbBgbmAdQsDesign and Build Clock Displays with Flutter (Flutter Interact '19)
https://youtu.be/i60HG1TtKJoUse Rive and Flutter for dynamic, interactive, & animated experiences (Flutter Interact '19)
https://youtu.be/6QZy5sYozVI