20191214のAndroidに関する記事は17件です。

AndroidアプリからTVを電源ONして、しかも入力切替をしたい

この記事はAndroid Advent Calendar 2019の14日目の記事です。

はじめに

自社でAndroid端末を開発していてかつ、その端末がTVにつながるようなサービスでAndroidアプリを作っているという、ニッチ中のニッチみたいな人たちに向けた記事になります。
Android7.1.1で動作の確認をしています。

実現したいこと

自社で独自のAndroid端末を製造していて、かつ、その端末がTVに接続するような端末の場合、結構な確率で下記のようなことがやりたくなります。

  1. Android端末からSW的にTVの電源をつけること
  2. 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-MATIC

AndroidでのHDMI CECサポート状況

Androidが動くSTB(Set Top Box)がで始めたAndroid4.xの頃からCECを使う事ができるSTBはいくつか有りました。
しかし、この頃のAndroidでのCECサポート状況は無いと行っても良いような状況でそれぞれのメーカーが独自で実装しているような状態でした。

潮目が変わったのは、Android TVが出たAndroid5からです。まるでBluetoothみたいですね
下記の比較表のように標準のコードにHDMIをコントロールするコードが入りました。
HDMI_Control_Service.png
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を入れます。
スクリーンショット 2019-12-14 21.38.59.png

このまま通常のライブラリと同様にimplementationしても使うことができますが、そうしてしますと、ビルド後のApkファイルにframework.jarが含まれることになってしまいます。

ビルド方法にもよりますが、framework.jarは20M~のファイルサイズのため、Apkの肥大化やビルド時間の悪影響がひどいので、コンパイル時に参照するだけにします。

