最近アプリやAndroid Studio用プラグインを作るのにKotlinを使っています。
始めたばかりの頃は「Javaだとああ書くんだけど、Kotlinだとどう書けばいいんだ」ということが多かったです。Javaでまともに書けないのにKotlinに手を出すのは早いんじゃないかとも思っていました。
しかし少しずつ試していくと、Kotlinの便利な部分が分かってきてきました。
私の場合、「Kotlinで書き始めたんだけどやっぱ使い方よくわからないからJavaに戻そう」とう場面が初期の頃はよくありました。はじめはKotlinで書いていたけど、やっぱりJavaで実装しようとという感じです。
そんなときに、「Javaに戻すのめんどくせえ」と感じる部分があって、そこで改めて「Kotlinってやっぱ便利やなぁ」なんて実感しました。
それからというもの、Kotlinの比重が徐々に増えてきて、今では逆にJavaで書く方が面倒くさいと感じるようになってしまいました。
一方で、Kotlinが無敵というわけではありません。Annotation Processingを使うライブラリがKotlinだとうまく使えないことがあったり(基本的には大丈夫ですが、Javaで書けば動くコードがKotlinで同じように書くと動かないことがあったりします)、Javaと比べるとコード補完が遅かったり、Kotlinを使うことで感じるストレスもあります。
ですが、不便さを差し置いてもKotlinで書いた方がすっきり書けるのはやっぱり快適だと思っています。
テストコードから始めるといいかも どなたかの記事で、Kotlinはテストコードから導入してみたらどうかという記事を読みました。私もいい方法だと思います。Kotlinの便利さを実感するためではなく、どう書くかを知るのにちょうどいいと思います。
私もJUnitでのユニットテストをKotlinで書いています。テスト対象をKotlinで書いてるからとか、セミコロンつけなくてもいいから、とかそんな理由です。ユニットテストについてはKotlinが便利だからという理由はあまりないかもしれません。
ユニットテストにおいてKotlinが便利だと思うのは、バッククオート(`)で囲むことで、メソッド名やクラス名を数字から始めることができたり、途中に空白を含めることができたりすることでしょうか。
私はテスト名に日本語を使うことが多いです。そしてそのときに、メソッド名に使える文字に制約があるのが微妙に困ります。
例えば各月の最終日を求めるメソッドのテストをするのに「4月の場合は30日を返す」というテスト名にしたくてもできません。Javaではメソッド名を数字から始めることができないからです。だからこんなときは「月に4月を指定したら30日を返す」という感じのメソッド名にするのですが、これが微妙なストレスになります。(頭に「月を」つけるだけやんという感じですが、微妙にストレス感じるんですよこれ)
Kotlinではこの制約に煩わされることがありません。メソッド名をバッククオートで囲めば、数字から始めようが、途中に空白を挟もうが問題ないのです。
@Test fun `2つの時刻の差を求める`(){ val time1 = LocalDateTime.of(2014, 1, 1, 23, 58, 30) val time2 = LocalDateTime.of(2014, 1, 2, 0, 4, 30) val actual = Duration.between(time1,time2).seconds assertThat(actual, `is`(360L)) } なにそれ気持ち悪いと思われるかもしれませんが、これはれっきとしたKotlinの仕様です。
Grammar – SimpleName
Javaのメソッド名規約によってテストメソッド名を考えるのが面倒くさいなぁと感じている人は私だけではないと思いたい。
一方でKotlinでユニットテスト書けば便利なことばかりではありません。例えばassertThatなどを使おうとするとimport文を手書きで書かないと認識してくれないのが不便です。(私の環境の問題なのかもしれません。JavaだとALT+Enterでimportできるんですけどね・・・)
セミコロンつけなくてもいい 単純なことですが、Kotlinは文末にセミコロンをつけなくてもいいのです。
これが便利・・・と言いたいところですが、私は半々かなぁと感じています。
確かにいちいちセミコロンつけなくてもいいので楽です。たまにJavaでコードを書くときに、しょっちゅうセミコロンつけ忘れます。それくらいには快適です。
一方セミコロンが不要なせいで、メソッドチェーンするときに私は微妙にストレスを感じます。
Javaだとメソッドチェーンするときに改行をするとインデントを一段深くしてくれます。
しかしKotlinでは改行した時に文末なのか次の文に移るのかが判別不能なので、インデントを深くしてくれたりしません。これが毎回微妙にめんどうくさいです。私はいつもドットを打ってカーソル戻して改行するという方法をとって回避しています。
多分Reformat Code(Cmd+Alt+l)を使うのが楽なんでしょうけども。みんなどうしてるんだろう・・・。
文字列の扱いが便利 Strings
Javaだと文字列に変数を埋め込もうと思うと、+演算子で連結しなければなりません。もしくはString.format()を使うかですね。
Kotlinだとそんな面倒くさいことをせずとも、文字列中に変数を埋め込めるので便利です。こんな感じに書けるわけです。
val hoge = "num is $num"
埋め込む変数が多くなればなるほど、これはとても便利になります。デバッグのために変数の中身を文字列として出力して確認することがよくあると思いますが、そんなときに特に楽だと思います。
複数行に渡る文字列も、"""で囲むことでそのまま文字列として扱うことができます。ただしこれを使うと、インデントによる空白も文字列に含まれてしまうので、使いドコロが難しい気もします。
配列操作が便利 例えば配列の要素の中から最大値を取得しようと思ったら、Javaだとこんな感じになるでしょう。
int[] array = {1,20,3,40,5,16,7}; int max = 0; for (int num : array) { max = Math.max(num, max); } Kotlinだとこうです。
val array = arrayOf(1,20,3,40,5,16,7) val max = array.
以前から作ってみたいなぁとは思っていたのですが、このたびやや必要性が増したこともあってAndroid Studio用のプラグインを作りました。
作成時間の半分くらいは環境構築に手間取っていたと思います。
ソースコードはGitHubで公開していますが、クローンしてもそのままビルドしたりできるのかよく分かりません。一応自分でgit cloneして確認したりしてみましたが、Androidのプロジェクトと比べると一手間必要で面倒くさい感じです。
参考にさせていただいたプラグインはほぼほぼgit cloneしただけでは動かせなかったので、Gradleは偉大だなということを再認識しました。
ADB Friendly ADB Friendlyという名前ですが、現状では端末の画面を回転させることしかできません。どんな感じかはYouTubeをご覧ください。
私は端末の画面を回転させながらメモリ使用量のグラフを見て、作成したアプリがメモリリークしていないかどうかを確認することがあります。グラフが右肩上がりに上がり続けていると、それはメモリリークが発生しているということです。
今までは端末を手に持って、縦横縦横・・・とやっていました。ところがつい最近、こんなものを買いました。Macbookのモニタ横に、スマホを固定するクリップです。
【日本正規代理店品】Ten One Design Mountie (iPhone、iPad用サブディスプレイ・マウントアダプタ) グリーン TEN-OT-000002 Twitterのタイムラインで見かけて一目惚れして買いました。実に便利です。 便利なのはいいのですが、モニタにスマホを固定していると、画面を回転させることができません。そもそも手で画面回転させるのも面倒くさいです。そこでプラグインを作ってやることにしたわけです。
環境構築が大変 参考にさせて頂いたサイトは最後にまとめました。偉大な先達たちに感謝です。
私はSDKを準備するところからつまづきました。開発に使っているのはIntelliJ IDEA 2016.1.1になるのですが、このバージョンをもとにIntelliJ Platform Plugin SDKを作るとInternal Java Platformに1.6を指定することができませんでした。
これに関しては開発に使うIntelliJと、SDKに使うIntelliJのバージョンは別物であると考えたほうがよいのだと思います。
IntelliJ Platform Plugin SDKを追加するには、プラグインを作るにあたって対象とするバージョンのIntelliJ IDEAを別途インストールし、それを指定してやるのが正しいのだと思います。
プラグインを作成する際に、AndroidでいうminSdkVersionのような指定がIntellij pluginにもあります。(私の場合<idea-version since-build="141.0"/>と指定しました)
このバージョンはIntelliJ14.1を意味するので(参考:Build Number Ranges)、実際にコーディングする最新のIntelliJとは別に14.1.6をダウンロードしてインストールしました。
Previous IntelliJ IDEA Releases
また、プラグインを開発していくにはソースコードがないとつらいと思います。別途IntelliJ-Community – GitHubをクローンしてソースコードを入手しておく方がいいでしょう。ちなみにcloneする際には自分がSDKに指定したブランチになっているかを確認しましょう。SDKで利用するclassファイルとソースコードの中身で食い違いが生じて余計に混乱します。
正直な所、公式のDeveloper Guideはお世辞にも分かりやすいとはいえないので、ソースコードとすでにある先人たちのPluginこそがお手本でした。
Kotlin + Gradle このプラグインはKotlinとGradleを使って作りました。既にやってくださっている方がいらしたので非常に助かりました。
KotlinとGradleで始めるAndroid Studioプラグイン開発
Kotlinを使った理由は、そんなに深い理由があるわけではありません。単に直前に作っていたアプリでKotlinを使った開発をしていた影響です。正確にはJDK1.6による開発のため、ラムダ式とか使うのにバックポートライブラリを入れるよりはKotlin使ったほうが楽かなとか、Javaで書いたときにセミコロンつけるのが面倒くさかったとか、そんな感じです。
Gradleを使ったのは、依存関係の処理に困ったからです。jarを拾ってきてlibフォルダに入れたらなんとかなるんでしょうが、どうやればいいのかがよく分からなくて、少しでも慣れているGradleを使いたかったのです。
最近読んだAndroid Studio本格活用バイブルのおかげで、「この.imlファイルを削除しても、最悪Gradleから復元できる」みたいなことが分かっていたのが大いに役立ちました。
Android Studio本格活用バイブル ~効率的にコーディングするための使い方 GitHubで公開中 ソースコードはGitHubで公開してあるので、ご意見ご感想待ってます。
細々した問題はあるものの、一応動くと思います。多分。
正直な所、自分以外の環境だと動くのかどうか分からないので、動いた・動かないの報告をいただけるだけでもありがたいです。
参考サイト・情報 参考にさせていただいた方々に感謝を。
初めてのAndroid Studioプラグイン開発入門
KotlinとGradleで始めるAndroid Studioプラグイン開発
IntelliJ Platform SDK Documentation
AdbCommander for Android – BitBucket
ADB Idea – GitHub
Android Material Design Icon Generator – GitHub
式の即時評価が便利だよねという話です。
私は以前Calendarクラスを使って日付の処理をしようとしていました。そのとき、どのフィールドを参照すれば目的の値が引っ張ってこれるかを確認するのに、愚直にLog.d()を使っていました。1つ1つメソッドの返り値を出力して(文字列の連結でさらにカオスになる)、目的の数値がちゃんと取れているのか確認していたのです。
ドキュメントを読めよっていう話なんですが、読んでもどういう値が取れるのかいまいち分からなかったんですよね・・・。
まあそんなアホなことをやっていたので、当然のようにバグを仕込んでいました。そんなバグに気づくきっかけとなったのが式の即時評価機能です。以来、とてもお世話になっています。
式の即時評価を使えば、ブレークポイントを設定してデバッグ実行するだけで、任意のメソッドや変数の確認ができるようになります。
ブレークポイントで一時停止させないと使えないので、状態の変化を追うのには向かないかもしれません。それでもlogcat頼みのデバッグより捗る場面があると思います。
どんなときに便利か 今日遭遇したエラーで、なんかのタイミングでNullPointerExceptionが発生してクラッシュする現象が発生しました。
例外の発生する箇所はわかっているものの、どういう状況でそれが生じているのかがよく分かりませんでした。
そこで例外の発生する部分をtry-catch文で囲み、例外をキャッチした所にブレークポイントを置いて調べてみることにしました。
ブレークポイントで止めればコールスタックを遡ってオブジェクトの状態を確認できますが、目当ての変数を探すのが大変なので、そういうときに式の即時評価が便利です。だと思います。
今回の記事のサンプルコードは、GitHubで公開しています。
お絵かきアプリを作ろうと思って格闘中です。とりあえず線を描くだけでも学びがいろいろあったのでまとめておこうと思います。
Pathを使って描画するとカクカクする問題 線を描くにはPathを使うのがオーソドックスのようですが、何も考えずにパスを使った描画を行うと、線がカクカクしてしまいます。(サンプルコードのPathPaintView)
path.lineTo(e.getX(), e.getY()); drawCanvas.drawPath(path, paint);
これはなぜ起こるのでしょうか。
MotionEventが配信される間隔の問題 その理由はまず線をPathではなく点で描画してみると分かります。(DotPaintView)
drawCanvas.drawPoint(e.getX(), e.getY(), paint);
描画される点がまばらになっています。このドットはonTouch()が呼ばれるタイミングで描画されています。このドットの間隔がタッチイベントがViewに伝えられているタイミングだということです。これはスクリーンをタッチした情報が、逐一間断なくonTouch()に渡されているわけではないことを意味しています。
Historical情報を利用する ではドットとドットの間のタッチイベントの情報は失われているのかというと、決してそうではありません。onTouchに渡されるMotionEventには、MotionEventが配信されていない時に生じた座標を保持しています。
その情報はMotionEvent.getHistoricalX()などで取得することができます。これを利用すれば、MotionEventの情報をより精細に取得することができます。(HistoricalDotPaintView)
int history = e.getHistorySize(); for (int h = 0; h < history; h++){ drawCanvas.drawPoint(e.getHistoricalX(h), e.getHistoricalY(h), paint); } drawCanvas.drawPoint(e.getX(), e.getY(), paint);
ドットの間隔が狭まりました。指をゆっくり動かせばキレイな線が描画できます。しかしこのHistorical情報にも限度があり、指を少しでも早く動かすとやはり間隔が空いてしまいます。
Historical情報を使ってPathによる描画を行う Historical情報を利用すれば、精度の高い座標情報を取得できることが分かりました。この座標情報をPathによる描画で利用してみます。(HistoricalPathPaintView)
int history = e.getHistorySize(); for (int h = 0; h < history; h++){ path.lineTo(e.getHistoricalX(h), e.getHistoricalY(h)); } path.lineTo(e.getX(), e.getY()); drawCanvas.drawPath(path, paint);
単にpath.lineTo(x, y)で描画した時に比べると随分なめらかになりました。しかし、高速で動かしたらやっぱりカクカクしてしまうのは避けられません。なぜならpath.lineTo()による描画は、HistoricalDotPaintViewで描画した点と点の間を直線で結んでいるにすぎないからです。
これを解決するには、点と点の間をなめらかな曲線で結べば解決できそうです。
ベジェ曲線を利用する ベジェ曲線によりスムーズな線をひく方法はいろいろ考えられるでしょう。1つの方法としてこんなやり方ができます。(BezierPathPaintView)
private void onTouchMove(MotionEvent e){ float midX = (previousX + e.getX()) / 2; float midY = (previousY + e.getY()) / 2; path.quadTo(previousX, previousY, midX, midY); previousX = e.getX(); previousY = e.getY(); }
自分で作っておきながら分かりやすく説明できないのですが、この処理のポイントは3つです。
前回のMotionEventで配信された座標点を記憶すること 前回の座標と今回の座標の中間点を計算すること 前回の座標を調整点とする、前回の中間点から今回の中間点までの2次ベジェ曲線を描く この方法では、正確にタッチした通りの線が描けるわけではないのですが、比較的簡単な処理でカクカクしない線を描くことができます。
DataBindingがアツいらしいと聞いて試してみました。簡単な使い方をするなら想像以上に簡単でした。
今までActivityなどでfindViewByIdを書きたくないから、ButterKnifeをどのプロジェクトでも使っていたのですが、DataBindingを使えば同じようなことができます。
両者を使ってみて感じたのは、ButterKnifeがレイアウトXMLをJavaコードに持ってくるイメージであるとすれば、DataBindingはJavaコードをレイアウトXMLに持っていくイメージであるということです。
DataBindingを使うことで、Javaで作成したコードを、レイアウトXMLに埋め込むことができるようになります。レイアウトXMLでどのデータを使うか指定しておけば、Activityで「このクラス(のインスタンス)を使ってくれ」と指定するだけでその内容を表示できたりします。
具体的な使い方はData Binding Guide – Android Developersを参照してください。
DataBindingを使う設定 Android Studio 1.3以上であることが必須です。
Android Gradle Plugin 1.5.0-alpha1以上を使っていることが必須、でした。
Android Studio 2.0 betaになると、コード補完のサポートがより強力になってます。
build.gradleでDataBindingの設定を有効にすることで利用できます。
android { .... dataBinding { enabled = true } } 表示するデータを保持するクラスの作成 public class Character{ public String name; public int age; public String skill; public Character(String name, int age, String skill){ this.name = name; this.age = age; this.skill = skill; } } DataBindingを使ってアクセスするには、publicなフィールドであるか、privateなフィールドである場合publicなgetterがあることが必須です。
レイアウトXML <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <data> <variable name="chara" type="jp.gcreate.sample.databinding.Character" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="jp.gcreate.sample.databinding.MainActivity" > <TextView android:id="@+id/chara_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{chara.name}" /> <TextView android:id="@+id/chara_age" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{Integer.toString(chara.age)}" /> <TextView android:id="@+id/chara_skill" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{chara.
端末の画面をタッチした情報はMotionEventとしてActivityやViewに通知されます。
MotionEventはさまざまな情報を持っています。
MotionEvent – Android Developers
アクション(触れたのか、動かしたのか、離したのか) ポインタの数(何本の指で触っているのか) タッチした座標 これらは全てポインタごと別々に識別されていて、全てポインタのインデックスでアクセスすることが出来ます。(ポインタのIDではありません)
その辺りをごっちゃにしてハマった結果、Stackoverflowに投稿した質問がこちらです。Androidでマルチタッチ時のポインターIDを検出する方法(ちなみに投稿後に勘違いが原因であることに気づいた)
ポインタインデックス ポインタのインデックスは必ず0から始まり、getPointerCount() - 1まで割り振られます。
例えば2本の指でタッチしている場合、getPointerCount()は2を返します。1本目の指がポインタインデックス0で、2本目がインデックス1となります。
さらにこの状態で1本目の指を離すと、2本目の指のインデックスが0に変わります。
指を離す順番によってインデックスはころころ変わるため、特定のポインタを識別するのには使えません。
例えば人差し指、中指、薬指を使ったタップを考えましょう。途中で人差し指、薬指は離したり触れたりしているとします。しかし常に中指はつけたままにして、これをトラッキングしたいとします。この場合にはポインタインデックスを使うことは出来ません。
特定のポインタを識別するにはポインタIDを利用します。
ポインタID 一度タッチするとポインタにはIDが割り当てられ、そのIDは指を離すまで変わりません。
上記の例で言うと、中指を画面から離さないかぎり中指を示すポインタのIDは常に同じです。
一方で注意しなければいけないのは、座標を取得したりするメソッドの引数はポインタインデックスであるということです。
ポインタはIDで識別するけど、そのポインタの情報を取得するために必要なのはポインタインデックスです。
そのため、特定のポインタIDの座標を取得したりするには、findPointerIndex()メソッドを使って、IDからポインタインデックスを引き出す必要があります。
インデックスとIDの違い ポインタインデックスは常に0から始まり、他のポインタが増減する度に再割当てされます。一方でポインタを識別するIDは、指が触れたときに割り振られ画面に触れている限りその値は変わりません。
例えばこんな感じになります。
インデックス0 ID0 人差し指 インデックス1 ID1 中指 インデックス2 ID2 薬指 ↓この状態で人差し指を離す インデックス0 ID1 中指 インデックス1 ID2 薬指 ↓人差し指でタッチする インデックス0 ID0 人差し指 インデックス1 ID1 中指 インデックス2 ID2 薬指
ポインタIDとポインタインデックスの値は、指を押した順番と反対に離す分には一致したままですが、押した順番とは異なる離し方をすると値がズレます。
ヒストリー タッチイベントはリアルタイムに配信されるわけではありません。
開発者向けオプションでポインタの位置を表示するようにすると、ポインタの軌跡がそのまま表示されますが、onTouchEvent()にMotionEventが配信される間隔はマチマチです。例えばgetX()で取得できる座標は飛び飛びになってしまいます。
手書きの文字を描画しようと思うと、getX()メソッドだけを使っていると、描画処理の分MotionEventが配信される間隔が空いてしまい、描画できる線がカクカクしてしまうことでしょう。
しかしちゃんとMotionEventには、前回onTouchEventに配信されてから今回配信されるまでの間に記録している情報が格納されて配信されています。
getHisorySize()を使うことで、以前のonTouchEventが呼ばれてから今回のイベントが呼ばれるまでに、いくつのイベントを保持しているかが分かります。
ヒストリー情報を使ってポインタの情報を取得するには、getHistoricalX()といったメソッドを利用することになります。
アクション タッチイベントの種類(触れたのか、離したのか、動かしたのか)はgetAction()で取得できます。
しかしgetAction()で取得できる情報は、ポインタのインデックスとポインタごとのアクションがごちゃまぜになった情報になります。例えば2本指同時押しだとgetAction()では261という数字が返ります。ちなみに1本でタッチすれば0です。
これはgetAction()がアクションの発生したポインタインデックスと、ポインタインデックスごとのアクションを全てまとめた値を取得するメソッドだからです。
getActionIndex()でアクションが発生したポインタのインデックスが分かります。
getAcitonMasked()は動作を表す純粋なアクションだけを返します。
つまりタップ(MotionEvent.ACTION_DOWN)を検出したい場合、マルチタッチを考慮するとgetActionMasked()を使う必要があるということです。getAciton()では二本指での同時押しを検出できない可能性があります。
座標 座標はgetX()でX座標、getY()でY座標を取得できます。
引数にポインタインデックスを渡すことで、指定したポインタインデックスの示す座標を取得できます。引数を省略した場合には、インデックス0の座標が取得できます。
getRawX()やgetRawY()と、getX()やgetY()の違いは、どこを基準とした座標数値が取得できるかです。
getRawX()などは座標の補正を行わない、端末のスクリーン上の座標を示します。スクリーンの左上をX=0,Y=0とした座標になります。Raw座標はポインタインデックス0のものしか取得できないみたいです。
対してgetX()はMotionEventを受け取るViewの左上をX=0,Y=0とした座標に変換されます。
サイズ getSize()でサイズが取得できます。
このサイズは何かというと、多分タッチパネルが認識しているタッチの範囲とでも言いましょうか、指のサイズみたいなイメージです。
指の触れる範囲を増やしていくとサイズも大きくなります。
圧力 getPressure()で圧力を取得できます。
感圧式のタッチパネルならそのまま圧力(どれくらいの強さで押しているのか)が分かるのだと思います。
静電気を検出する静電容量方式タッチパネルでも値は変動しますが、純粋な意味での圧力を示しているわけではありません。指の触れている範囲が大きくなれば圧力も大きくなるみたいです。
ツールタイプ 指で触れているのか、スタイラスなのかというのが、getToolType()を使うことで検出できます。
しかしこの情報でスタイラスを識別するには、当然ながらスタイラスが端末に「自分はスタイラスである」と情報を送信している必要があります。
スタイラスを識別する万能メソッドではないことは注意が必要でしょう。少なくとも端末とペアリングするタイプのスタイラスでないと、ダメだと思います。
試していませんが、Bluetoothのマウスを端末にペアリングして使うと、これでマウスのポインタが識別できるのかもしれません。
タッチイベントを確認するサンプル ActivityであればonTouchEventをオーバーライドすればタッチイベントを受け取ることが出来ます。例えばこんなコードを利用することでタッチイベントを確認することが出来ます。
public class MainActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.
Android Testing CodelabはEspressoなどのテストツールの使い方を学べるサンプルです。
全篇Englishですが、大体雰囲気でわかるレベルだと思います。
このレッスンをやれば、アプリ開発におけるテストツールの使い方、
JUnitを使ったユニットテスト(Andoridの端末を必要としないテスト) Espressoを使ったUIテスト(実機orエミュレータを必要とするテスト) が学べます。
ちょっとお得だなと思ったのが、アプリがMVPパターンで作られていることです。テストツールの使い方の勉強のついでに、MVPパターンも学べるなんて一石二鳥だな、なんて思ったのでちょっとやってみました。
思っていたよりもざっくりとした解説なので、雰囲気をつかめるものくらいに考えるといいと思います。
それでもテストのやり方よく分かっていない私からすると、学びの多いレッスンでした。
パッケージを機能で分けるやり方もある このサンプルではパッケージをレイヤーごとではなく機能ごとに分けてありました。(機能ごとというよりはActivityごとに近い分け方だと思いましたが)
私はこれまでずっと、ModelはModelパッケージに、というレイヤーごとにパッケージを分けていましたが、「機能ごとに分けた方が見やすくていいだろ」と書かれていて目からうろこでした。
ProductFlavorを使ったクラスの切り替え テストのためにモック用のクラスに差し替えるやり方の解説があります。
XMLのリソースファイル(strings.xmlなど)は異なるFlavorで同一のリソース名が存在した場合、Flavorのものが優先されmainで定義したリソースは上書きされます。
一方でJavaのクラスだと挙動が異なり、同一名のクラスが存在するとエラーになります。そのため、全てのFlavor共通で利用するクラスだけをmainに配置し、切り替えが必要なクラスはFlavorのディレクトリに配置するという工夫が必要になります。
モックを利用したJUnitのテスト Mockitoを使ったJUnit4のテストのやり方が勉強になりました。これは単純に私がMockitoの使い方がよく分かっていなかったからですが。
@Mockアノテーションに寄る初期化とか、ArgumentCaptorの使い方とか。
EspressoによるUIテスト Espresso-IntentsによるIntentのモック方法、Espresso-Contribを使ったナビゲーションドロワーのUIテストなどが紹介されています。
Espressoテストの章になると、一部修正が必要な部分がありましたが、それもまた勉強になりました。(EditTextへ文字入力をエミュレートした後は、croseKeyboard()しないとエラーになるとか、上へボタンを参照する部分が英語以外の環境だとエラーになるとか)
Android StudioはビルドツールにGradleを使っているので、ライブラリはbuild.gradleのdependenciesに書くことで簡単に取り込むことが出来ます。
しかし、ライブラリによってはjarファイルで配布されているものもあります。(この例ではNiftyのMobile backend)
jarで配布されるライブラリを組み込む手順は簡単です。app/libsディレクトリにjarファイルを置くだけで完了です。
これはapp/build.gradleにて、libsディレクトリにあるjarファイルをビルド時にコンパイルするよう指定されているからです(compile fileTreeの部分)。
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.+' compile 'com.android.support:design:23.+' } libsディレクトリなんかない、という場合、プロジェクトビューがAndroidになっている可能性が考えられます(デフォルトではAndroidになっています)。この場合、Projectに表示を切り替えることでディレクトリ階層が表示されるようになるはずです。
それでも見つからなければappディレクトリの下にlibsディレクトリを作成し、app/build.gradleにcompile fileTree(dir: 'libs', include: ['*.jar'])を追加すれば組み込めると思います。
Udacityをご存知でしょうか。動画を見ながらAndroid開発(だけじゃありませんが)の勉強ができるサイトです。
入門的な内容から高度な内容(アプリのパフォーマンスを向上させるとか)まで幅広く学ぶことができます。しかも無料で。
私は今この講座を勉強中です。動画に日本語字幕がついているありがたいコースです。Udacity – Developing Android Apps
私がUdacityの動画で勉強している理由はいくつかあります。
無料で見れる(有料の講座もあるので注意) Googleが提供しているので品質が保証されている ちゃんとまとまった1つのコンテンツとして勉強したい ついでに英語の勉強にもなりそう すでにAndroid Development for BeginnerやMaterial Desgin for Android Developersなどを受講しましたが、「そういうふうにやるんだ」という新たな発見もあって面白いです。(そんなん知っとるわ、ということもまた多いですけど)
動画で「こんなふうにやるんだよ」と教えるだけでなく、GitHubにあるサンプルコードを元に自分でAndroid Studio使いながら実践する内容もあるので、無料といえどかなり本格的です。
Udacityのコンテンツは全て英語です。ですが扱っている内容はプログラミングなので、雰囲気でなんとか進めていくことはできると思います。言ってることはよく分からなくても、コードを見ればなんとなく理解できると思います。
英語の勉強になりそうというのも意外とバカにできない重要な要素だと思います。少なくとも私は、Udacityで勉強するようになってからというものの、英語に対する苦手意識が薄らいできたように思います。おかげでAndroid Developersの情報を原文で読むのが自分の中で普通になってきたりしてます。
ただし抵抗感が薄らいだだけであって、英語ができるようになっているわけではありませんけどね。しかしそれだけのことでも、こと英語に関しては充分な進歩だと思います。英語も勉強したいなぁなんていう人には、Udacityを利用するのがついでにAndroidの勉強もできるおすすめな方法じゃないかなと思います。
Android Studioは常に最新を追いかけるんだ、なんてやっていた私ですが、最近ではそれもやや保守的になりつつあります。その理由の一つに、アップデートでプラグインを入れなおさないといけないというのがあります。
例えば1.4から1.5にバージョンを上げると、今まで使っていたAndroid Studioのプラグインは入れなおしになります。設定項目は引き継いでくれるのに、なぜプラグインは引き継いでくれないのか不思議です。マイナーバージョンアップによってプラグインが誤動作する可能性を考慮して、プラグインだけは引き継がないようにしてるのでしょうか?
Android Material Design Icon Generator GoogleのMaterial Design Iconをプロジェクトに取り込むのに便利なプラグインです。
ちなみに余談ですが、Android Studio 1.4からVector Assetなる機能が追加されています。Vector Assetを使うとMaterial Design Iconを取り込むことができるのですが、これを使うためには条件があります。minSDKを21以上にするか、それができない場合はgradleのandroid pluginのバージョンを1.4以上にする必要があります。SDK21未満だとvectorを扱うことができないので、これをpngファイルとして書き出してやる必要があるのですが、それをするためにはandroid plugin 1.4以上が必要なのです。ちなみにgradleのandroid plugin1.4以上(これを書いてる時点では最新が1.5)はbeta版であり、気軽には導入できません。
結局Material Designのアイコンを取り込もうと思うと、こちらのプラグインを利用するのが楽なのです。
Dash とりあえず入れてる系プラグイン。そもそも私自身がDashを使いこなせていないのが問題。
Fabric for Android Studio Crashlyticsを導入するのに使っているというか、それ以外の使い方をよく知りません。発生したcrashなどを確認するのに便利なんでしょう、たぶん。
私の場合、アプリを作ったらそのまま放置してしまっているので、いかんなぁ、改善せんとなぁと考えているところです。たぶん、今後お世話になる機会が増えていくプラグインだと思います。
Genymotion Android StudioからGenymotion使ったエミュレータを起動するのに使うプラグインです。
しかし最近は実機でデバッグすることが多いので出番があまりありません。動作の軽快なエミュレータといえど、貧弱な私の開発環境では実機で確認した方が早いんですよね。久しぶりに起動したらGenymotionがちゃんと動かないという状態になっていて、余計に出番が遠のいてしまいました。
IdeaVim Android StudioでVimキーバインドを有効にするプラグインで、私の中でなくてはならないプラグインの1つです。
なんでもVimでやっちゃうようなバリバリ使いこなしてる人だと物足りなさがあるのかもしれません。しかし、私はなんちゃってVim使いなのです。カーソルの移動(行末に飛ぶとか)、次の行から入力を始めるとか、インデントを整えるとかいう操作をVimでやる程度です。
その程度の使い方ではありますが、これをvimでやると何が便利かといえば、Android Studioのキーボードショートカットを覚えなくてもすむことにつきます。それぞれのエディタ、IDEごとにショートカットを覚えるのは面倒くさい(というか覚えられない)ので、私はいつもVimキーバインドで代用するわけです。
Xcode、Visual Studio、MonoDevelop(Unity)などでもVimキーバインドで使おうとしましたが、それらに比べるとAndroid Studioの(というかInteliJの)Vimプラグインは素直で非常に使いやすい印象です(エディタとの競合が少ない、再現度が高い)。
Markdown Markdownを書くプラグイン。特にこだわりがあって入れているわけではなく、一番ダウンロード数の多いものをとりあえず入れただけです。Markdownで書いて、HTMLでプレビューができます。
そもそもあまり使用頻度が高いわけではなくて、GitHubのREADME.md書いたり、ストア掲載文の草稿を書いたりするのに使う程度です。プレビューなくても問題ない使い方してるので、別に入れなくてもいいんじゃないかと思わなくもないです。