build.gradle
dependencies {

    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.HdmiControlManager
android.hardware.hdmi.HdmiPlaybackClient
android.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.kt
    private 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フレームワークへのつなぎ込みはデバイスメーカーが行う必要があります。

HDMI_Control_Service_Flow.png
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メーカー・機種によりまちまちで、更に設定項目の名称もバラバラ、更に初期値も統一感が無いという非常にひどい状況なので、この機能をサービスの絶対必須条件にするのは辞めたほうが良いです。
正直統一規格なんだからそこら辺も統一しろよというお気持ち

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter でアプリ開発してみる

はじめに

個人的にスマホアプリをいくつか作りリリースしているのですが、作ったアプリを友達などに見せると、必ず言われるのが、
「iPhoneで動かないの?じゃいいや」
でした。
自分の周りでは、iPhoneを使っている率が高い気がしますが、実際のところ Android と iOSの日本でのシェア率は、ほぼ半々ぐらいのようです。

そのような状況なので、モバイルアプリをリリースする場合は、Android, iOS の両対応が必須です。

ただ、Android と iOS はまったく別物なので、Native なアプリを作るとなると、工数は倍近く掛かってしまいます。

ということで、クロスプラットフォーム対応のフレームワークがいくつかあります。
たとえば、 Xamarin や PhoneGap、うちの会社なら Qt もその一つだと思います。

その中で、最近注目され、実際に使われ始めているものに Flutter があります。
この記事では、その Flutter について紹介致します。

Flutter って?

flutter1.png

Flutter は、Google が、これまで蓄積してきたモバイルアプリ作成の知見を基に、新たに作ったクラスプラットフォーム対応のフレームワークです。

使用する言語は Dart です。Java に比較的近い言語なので、Java を理解していれば、容易に使えるようになるかと思います。

パフォーマンスは AndroidでもiOSのそれぞれ、Native code にコンパイルして実行するらしく、Nativeアプリに比べても遜色は無いパフォーマンスだと思います。

UI周りでは、マテリアルデザインの UI Widget が揃っているので、マテリアルデザインに沿った アプリを、Android Native で作成するよりも比較的簡単に実現できるかと思います。
flutter.png

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/codelabs

Flutter のデメリット?

英語が必須

正式版のリリースから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
calc.png

ちなみに、Flutterで作ったので iOSでも動くのですが、Apple税を払うの辞めてしまったので、公式にリリースはしていません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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のソースも見れるようです。
スクリーンショット 2019-12-14 18.22.04.png

BluetoothGattServerで検索してみました。
Bluetoothのソースコードはframeworksというディレクトリ下に入っているんですね。
スクリーンショット 2019-12-14 18.25.39.png

左上のブランチを指定すると、各Androidのバージョン毎のソースが見れます。
ソースコード間をジャンプすることもできるようです!
スクリーンショット 2019-12-14 18.26.22.png

画面下のHistory を押すと、コミット履歴が見れます。
最初のコミットは2009年なので10年も前ですね。。。。歴史が感じられます。
スクリーンショット 2019-12-14 18.32.02.png

アプリ開発をしているとAOSPのコードを見る機会は少ないですが、Androidのバージョン毎の挙動の変化を
知りたい時や、単純に興味本位で眺めることもあるので今後はAndroid Code Searchを活用していきたいと思います。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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に飛べるのでデザインで迷ったときには見に行ってみるのもいいと思います。
1.gif

Component Tree

Paletteでも登場しましたが、Component Treeではレイアウトの階層が確認できます。
たまに使うのが、ViewのConvertです。
例えば、LinearLayoutをConstraintLayoutに変換したいときなど、右クリック-> Convert LinearLayout to ConstraintLayoutで変換することができます。
idがlinearLayoutになるので最初は変換されてないと思うかもしれませんが、ちゃんとConstraintLayoutになっています。
もちろん、ConstraintLayoutからLinearLayoutへの変換も可能ですし、TextViewからImageView、EditTextなどの相互変換も可能です。(右クリック -> Convert View... から実行できます。)
2.gif

Attributes

右にあるタブで、選択したViewの属性を確認・入力することができます。
idやlayout_width, heightなどをここで設定するという方もいらっしゃるのではないかなと思います。
私は、ほぼすべての属性をAttributesで書きます。
上にある検索ボタンを押すことで任意の属性を検索することができるので、効率的に属性を入力していくことができます。
また、右端にある楕円のインジケータを押すとProjectのresourceから選択できるので、(gifだと見切れてますが)colorやdimen、styleなどを使いたい場合でも選択が簡単です。
ちなみに白く塗りつぶされた状態はresourceから選択されている状態を示します。
3.gif

ConstraintLayoutとの連携

Layout Editorと非常に相性がいいのがConstraintLayoutだと思います。
ConstraintLayoutはコードで書くと、書くことが多くて大変なのですがLayout Editorを使うと直感的にレイアウトを作成できます。

Constraints

Design Editorでアイテムをクリックして円形の部分を制約をつけたい箇所にドラッグアンドドロップすることでConstraintsを設定できます。
この方法も便利には便利なのですが、ひとつひとつ制約をつけていくのが面倒だと感じる場面もあります。
4.gif

そこで、更に便利なのがAlignです。
Viewを選択したときDesign Editorの上にAlignというボタンが表示されるのでそれを使えば様々な制約を一気に設定することができます。
例えば、複数のViewを垂直均等に配置したい場合は、対象のViewを複数選択した状態でAlign -> Verticallyを押すと一発でConstraintsを設定してくれます。
ただしViewの位置によっては上手く働いてくれない場合もあるので、触って試してみてください。
5.gif

bias

biasを加えたいときもAttributesタブから設定できます。
Constraint Widgetにあるバーを動かすことでリアルタイムにViewの位置を確認しながらbiasを設定できます。
6.gif

Chain

個人的に使うことは少ないですが、chainの設定もできます。
Constraintsを設定した状態でViewを右クリック -> Cycle Chain modeで各Chainに切り替えることができます。
7.gif

さいごに

Layout Editorをもっと使いたいと思っていただけたなら幸いです。
また、こんな使い方してるよ!とかもっと便利な機能があるよ!とかありましたら教えていただけると嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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に飛べるのでデザインで迷ったときには見に行ってみるのもいいと思います。
1.gif

Component Tree

Paletteでも登場しましたが、Component Treeではレイアウトの階層が確認できます。
たまに使うのが、ViewのConvertです。
例えば、LinearLayoutをConstraintLayoutに変換したいときなど、右クリック-> Convert LinearLayout to ConstraintLayoutで変換することができます。
idがlinearLayoutになるので最初は変換されてないと思うかもしれませんが、ちゃんとConstraintLayoutになっています。
もちろん、ConstraintLayoutからLinearLayoutへの変換も可能ですし、TextViewからImageView、EditTextなどの相互変換も可能です。(右クリック -> Convert View... から実行できます。)
2.gif

Attributes

右にあるタブで、選択したViewの属性を確認・入力することができます。
idやlayout_width, heightなどをここで設定するという方もいらっしゃるのではないかなと思います。
私は、ほぼすべての属性をAttributesで書きます。
上にある検索ボタンを押すことで任意の属性を検索することができるので、効率的に属性を入力していくことができます。
また、右端にある楕円のインジケータを押すとProjectのresourceから選択できるので、(gifだと見切れてますが)colorやdimen、styleなどを使いたい場合でも選択が簡単です。
ちなみに白く塗りつぶされた状態はresourceから選択されている状態を示します。
3.gif

ConstraintLayoutとの連携

Layout Editorと非常に相性がいいのがConstraintLayoutだと思います。
ConstraintLayoutはコードで書くと、書くことが多くて大変なのですがLayout Editorを使うと直感的にレイアウトを作成できます。

Constraints

Design Editorでアイテムをクリックして円形の部分を制約をつけたい箇所にドラッグアンドドロップすることでConstraintsを設定できます。
この方法も便利には便利なのですが、ひとつひとつ制約をつけていくのが面倒だと感じる場面もあります。
4.gif

そこで、更に便利なのがAlignです。
Viewを選択したときDesign Editorの上にAlignというボタンが表示されるのでそれを使えば様々な制約を一気に設定することができます。
例えば、複数のViewを垂直均等に配置したい場合は、対象のViewを複数選択した状態でAlign -> Verticallyを押すと一発でConstraintsを設定してくれます。
ただしViewの位置によっては上手く働いてくれない場合もあるので、触って試してみてください。
5.gif

bias

biasを加えたいときもAttributesタブから設定できます。
Constraint Widgetにあるバーを動かすことでリアルタイムにViewの位置を確認しながらbiasを設定できます。
6.gif

Chain

個人的に使うことは少ないですが、chainの設定もできます。
Constraintsを設定した状態でViewを右クリック -> Cycle Chain modeで各Chainに切り替えることができます。
7.gif

さいごに

Layout Editorをもっと使いたいと思っていただけたなら幸いです。
また、こんな使い方してるよ!とかもっと便利な機能があるよ!とかありましたら教えていただけると嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Codemagicの設定手順&ハマりそうなところをまとめてみた

はじめに

  • ネイティブアプリを開発したことがない方
  • Flutterを触っている、もしくは触る予定の方

へ向けた記事になります。

私自身、ネイティブアプリの開発がなく、Flutterに入門しており、リリースの時にあたふたしたため、同じような境遇の方のためになればと思い執筆しました。

Codemagic とは

Newsletter-second.jpg

Flutter専用のマネージド CI / CD サービスです。
登録から最初のビルドまで簡単に出来てしまう大変便利なサービス。

Codemagic に登録

Codemagic - SignUp

任意のリポジトリサービスを選択して簡単にSignUpすることができます。

スクリーンショット 2019-12-14 15.09.31.png

任意のリポジトリを選択し、即ビルド

codemagic-device-farm-import-1024x474-2.png

ビルドが終わると APK ファイルが生成されています。

1_6E9A_JrjZCXL0Own0_AyFQ.png

めっちゃ簡単。。
実際にアプリをリリースビルドして自動でアップロードするには Android / iOS 共に設定が必要になります。
それぞれ分けて説明します。

Android に関する設定

Keystore ファイルの作成

Keystore ファイルは Google Play に公開するアプリの署名の際に必要になります。
Keystore ファイルは紛失するとアプリのアップグレードが出来なくなりますし流出すると成りすまされてアプリを公開されてしまう可能性があるので取り扱いには注意しましょう。

Ksystore生成コマンド
keytool -genkey -v -keystore keystorename.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Keystore のパスワードと Key のパスワードの2種類入力しなければならないので覚えておいてください。のちに使用します。

Codemagic 上での Code Signing

Android Code Signing
基本的に公式のサイトにまとまっています。

Keystore ファイルのアップロード

Workflow 内の Publish の項目の Android code signing に、作成した keystore をアップロードします。
その際に、

  • keystore password
  • Key alias
  • Key password

も合わせて入力しておきます。

code signing.png

環境変数の設定

公式にもある通り、Keystore に関する情報を環境変数で与えなければいけません。

  • FCI_KEYSTORE_PASSWORD = myKeystorePassword
  • FCI_KEY_ALIAS = MyReleaseKey
  • FCI_KEY_PASSWORD = myKeypassword
  • FCI_KEYSTORE_FILE = myKeystoreEncodedByBase64

スクリーンショット 2019-12-14 14.16.12.png

最後の FCI_KEYSTORE_FILE は最初何を設定するのか分かりませんでしたがどうやら Base64 に encode した Keystore 文字列を突っ込む必要があるらしい。先程の項目で Keystore アップロードしていたので必要ないと勝手に思い込んでました。ドキュメントちゃんと読まないとですね。

Base64_encode
$ base64 -i /path/to/keystore -o output.txt

Post-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.jks

Play Console でのアプリケーション作成

Google Play の Developer 登録が済んでいない人は公式サイトから登録しましょう。

SignInしたらアプリの作成を押して新規アプリを作成します。

スクリーンショット 2019-12-14 14.38.56.png

Service Acount の作成

Codemagic から APK ファイルを自動アップロードするために Service Account を作成する必要があります。
手順に関してはこちらのサイトが参考になるかと思います。

Codemagic 上での Google Play の設定

Workflow の Publish の項目の Google Play を開きます。

スクリーンショット 2019-12-14 14.52.38.png

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 ボタンを押して連携します。
連携できると以下のように緑色に変わります。

スクリーンショット 2019-12-14 16.48.48.png

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 を押すと以下の画面になります。

スクリーンショット 2019-12-14 17.20.33.png

  • 名前 ( アプリ名 )
  • プライマリ言語 ( 第一言語選択 )
  • バンドル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 の設定

WorkflowPublish 内にある App Store Connect を開きます。

  • Apple ID ( アカウントの Email Address )
  • App-spcific password (App 用パスワード )
  • App ID ( App Store Connect で作成した App の ID )

スクリーンショット 2019-12-14 17.28.20.png

App-specific password を作成するには、
Apple ID Service にアクセスします。アカウントの管理の セキュリティ の項目があるのを確認します。

スクリーンショット 2019-12-14 17.35.49.png

パスワードを生成... をクリックし、任意のラベルをつけて作成します。その際に表示されるパスワードをメモしておきます。パスワードはこれ以降確認できないので注意が必要です。
また、2ファクタ認証 が有効になっていない場合は App 用パスワードを生成することができないので済ませておきましょう。

App ID は App Store Connect へアクセスし、作成したアプリをクリックして確認することができます。

スクリーンショット 2019-12-14 17.43.47.png

全般の設定

Build triggers の設定

  • Trigger on push
  • Trigger on pull request update
  • Trigger on tag creation

の3つの Trigger を設定できます。
ワイルドカードを使って特定の Branch を対象にすることも可能です。
下の画像では、 featurefix で始まる Branch から PullRequest が出された時に実行するパターンです。

スクリーンショット 2019-12-14 17.52.48.png

連携の設定

Slack

Publish もしくは Team integrationに画像のような項目があるので connect を選択

スクリーンショット 2019-12-14 13.47.16.png

Slack の integration の設定ページに飛ぶので許可する
その後、Codemagic でビルドを開始して終了すると Slack に Artifact のリンクを含んだメッセージが post されます。

スクリーンショット 2019-12-14 13.41.07.png

終わりに

Flutter 自体もとっつき易く、新しく始めるのに敷居が低いので嬉しい限りです。
Flutterを触り始めてから Swift や Kotlin でも開発してみたいなと思うようになったので挑戦しようかなと目論んでいます。
ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidエンジニアにコンバートして学んだ、厳選Tips集

Android#2 Advent Calendar 2019 21日目の記事です:christmas_tree:

どうも、iOSエンジニア→Androidエンジニアにコンバートして丸一年が経った@orimomoです。

思えば、ある日突然Androidエンジニア(それも、社内唯一の…)になってから色々ありました。
Android Studioとはなんぞやというところから始まり、iOSとの違いに苦しみ、チョットデキルようになったと思ったらライフサイクルの罠にハマり、あれよあれよという間にAndroid10がリリースされて対応に追われ…。

そんな辛くも楽しい一年を振り返り、実際にハマったり、ベテランエンジニアにレビューで指摘を受けたことを中心にTips集としてまとめてみました:slight_smile:

これから本格的にAndroid開発を始める方にとって、少しでもお役に立てば嬉しいです。

ライフサイクルについてのTips

皆さんご存知、この図です。
「iOSのライフサイクルと同じようなものでは?」と思っていた私が躓いた点を挙げておきます。
(ここではFragmentのライフサイクルに絞って話をします)
fragment_lifecycle.png
フラグメント  |  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"

下記の通り、公式ドキュメントにもしっかり記載されています。
スクリーンショット 2019-12-15 12.17.18.png
ちなみに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もまだまだあるので、また別の機会に紹介できたらと思います。

来年もモバイル開発、やっていくぞ:star2:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WeChat SDKを用いたシェア機能の作り方

はじめに

この記事は、ZOZOテクノロジーズ #4AdventCalendar2019の記事です。
昨日は@inductorさんの「Kubernetesをぶち壊す10の奇妙な方法 (後編)」の記事でした。

ZOZOテクノロジーズでは、他にもAdventCalenderを書いている方がいらっしゃるので、よかったらみていってください!

はじめまして、普段は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.gradle
dependencies {
  ...
  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.kt
package hogehoge.hugahuga.wxapi

class WXEntryActivity : AppCompatActivity() {
  ...
}

これを行わないと、後述するSDKからのコールバックをうまく受け取ることができません。

次に、インターフェースを通してWeChatアプリに自身のアプリを登録する処理を見ていきましょう。

WeChatアプリに自身のアプリを登録する

WXEntryActivity が作られたタイミングでWeChatアプリに登録することでSDKの機能を利用できるようになります。
実際の処理は次の通りです。

WXEntryActivity.kt
class 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.kt
class 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は、onResponReqの二つのメソッドを持っているので、これらを実装します。
onRespの中には、SDKからのレスポンスが返ってきます。メッセージを送った場合は、 SendMessageToWX.Respが返ってきて、その中のerrCodeが成功、失敗、キャンセルの状態を持っています。

WeChatアプリにリクエストを投げた場合に、WeChatアプリが立ち上がり、ログイン画面に遷移しますが、ここで通信が失敗したり閉じてキャンセルした場合などでonRespに流れてきます。WXEntryActivityはWeChatアプリとの仲介の役割を持っているため、キャンセルや失敗した場合はfinishさせて元の画面に戻るなどの処理を入れると良いと考えて、今回はfinishさせています。

最後に忘れてはならないのが、setUpWeChatの中にある api.handleIntentです。
これを記載することで初めて、IWXAPIEventHandlerのonRespに流れてくるようになります。

コールバックを受け取れるようになったので、次はWeChatアプリにシェアをリクエストするについて見ていきましょう。

WeChatアプリにシェアをリクエストする

WeChatでシェアすることができるものは、テキスト、画像、音楽、ビデオ、Webページ、およびアプレット(ミニプログラム)になります。
シェアするには、メッセージの内容とリクエストを作成して、IWXAPIインターフェースを通じてWeChatアプリに投げるだけで実装できます。

まずはじめに、メッセージの内容を作成します。

WXEntryActivity.kt
class 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を採用しています。
WXWebpageObjectwebpageUrlでシェアしたいウェブページのURLを設定します。

WXMediaMessageのtitleに表示したいタイトルを入れて、setThumbImageでサムネイル画像を指定することができます。

次に、WeChatに送るためのリクエストを作成し、送信する部分を作成しましょう。

WXEntryActivity.kt
class 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週間後にお会いしましょう。

参考にしたリンク

WeChat Open Platform
Resource Center | Weixin

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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」を選んでプロジェクトを作成すればできるっぽいです.

スクリーンショット 2019-12-13 13.39.25.png

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などで場所を指定したあげないとフルスクリーンで表示されます.
  • ステータス部分の背景色は,(この場合)AppThemecolorPrimaryDarkが自動で設定されます.
    しかし,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

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ぬるぬる動いていい感じのアニメーションが簡単にできるらしいから触ってみた

Lottieという、おもしろそうな物を見つけたので色々やってみようと試行錯誤した記録です。
調べると自分が知らなかっただけなのか、結構記事があったので、あまり詳しいことは書かないと思います。

Lottieとはなんぞや

  • iOS、Android、Webと幅広くつかえる
  • お手軽にいい感じのアニメーションがつけられる
  • Apache License 2.0
  • 色んな人のアニメーションが公開されているので探すとたくさんでてくる
  • 自作する場合はAdobeのAfter Effectsとbodymovinという拡張機能が必要

とりあえずクリスマスも近いことだし、アニメーションでカップルを爆発させてみようかと考える午前三時

テンションでひらめいて、勢い任せにみんな大好きいらすとやで画像を集めてアニメーション作っていく(しっぱいする)

1.お好きな画像を用意してIllustratorでレイヤーを分けて保存する

スクリーンショット 2019-12-14 9.09.26.png

2.After Effectsで保存したファイルを読みこんでアニメーションを付ける

スクリーンショット 2019-12-14 10.54.45.png

3.bodymovinでアニメーションをjson形式で出力する

bodymovinが導入できていれば、メニューのエクステンションにbodymovinがあります
スクリーンショット 2019-12-14 10.56.02.png

Renderを選択するとjsonが出力される。

スクリーンショット 2019-12-14 10.58.11.png

Androidに導入していく

  1. gradleに追加していく
build.gradle
dependencies {
   //・・・
    implementation 'com.airbnb.android:lottie:3.3.1'
   //・・・
}

2.先程作成したjsonをリソースへいれる
スクリーンショット 2019-12-14 11.04.03.png

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"/>

いよいよ実行だ!!!!!

Screenshot_20191214-130054.png

アニメーションが動かず、いろいろ調べてみると、画像を含めたjsonファイルはアニメーションしてくれないような情報を発見したところで、これ以上は時間的に諦めることに。。。

諦めてクリスマスっぽいアニメーション探して入れてみる

1.公式でいい感じのアニメーションを探してjsonファイルをダウンロードする
-> こちら をお借りしました。

スクリーンショット 2019-12-14 13.10.02.png

2.自分で作成したjsonファイルと同じようにファイルを追加する

スクリーンショット 2019-12-14 11.04.03.png

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"/>

実行してみると

うごいた!カワイイヤッター!!!!!!!

ssaaaaaa.gif

使ってみての感想

すごい高いクオリティのアニメーションがとってもお手軽に導入することができたので、今度はきちんとアニメーションから作ってみたいと思いました。
また、AndroidやiOSもwebでも同じアニメーションが使えるため、使い回せて便利だなと思いました。
現在は、まだ使えないアニメーションなどがありますが、今後どう進化していくのか注目して行きたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を使いました。ボタンをポチポチするだけで定義できます。
image.png

コレクションはドキュメントの集合です。
1ツイートのデータを1ドキュメントとして保存するコレクションpicsと、保存総数をカウントするためのドキュメントを1つだけ持つコレクションinfoを設定しました。
image.png

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は、ドキュメントをそのフィールドを使って検索したり、並び替えたりすることができます。

試しにリスイン数で並び替えてみました。
image.png

image.png
image.png
image.png

以上です

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

OpenCV4Android Samples

tutorial-1-camerapreview

カメラのプレビュー画像を表示する。
opencv42_preview.png

基本となるサンプルなので、別の記事で解説する。

Android で OpenCV のサンプル camerapreview を試す

tutorial-2-mixedprocessing

OpenCVの Java API と Native API の2つの方法で、
カメラからの画像を変換して、表示する。

特徴点抽出の例
opencv48_find._feature.png

Native API を使うサンプルなので、別の記事で解説する。

Android で OpenCV のサンプル mixedprocessing を試す

tutorial-3-cameracontrol

OpenCV の Java API を使って、
Androidアプリでカメラを操作する方法を示す。

sepia の例
opencv43_sepia.png

下記の機能がある。
- カメラの Color Effect を有効にする。
- カメラのプレビュー解像度を変更する。
- カメラ画像をキャプチャしてファイルに保存する。

カメラの Color Effect には、下記の選択肢がある。
- none
- mono
- negative
- solarize
- sepia
- posterize
- whiteboard
- blackboard
- aqua

注意:
これらの機能は API 21以降で、廃止予定 (Deprecated) になった旧来の Camera API により実現される。

reference : setColorEffect

サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv43

Camera2 API を使って同様の機能を実現する方法は、
下記の記事を参照ください。

Android の Camera2 API を使って Color Effect する

image-manipulations

複数のフィルターをサポートし、色空間の変換とヒストグラムの操作を示す。

ヒストグラムの例
opencv45_histgram.png

下記の選択肢がある
- Preview RGBA
- Histograms
- Canny
キャニー ( Canny) 法によりエッジを検出して描画する
- Sobel

ソーベル ( Sobel) 法によりエッジを検出して描画する
- Sepia
- Zoom
- Pixelize
モザイク処理
- Posterize
階調変更

サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv45

15-puzzle

カメラ画像を分割して、15パズルにする。
opencv44_15puzzle.png

メニューは下記の2つ
- Show/hide tile numbers
- Start new game

画像の分割には、Mat#submat を使用する。
タイル画像の中央に赤い文字でタイル番号。
タイル画像の境に緑の線

サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv44

color-blob-detection

Blobは、いくつかの共通のプロパティ(グレースケール値など)を共有する画素の集合です

このサンプルは、タッチされた場所の色と同じ色の領域を検出し、
領域に輪郭を描画する。

opencv46_blob.png

InRange で、同じ色の領域を検出する
findContours で、輪郭を検出する
drawContours で、輪郭を描画する

サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv46

 camera-calibration

カメラの歪みを較正する

グリッドパターンを検出した例
opencv47_calibration.png

このサンプルは、下記を基にしている。
Camera calibration With OpenCV

使い方

OpenCV非対称円グリッドパターン11x4 を使用する。
https://github.com/opencv/opencv/blob/3.4/doc/acircles_pattern.png

グリッドパターンを検出すると、ハイライト表示される。

グリッドパターンを様々な距離、角度から画面をタッチして、キャプチャーする。

メニューバーの「calibrate」ボタンを押す。
結果として、5つの歪み係数が得られる。

プレビューモードは3通りある。

  • Calibration グリッドパターンを検出してハイライト表示する
  • Undistortion 歪みを較正した画像を表示する
  • Comparison 元の画像と歪みを較正した画像を並べて表示する

opencv : cv::calibrateCamera

サンプルコードはこちら
https://github.com/ohwada/Android_Samples/tree/master/Opencv47

 face-detection

顔検出機能の最もシンプルなサンプルです。
opencv49_face_detect.png

別の記事で解説する。
https://qiita.com/ohwada/items/d4cae2924aabe18cc9c4

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidアプリ開発初心者が1ヶ月でアプリをリリースするまでにやった8つのこと

はじめに

こちらはDeNA 20 新卒 Advent Calendar 2019の18日目の記事です。

はじめまして、@yayamochiです。
10月末からAndroidアプリの開発の勉強を始めました。
Android端末をほぼ8年ぶりに触るところから始めて、
12月の半ばくらいまでにアプリを2つ開発し、1つはリリースまで行うことができました。

この記事では

  • どうやってAndroidアプリ開発の勉強をしたのか
  • 大学院生の私が修士論文と闘いながら、どのように個人開発を継続していったのか

ということについてそれぞれ4つずつ、合わせて8つのことを紹介していきたいと思います。

対象読者?

  • Androidの開発にチャレンジしてみたい人
  • 個人開発が続かない人
  • ある程度プログラミング経験がある人

私の事前知識?

今回つくったもの?

レーダーチャートメーカー

radarchart.gif

その名の通りレーダーチャートの一覧を簡単にスマホで作れるアプリです。
こちらが今回リリースまで行ったアプリです。
来年度に引っ越しの予定があり、家を決める際に簡単に評価のメモができれば良いと思い作成しました。

レーダーチャートメーカー(Google Play)

【使用ライブラリ】
公式のもの
- Android Architecture Components(LiveData・Room・ViewModel・Lifecycle)
- Navigation Architecture Component
- RecyclerView
- kotlinx.coroutines
依存性注入
- Koin
ログ解析
- Firebase
グラフ描画
- MPAndroidChart

写真メモ帳 フォトメモ

photomemo.gif

こちらは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
メモリリークチェッカー
- LeakCanary

Android開発の勉強法?

1. Kotlin Koans でKotlinならではの書き方に触れる

Kotlin Koans

私は普段からJavaを使って研究のコードを書いていたので、簡単にKotlinの文法を確認しました。
30分で覚えるKotlin文法

その後はこちらのサイトでKotlinならではの書き方を勉強していました。
Kotlinの問題をWeb上で実行して確かめることができます。

スクリーンショット 2019-12-14 11.12.31.png

2. Android Kotlin Fundamentals Course で一通りの開発の流れを学ぶ

Android Kotlin Fundamentals Course
KotlinでのAndroid開発の基礎を教えてくれる無料のコースです。
全10章で、非常でボリューミーな内容です。なぜ無料なの…!

  • IDEのインストール方法・便利な使い方
  • 基本的なUIの作成方法
  • 画面遷移の作成方法
  • ライフサイクルの理解
  • DB操作

などを学ぶことができます。

内容がアップデートされることもあり、個人的には本を買うよりは新しい内容が書かれているのでやりやすいと思います。

少し大変な点は、すべて英語で書かれているという点です。
しかし、丁寧なソースコードもあるので翻訳しながら読んでいけば読み進められると思います。
本当に本当にオススメのコースです!

ちなみにChromeのブラウザを使っているのであれば、こちらの翻訳ツールが使いやすくて便利です。
カーソルで選択してアイコンをクリックすれば、すぐに翻訳結果をみることができます。
ImTranslator: Translator, Dictionary, TTS - Chrome Web Store

スクリーンショット 2019-12-14 11.08.05.png

私はこのコースを7章までやりました。
あとは作りたいアプリを作る!→バグにぶちあたる→解決してまた作る!のサイクルを繰り返してどんどん足りない知識を補っていきましょう。

3. DroidKaigiのYouTubeやスライドをチェックしよう

AndroidのカンファレンスであるDroidKaigiのYouTubeチャンネルがあります。
DroidKaigi YouTubeチャンネル
少し難しめの内容も多いかもしれませんが、面白そうな発表がたくさんアップロードされています。

  • ぼくのかんがえた最強の Usecase の作り方
  • Navigation Architecture Component によるアプリ内遷移の管理
  • 今日から始める依存性の注入

などの発表をみました。
今の知識では少し難しい話でも、後々知識がつながることがあるので興味があれば是非聴いてみましょう。

4. Twitterやブログを購読して毎日情報収集をする

この1ヶ月の間に修士論文の審査が入っており、開発の時間がとれない日もありました。
しかし毎日技術には触れたい!ということでTwitterで情報収集のためのリストを作成し、それだけは毎日眺めるようにしていました。

リスト表示やキーワード検索の表示には以下のアプリがおすすめです。
- PCならTweetDeck
- iOSアプリならBuncho
- Androidアプリならツイタマ

IMG_5748.PNG

個人的にオススメのタグは「#KotlinTips」です。

Kotlinの公式アカウントだけではなく、色んな人が日々情報を流してくれているので、初心者にはとても参考になる情報ばかりです。

個人開発の継続のコツ?

5. 最低限つくる機能と、あったらより良い機能をわける

IMG_5664 2.jpg
これはレーダーチャートメーカーを作る際にUIの概要をざっくりメモしたイラストです。(超汚い?)
黒が最低限の機能、赤が余力があれば追加したい機能です。
最初から機能を盛り盛りにしてしまうと、先が果てしなくなり、どんどんやる気がなくなっていきます。
少し難しそうな機能の実装は最低限の機能の完成後に回しておくことで、挫折せずに開発を進めることができます。

6. 開発開始日は時間が取れる日を選ぶ

最初のたたき台を時間のある日に一気に作ってしまいましょう。
私は12月1日(日)にレーダーチャートメーカーの開発を始めましたが、
その日に大まかなアーキテクチャの構成、DBの設定、UIのベース作成、できる範囲で画面遷移の作成をガッツリやってしまいました。
大変な所を先にやっておくことで、時間のない平日にも「このUIだけ作っちゃおうか〜」と思うことができるのでオススメです。

7. できる限り毎日コードを書く

とても当たり前のことなのですが、結構難しいですよね…。
5分でもいいので、毎日コードを書くかそれに関わる話に触れましょう。
ベテランの方は知識が染み付いているので良いですが、
初心者の段階だと基礎が身についておらず、忘れるスピードも早くなり、余計やる気がなくなってしまいます。
なお私は一週間近くAndroidアプリの開発ができない期間があり、サッパリわすれてしまいました。

8. とりあえずリリースしてみるという考えをもつ

私は今まで3本のiOSアプリを作りました。
自分では「このアプリ絶対イケる…!!!」と思っても、全然インストールされないなんてザラです。
ツラいですが現実です。
私は初めて出したiOSアプリはあんまりインストールされずに、「あんなに大変だったのに…!」とがっかりしてしまった記憶があります。笑
だったら最初に最小限の機能だけ作ってしまってリリースしちゃいましょう。
出してみないと本当にニーズがあるアプリなのかわかりません。
一度リリースしてみて、ユーザーがいることが確認できれば、アップデートするやる気も湧いてきます。

まとめ

この記事では私のAndroidアプリの開発の勉強方法と、リリースするまでアプリを作り続けるコツについて紹介しました。
あまり時間がとれないない中でも、単純なアプリですが作り切ることができました。
Androidアプリ開発をしたいけど、何からしたら良いかわからない、個人開発しようとしても作り切ることができない…!という方の参考になっていただけたら嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

星1個のアプリなんて作りたくない

Labomartの軌跡。構想から運用まで@unotovive
ゆめみ Advent Calendar 2019 の前回の記事

はじめに

 Android アプリ開発者のみなさん、Google I/O 2018 の下記のセッションは見ましたか?

ちなみに、私は、、、、、、見てないです :rolling_eyes:

セッションでは、Play Store で星1レビューのついた原因分析を公開して

原因の42% は「アプリのクラッシュを含む安定性の問題」だったみたい。


出典: Youtube (Improve app performance and stability with Firebase (Google I/O'18))

星1レビューもらいたくない

 テストを書きましょう笑

よくテストを書く時間がないと聞きますが、、

たぶん仕様調整やテストの時間がないだけです笑

きちんと仕様調整やテストの時間をもらいましょう :no_good:

そして、プログラムや仕様のバグを洗い出すのです :sunglasses:

千里の道も一歩から

 テストを書くことは思っているより簡単。

まずは1つテストを書いてみましょう!!

テストって?

テストの種類には下記のようなものがあって

  • 単体テスト
  • 結合テスト
  • UI テスト

今回は単体テストに焦点を当てて話をしていきます!

Android の単体テスト

 Android の単体テストには下記のようなものがあります

  • Local Unit Test
  • Instrumented Unit Test

何が違うん??って感じですが、簡単にいうと実機やエミュレーターが必要かどうかです。

今回は、実機やエミュレーターが必要ない Local Unit Test をやっていきます!

強い意志でテストを書こう

 テストを書くのに強い意志は必要ありませんw

なんか物足りなかったので付けました笑

さて、テストを書いていこうと思いますが

せっかくなので最新の技術を使っちゃいます :nerd:

今回は JUnit5Truth を使います!

※ Truth は assertThat 使うために入れてます。ただそんだけですw

準備

 ここからやっとコード書いていきます〜

下記の変更をして、Sync Now しちゃえばおk

ExampleUnitTest.kt は要らないので削除してね

app/build.gradle
android {
    ...
+   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.kt
package {自分のパッケージ名}.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.kt
package {自分のパッケージ名}.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 があります :sunglasses:

使ってみましょ〜

extensions/StringExtensionKtTest.kt
package {自分のパッケージ名}.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 最高

これで複数ケース簡単にテストできますね♪

じゃんじゃんテストケース追加していってください :relaxed:

実は、、

 テストが失敗するケースがあります笑

みなさん見つけられましたかね?

見つけられていない方はテストケース追加しまくってくださいね :heart:

おわりに

 ちゃんとテストを書いて、少しでもバグを減らして

星5のレビューたくさんもらえるようになりましょ〜 :star2:

明日は @ihcamonoihS さんの記事 :hand_splayed:

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】ConstraintHelperの使い方

本記事はAndroid #2 Advent Calendarの22日目の記事になります。 :christmas_tree: :snowman2:
初の参加で若干緊張しています。。。。 :innocent:

はじめに

現在、AndroidのUI実装ではConstraintLayoutを利用することがほとんどだと思います。
今回はConstraintLayoutの一つの、ConstraintHelperについて紹介したいと思います。

なお、今回使用するバージョンは、ベータ版のConstraintLayout 2.0.0-beta2を使用しているので、後ほど内容が変更になる可能性がありますので、その点はご了承ください。
また、変更がかかった際は、再度こちらの記事にも更新をかけようと考えています。 :bow:

ConstraintHelperとは

複数のViewに対して同じ操作を行うためのものと認識しています。
ConstraintHelper自体の実装はViewを拡張した抽象クラスであり、このクラスを独自に拡張することもサポートされているようですので、非常に便利です。

ConstraintHelperへ反映させたいViewを指定するときは、app:constraint_referenced_idsにidをコンマ区切りで格納することで指定できます。これは基本的にどのConstraintHelperでもこの方式を用いており、共通仕様となっております。

独自で拡張するまでもなく、すでにConstraintHelperを拡張したクラスとして、以下が存在します。

image.png

すでにご存知のものも多いと思いますが、一つずつ軽く説明します。(VirtualLayoutは抽象クラスですので飛ばします。)

Barrier

下のように、二つの並んだViewに基準線をもうけます。これがバリアーと呼ばれるConstraintHelperであり、そこに Constraint をつけることで、片方の大きさが変わっても、基準線は変動してViewが重なるということは無くなります。これは毛っここう使ったことのある人が多いのではないでしょうか。

barrier.gif

Flow

特定のViewを指定した方向に並べます。方向としては、HorizontalVerticalが存在し、それぞれの並び方を以下に示します。こちらは最近リリースされた機能になるので、初めての方も多いと思います。

Horizontal

スクリーンショット 2019-12-11 1.20.27.png

Vertical

スクリーンショット 2019-12-11 1.29.48.png

Group

まとめてViewのVisibilityを変更したい時に使用します。
GroupのVisibilityを変更すると、それに応じて、中身のViewのVisibilityも一括で変更されます。
こちらはかなり有名なので、愛用している人も多いと思います。

group.gif

Layer

Viewをまとめて操作したい時に使用します。

今回はLayerを使用して特別なことはしていませんが、app:constraint_referenced_idsにViewを追加してみると、下のようにまとめてくれましたので、今までLayoutをネストしてUI実装してた箇所が不要になると思います。

スクリーンショット 2019-12-12 1.17.30.png

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.kt
class 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せずに単純にmIdsforEachだけでも良いのでは? :thinking: と思っているのですが、他のGroupBarrierでも同じ手法で実装されているので、毎回これを使っています。 :thinking:

実行

このBakgroundHelperを先ほどのTextView二つに適応させると以下のようになります。(色は#D81B60で指定しています。)

スクリーンショット 2019-12-14 0.18.41.png

ConstraintHelperで個人的に一番感動したポイントなのですが、このようにbackgroundColorを変更すると、リアルタイムでその変更が適応されます。(画面左側)

background_helper.gif

最後に

今回はConstraintHelperというConstraintLayoutの一部について紹介しました。
ConstraintLayoutだけでは表現しきれないUIも、これを使えば実装ができるかもしれませんし、非常に将来性のある機能だと思っています。

2.0.0がstableになるのが待ち遠しいですね! :upside_down:

余談

ConstraintLayoutの最新版として、ConstraintLayout 2.0.0-beta3があるのですが、ConstraintHelperがうまく動いてくれませんでしたので、しぶしぶ今回はConstraintLayout 2.0.0-beta2を使用しています。。。
やはりベータ版だと、ところどころバグがあるので、stableになるまで待ったほうがよさそうですね :sweat:

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】ConstraintLayoutの使い方

本記事はAndroid #2 Advent Calendarの22日目の記事になります。 :christmas_tree: :snowman2:
初の参加で若干緊張しています。。。。 :innocent:

はじめに

現在、AndroidのUI実装ではConstraintLayoutを利用することがほとんどだと思います。
今回はConstraintLayoutの一つの、ConstraintHelperについて紹介したいと思います。

なお、今回使用するバージョンは、ベータ版のConstraintLayout 2.0.0-beta2を使用しているので、後ほど内容が変更になる可能性がありますので、その点はご了承ください。
また、変更がかかった際は、再度こちらの記事にも更新をかけようと考えています。 :bow:

ConstraintHelperとは

複数のViewに対して同じ操作を行うためのものと認識しています。
ConstraintHelper自体の実装はViewを拡張した抽象クラスであり、このクラスを独自に拡張することもサポートされているようですので、非常に便利です。

ConstraintHelperへ反映させたいViewを指定するときは、app:constraint_referenced_idsにidをコンマ区切りで格納することで指定できます。これは基本的にどのConstraintHelperでもこの方式を用いており、共通仕様となっております。

独自で拡張するまでもなく、すでにConstraintHelperを拡張したクラスとして、以下が存在します。

image.png

すでにご存知のものも多いと思いますが、一つずつ軽く説明します。(VirtualLayoutは抽象クラスですので飛ばします。)

Barrier

下のように、二つの並んだViewに基準線をもうけます。これがバリアーと呼ばれるConstraintHelperであり、そこに Constraint をつけることで、片方の大きさが変わっても、基準線は変動してViewが重なるということは無くなります。これは毛っここう使ったことのある人が多いのではないでしょうか。

barrier.gif

Flow

特定のViewを指定した方向に並べます。方向としては、HorizontalVerticalが存在し、それぞれの並び方を以下に示します。こちらは最近リリースされた機能になるので、初めての方も多いと思います。

Horizontal

スクリーンショット 2019-12-11 1.20.27.png

Vertical

スクリーンショット 2019-12-11 1.29.48.png

Group

まとめてViewのVisibilityを変更したい時に使用します。
GroupのVisibilityを変更すると、それに応じて、中身のViewのVisibilityも一括で変更されます。
こちらはかなり有名なので、愛用している人も多いと思います。

group.gif

Layer

Viewをまとめて操作したい時に使用します。

今回はLayerを使用して特別なことはしていませんが、app:constraint_referenced_idsにViewを追加してみると、下のようにまとめてくれましたので、今までLayoutをネストしてUI実装してた箇所が不要になると思います。

スクリーンショット 2019-12-12 1.17.30.png

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.kt
class 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せずに単純にmIdsforEachだけでも良いのでは? :thinking: と思っているのですが、他のGroupBarrierでも同じ手法で実装されているので、毎回これを使っています。 :thinking:

実行

このBakgroundHelperを先ほどのTextView二つに適応させると以下のようになります。(色は#D81B60で指定しています。)

スクリーンショット 2019-12-14 0.18.41.png

ConstraintHelperで個人的に一番感動したポイントなのですが、このようにbackgroundColorを変更すると、リアルタイムでその変更が適応されます。(画面左側)

background_helper.gif

最後に

今回はConstraintHelperというConstraintLayoutの一部について紹介しました。
ConstraintLayoutだけでは表現しきれないUIも、これを使えば実装ができるかもしれませんし、非常に将来性のある機能だと思っています。

2.0.0がstableになるのが待ち遠しいですね! :upside_down:

余談

ConstraintLayoutの最新版として、ConstraintLayout 2.0.0-beta3があるのですが、ConstraintHelperがうまく動いてくれませんでしたので、しぶしぶ今回はConstraintLayout 2.0.0-beta2を使用しています。。。
やはりベータ版だと、ところどころバグがあるので、stableになるまで待ったほうがよさそうですね :sweat:

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter Interact 2019のまとめ

はじめに

この記事はFlutter #2 Advent Calendar 2019の11日目分です。

今年もすぐに枠が埋まってしまっていたのでFlutter Advent Calendarに参加する予定はなかったのですが、ふとカレンダーを覗いていみると11日枠が投稿されずに空いていたのでちょうどFlutter Interactが開催されたので急遽この記事を書くことにしました。

Flutterの最新情報に興味がある方はFlutterウィークリーもよかったらご覧ください
https://qiita.com/tags/flutterweekly

Flutter 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やマルチサイズのスクリーンに簡単に対応できるようにするには必要な機能ですね
Adaptive UI

開発中のレイアウトコードエディタ。レイアウトの細かな調整もプレビューで確認しながらデザイナーが調整しそのままコードを出力できるようになります。
Layout Editor

Product Keynote (Flutter Interact '19)

https://youtu.be/ukLBCRBlIkk

a portable UI framework for an ambient computing world

Flutterはモバイルアプリのクロスプラットフォームとして知られていますが、様々なスクリーンが増え続ける中すべてのスクリーン向けのアプリを開発できるようにすることを目指してFlutterの開発が続いています。

Flutter 1.12リリース

Flutter 1.12

ダークモードのiOS対応

Dark mode support for iOS

新しいCupertinoウィジェット

Cupertino widgets

Android Xがデフォルト

Android X

既存アプリへの追加

add-to-app support

Googleフォント

Google Fonts

Flutterギャラリー

Flutter Gallery

StadiaをFlutterで開発

Stadia

eBayがFlutterでアプリを作り直した

https://play.google.com/store/apps/details?id=com.ebay.mobile
ebay

Flutter on Desktop

Flutter On Desktop

Flutter on the Web

Flutter on the Web

Dart

Screen Shot 2019-12-14 at 00.00.00.png

Dartpad

https://dartpad.dev/
Dartpad
Web上のPlaygroundでFlutterも動くようになりました

Codelabs

https://flutter.dev/docs/codelabs
Codelabo
コードラボもそのままFlutterの動作を確認できるようになりました

Flutter outline Editor

Flutter outline Editor

レイアウトエクスプローラー

Layout Explorer

Adobe XD

Screen Shot 2019-12-14 at 00.09.54.png

キーノート以外のセッション

Welcome to the Sparkle Party (Flutter Interact '19)
https://youtu.be/1AxXF038-lY

Material Theming with Flutter (Flutter Interact '19)
https://youtu.be/stoJpMeS5aY

Designing for the Web with Flutter (Flutter Interact '19)
https://youtu.be/tot-R_q5-0o

Building in Accessibility with Flutter (Flutter Interact '19)
https://youtu.be/bWbBgbmAdQs

Design and Build Clock Displays with Flutter (Flutter Interact '19)
https://youtu.be/i60HG1TtKJo

Use Rive and Flutter for dynamic, interactive, & animated experiences (Flutter Interact '19)
https://youtu.be/6QZy5sYozVI

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む