Bluetooth経由でデバッグする

Android WearデバイスをBluetooth経由でデバッグする方法について。 Android WearもUSB経由でパソコンに接続してデバッグする方が何かと便利です。ですが、USB経由で接続しようと思うと、Wearデバイスに直接USBケーブルをつなぐタイプのものなら問題無いでしょうが、クレードル経由で接続するタイプの製品だと腕につけた状態でデバッグできません。 そんなときはBluetooth経由でデバッグすると便利です。 https://developer.android.com/training/wearables/apps/bt-debugging.html Android Wearでの事前準備 Bluetooth経由でのデバッグを有効化しておく必要があります。 まず設定→端末情報→ビルド番号を7回タップして開発者オプションを有効にします。 すると開発者オプションを選択できるようになるので、そこからADBデバッグとBluetooth経由でデバッグを有効にします。 以上でWear側の事前準備はOK。 スマホ側での事前準備 Android Wearとペアリングしているスマホ側でもBluetooth経由のデバッグを有効化してやる必要があります。 Android Wear companion app(日本語だと単にAndroid Wear)を実行します。Android Wearとのペアリングしたりするアプリです。 使いたいAndroid Wearデバイスとのペアリングした状態で、Appbarにある歯車アイコンを押します。 すると設定画面が開くので、その一番下にあるBluetooth経由のデバッグを有効にしてやります。 ホスト:未接続、ターゲット:接続済みとなっていると思います。 それができたら次のステップ。 パソコンからターミナルで操作 以下のコマンドを実行。 adb forward tcp:4444 localabstract:/adb-hub adb connect localhost:4444 adb connect localhost:4444でConnection Refuesedとなってしまう場合、localhostの部分を127.0.0.1とすれば接続できると思います。 接続できればスマホで「ホスト:未接続」となっていた部分が「ホスト:接続済み」となると思います。 そうすればAndroid StudioからAndroid Wearデバイスが見えるようになっていると思います。 切断 adb disconnect 127.0.0.1:4444 adb forward —remove tcp:4444 ちなみにポートフォワーディングしているかどうかを確認するにはadb forward —listで確認可能。 もっとも、スマホのUSBケーブルを抜くとそのままBluetooth接続も解除されるので、わざわざ上記コマンドを叩いて切断する必要はまったくありません。 注意点 Bluetooth経由のデバッグでも、デバッガでブレークポイントを設定したりステップ実行したりすることができます。ただし、USB経由でのデバッグと比較すると格段に遅いです。 アプリのインストールもBluetooth経由で可能ですが、やっぱりUSBで繋いだ時と比べると遅いです。 ケーブルレスでデバッグできるのは便利なのですが、可能であればUSBで実機をつないでデバッグした方が開発サイクルを早く回すことが出来ると思います。 Bluetooth経由のデバッグは、Wearを腕にはめた状態でないと出来ない動作の確認(センサーを使った動作のデバッグ)などに限定して使ったほうがいいと思います。 ちなみにBluetooth経由のデバッグを有効にした状態で、WearをUSBで直接パソコンにもつないでおくと、WearデバイスはBluetoothで接続したものとUSBで接続したもの2つが見える状態になります。

久しぶりにAndroid Wearの電源を入れた

私はLG G watch Rを持っていたのだけれども、長いこと使っていなかった。普段腕時計をしないし、するにしても薄くて軽いやつをずっと使っていたので、スマートウォッチの大きさが我慢できず、そのうちつけるのをやめた。

そんな放置していたスマートウォッチを久しぶりに使ってみた。

Read full post gblog_arrow_right

Contextの使い分けについて私が辿った変遷

Application Contextを渡してはいけないという記事を読みました。 https://ytrino.hatenablog.com/entry/2016/05/26/033936 この記事を読んで改めて自分のContextに対する認識があまいことを実感したので、思ったことをまとめてみようと思います。 趣旨は「僕はContextに対してこういうふうに思ってるんだけど、違う・違わないが自信持ててないから誰か突っ込んでくれ」です。たぶんそんなに間違ってないと思うんですけど、相談できるような相手がいないんだ、察しておくれ。 「私もそういう認識です」と言ってもらえれば自信になるし、違う部分があれば指摘をもらって学びの機会になり私のためになります。また、こういう変遷を経て学んできたという情報が初学者の役に立てばなぁなんて思ってもいます。 私のAndroidレベルは初学者というわけでもないですが、Contextにまつわる話を聞くと理解が曖昧でモヤモヤするレベルです。上記記事に倣えばAndroid2級でしょうか。 私は最近でこそライフサイクル考えてContextを使い分けるようになりましたが、ちょっと前まで「困ったらApplication Context渡しとけば問題ない」という考えの人でした。前までというか、今もそういう部分があるんですけどね。だからこそ「Application Contextを渡してはいけない」というのを見て不安になってしまいました。 渡してはいけないの意味 Application Contextを渡してはいけないというのは、どういう意味なのでしょうか。 アプリがクラッシュするから渡してはいけない 表示がおかしくなるから渡してはいけない 渡しても動作上は問題ないが、コードの見た目上・意味的に美しくないので渡してはいけない 実はApplication Contextをなるべく使おうというのも、Application Contextを渡してはいけないというのも、あるコンテキストにおいては間違ってないんじゃないかなと思います。それぞれ違うコンテキストで話しているので、そこを混同すると混乱してしまいます。 前者は「メモリリークを回避する」という文脈での話で、Activityのリークを防ぐにはApplication Contextを使えばいいのは正しい(はず)です。 一方後者はthemeの適用の観点での話で、Application Contextでは意図した動きにならない・変にハマることがあるからApplication Contextを安易に使ってはいけないという話で、これはこれで正しいわけです。 どっちも正しいのですが、残念ながらコードを書いていてContextを要求された時に、私たちは何らかのContextを渡さないと先に勧めません。何を渡せばいいかは「ケースバイケースなんで一概にどれを使えとは言えない」んで、Contextの要求に対しては、それぞれ適切なContextを渡す必要があります。 ではどのようにしたら、何を使うのが適切かを判断できるのでしょうか。ライフサイクルを考えろということでしょうか。そのライフサイクルはどうやって判断するんでしょうか。 私はその判断を、今はソースコードを読むことで行っています。Contextを要求するメソッドが、クラスが、いったい渡されたContextをどのように利用しているのか。それをもって何を渡すか考えています。 Contextについて私の辿った道 Androidを学び始めた当初は、Contextなんて意識していませんでした。本やサンプルコードの写経する分には意識しないですみますから。 しかし写経から脱し、自分でコードを書き始めるとそうもいきません。こういう処理を実装したいな→このメソッド使えばなんとかなりそう→Contextが必要らしい。はたとキーを打つ手が止まります。 そのメソッドを使うにはContextを渡すしかない。渡さないと先に進まない。とりあえずthisって書いたら動いた。よし、これで先へ進める。なんていうのが、Androidを学び始めた人が辿る道ではないでしょうか。私はそうでした。 学び始めの頃はActivityからなんかすることが多いので、this、すなわちActivityを渡していればとりあえずは動いてくれました。私はそうして「ああ、ContextっていうのはActivityを渡せばいいのだな」と認識するようになりました。 しかしActivity Contextを渡してしまうとメモリリークが発生するぞという話を耳にします。そこで登場するのがApplication Context。getApplicationContext()を使えばメモリリーク対策になるという話を聞いて、「ああ、じゃあとりあえずApplication Contextを渡しておけば問題ないのか」となりました。 しかし今度はApplication Contextを渡すと想定通りにViewが表示されないことがあるという。無思考にApplication Contextを渡すのもどうやらダメらしい。 一周回ってきてしまいました。もう一体どうしろと。このメソッドContext要求してくるのに僕は一体何を渡せばいいんだ。 私のContextとのつきあい方 そんな私が多少なりとも指針を持てるようになったのは、一言で言ってしまえば慣れてきたからなんだと思います。 とりあえずコード書いて、動かしてみて。それを繰り返すうちに慣れてきて。ソースコードを読むようになって「多分こうやれば大きく間違ってはいないはず」という、そんな指針を持てるようになりました。 Contextを要求されたら何を渡すか。重要なのは考え方、つきあい方だと思います。これが今回の記事の本題です。 私はまずライフサイクルを考えるより前に、Contextを要求するメソッドのソースコードを確認しに行きます。そいつがContextへの参照を保持するのか、それともContextを使ってなんかするだけなのか。それを確認するのです。 Contextへの参照を保持するなら、Activity Contextを渡していいのか、ライフサイクルを考えなければなりません。ライフサイクルを考えるというよりは、Activityとともに役目を終えるのか、そうでないのかで判断していますが、正直このあたりは曖昧です。 該当のメソッドが単にStringリソースを読みだすのに使っているとかだったら、別にActivity Contextを使っても問題ないですよね。そもそもActivityがリークするのは、Activityへの参照を持ち、渡した先のオブジェクトがActivityより長生きする場合だからです。 Contextへの参照を持たないのであれば、別にどっちを使おうが問題ないので、好きな方使えばいいじゃんって感じですが、Activityから呼び出すのであれば、わざわざApplication Context渡すのも大げさなので、Activity Contextでいいかと思います。 Androidと付き合っていくうちに、なんとくこんな感じに行き着きました。それでもメモリリークが怖いなら、Contextに何を使えばいいか迷うよりも、それでメモリリークするのかを確認する方に力を注いだほうが良いと思います。 でも、独学でやってるとこれで本当に問題ないのか確証が持てないので、いつもどこかで不安です。誰かに聞く機会もなくてつらい。 見えないメモリリークの影 そもそもContextについて意識し始めるキッカケは、私の場合はActivityのメモリリークです。 このメモリリークも私にとってはContext以上によく分からない存在でした。最初のうちはメモリリークしているかどうかをどうやって確認すればいいのかがわからなかったからです。確認方法が分からないから、メモリリークしないコードを書かなきゃという気持ちだけが肥大化していました。 そんな中で「とりあえずApplication Context渡しておけばライフサイクルの関係でActivityがリークすることはない」という1つの指針は、私にとっては心強かったです。 実際にはApplication Contextを渡すからメモリリークしなくなるわけではないというのは、今であれば分かります。しかしはじめの頃はそんな違いも分からなくて、そんなことよりContextとして何を渡せばいいのかという指針があることがありがたかったのです。 これに関しては、メモリリークしているかどうかを確認する方法を学ぶ方が大切だと、今であれば思いますけどね。当初は確認方法がわからないけど、メモリリークしたら困るからApplication Context渡しとけっていう感じでコード書いてました。 初学者に向けてステップアップのために 初学者がもっとも優先すべきはとりあえず先に進むことだと思います。動くアプリを作ることです。そしてAndroidに慣れることです。 Contextを要求するメソッドに対して、何らかのContextを渡す。Activity Context渡すのかApplication Context渡すのか迷うと思います。最初のうちはとりあえずどっちか渡して動いてればいいと思います。動けば正義です。迷って足を止めるより、いっぱい作ってAndroidに慣れる方が大事だと思います。 慣れてきたらContextを渡すときに「こいつはActivityより長生きする奴なのかな」と考えましょう。そんなときはソースコードを読むことが助けになると思います。 Contextを要求するメソッドは、渡されたContextをどのように使っているのか。そいつもContextが必要なメソッドを呼び出すために必要としているのか。それともContextへの参照を保持するのか。 Contextに何を使うべきなのかはケースバイケースで一概には言えません。Activity Contextじゃないとダメな場合もあれば、Application Contextの方が適切な場合もあり、はたまたどっち渡しても問題ない場合だってあります。・・・なんでこんなややこしいことになってるんでしょうね? とりあえず、動けば正義でトライアンドエラーを繰り返してAndroidに慣れる。慣れてきたらソースコードを読んでみて、Contextがどう使われているかを気にしてみる。そうやってステップアップしていくしかありません。 私のContextとの現在のつきあい方 長くなったので最後にまとめ。 私はActivity Contextを基本的に使うようにしていて、利用先でContextへの参照を持つ場合にはApplication Contextの利用を考える、というような感じでやってます。 Activity Context使っているのはThemeのためとかじゃなくて、単にgetApplicationContext()を書くと長くなるからとかそんな理由です。明らかにActivityのライフサイクルより長生きするオブジェクトに対しては、Application Context渡していますけれども。 正直な所、メモリリークをおそれてContextにどれを使うか悩むより、メモリリークの確認の仕方を調べて、リークしていないかを確認することに力入れたほうが建設的なんじゃないかなと思います。 何を使うのが適切か迷ってまごまごする前に、どう使われているのかをまず確認する。Contextの使い分けに関してはこれが私の指針なんですが、そんなに大きく間違ってはないですよね?

画面をタッチして線を描く お絵かきアプリを作るための第一歩

今回の記事のサンプルコードは、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次ベジェ曲線を描く この方法では、正確にタッチした通りの線が描けるわけではないのですが、比較的簡単な処理でカクカクしない線を描くことができます。
Read full post gblog_arrow_right

DataBindingを試す

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.
Read full post gblog_arrow_right

タッチイベントについて

端末の画面をタッチした情報は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.
Read full post gblog_arrow_right

Android Testing Codelabやってみた

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()しないとエラーになるとか、上へボタンを参照する部分が英語以外の環境だとエラーになるとか)

UdacityでAndroid開発の勉強(ついでに英語も?)

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の勉強もできるおすすめな方法じゃないかなと思います。

パララックスイメージのAppBarをListViewを使って実装しようとしてハマった話

AppBar(Toolbar、ActionBar)の部分が大きめの画像になっていて、コンテンツをスクロールするとそれに合わせて画像が縮んでいき、最終的にToolbarだけが残る(もしくは全部隠れる)みたいなデザインがありますよね。あれを実装しようと思って試行錯誤してみました。 試行錯誤になってしまった原因は、スクロール可能なコンテンツ部分を横着してListViewで作ってしまったからでした。見かけるサンプルはだいたいRecyclerViewを使っていたのですが、使ったことがないため使い慣れているListViewでやろうとしたのが間違いでした。 ListViewで実装すると、ListViewをスクロールしてもAppBarは連動して動いてくれません。AppBarの部分をスクロールすると伸縮してはくれますが、巷にあふれるパララックスAppBarはこんな残念な動きはしていません。 コードで何か手を加えないといけないのだろうかと調べるうちに、なぜListViewではAppBarが連動して動かないのか原因が分かりました。今回はそのお話です。 Patterns– Scrolling techniques layout.xmlの設定 基本的にパララックスなAppBarを実装するには、レイアウトXMLの記述のみで実装できます。 サンプルコード – GitHub このMaterial Design(Android desgin support library)による階層構造を初めて見ると、なんだかややこしく感じてしまいますが、1つずつ紐解いていけばそう難しい構造ではありません。 正確にはandroid.support.design.widget.〜とFQCN(パッケージ名を含めたクラス指定)になりますが、ここでは長くなるので省略しています。 CoordinatorLayout ├AppBarLayout │└CollapsingToolbarLayout │ ├ImageView │ └Toolbar ├ListView(などスクロール可能なコンテンツ) └FABなどお好みで 基本的にXML上でちゃんと必要な指定さえ行えば動きます。コードは不要です。 CoordinatorLayout 今回の例ではListViewのスクロールにあわせてAppBarLayoutを伸縮させるために存在しています(FABをToolbarとListViewの中間に配置する役割も担っていますが)。このCoordinatorLayout自体は内包したView同士を連携させたりする単なる入れ物です。全然「単なる」ではないですけど。 AppBarLayout AppBar部分のLayoutを管理するコンテナで、AppBarの部分に表示するViewをこの中に入れてやります。Blank Activityを作成すると、この中にはToolbarだけが入っていると思います。 ここではAppBarの高さを指定してやります。android:layout_height="192dp"。 CollapsingToolbarLayout 折りたためるToolbarのための入れ物です。スクロールによるAppBarの動き方を指定することができます。ここではapp:layout_scrollFlags="scroll|exitUntilCollapsed"と指定しています。 ImageView AppBarが全開のときに表示されるイメージ画像です。コンテンツのスクロールに合わせて縮み、最終的にToolbarだけが残ります。ここではapp:layout_collapseMode="parallax"を指定しています。 Toolbar Toolbarです。ここではapp:layout_collapseMode="pin"を指定しています。この指定でToolbar自体は隠れずに残ります。 ListView よく見かけるサンプルではRecyclerViewやNestedScrollViewが利用されています。しかし私はRecyclerViewの使い方がよくわからなかったのでListViewで代用しています。 ここでは必ずapp:layout_behavior="@string/appbar_scrolling_view_behavior"の指定が必要です。 この@string/〜はAndroid Support Libraryのstringリソースを参照していて、その中身はandroid.support.design.widget.AppBarLayout$ScrollingViewBehaviorとなっています。つまりこのListViewの振る舞いとして、AppBarLayoutのScrollingViewBehaviorを指定しているわけです。 ListViewを使うと、そのままではListViewがスクロールされるだけでAppBarが伸縮しません。ListViewのスクロールと連動させるためには、ListViewにandroid:nestedScrollingEnabled="true"を指定する必要があります。 なぜか。スクロール可能なコンテンツとAppBarの伸縮を連携させるためには、ListViewがスクロールされたということをCoordinatorLayoutに伝える必要があります。RecyclerViewやNestedScrollViewは標準でこれをやってくれるわけですが、ListViewは何もしません。そこでCoordinatorLayoutにスクロールイベントを通知するための設定を有効にしてやる必要があるのです。 android:nestedScrollingEnabled="true"(NestedScrollに関する処理)はAPI21以上のViewに実装されています。 余談:なぜAppBarが動くのか 仕組みを完全に理解したわけではないので、ざっくりとした説明です。 ListViewの上でスクロールを行うと、ListViewの中身がスクロールされます。これはListViewのonTouchEventで処理されています。これだけではListViewの中でスクロールイベントが処理されるだけで、AppBarの変形にはつながりません。 そこで登場するのがCoordinatorLayoutです。こいつが子Viewのスクロールと、別の子Viewを連携させるわけです。 連携させるためにはCoordinatorLayoutにスクロールイベントを通知する必要があり、その仕組がNestedScrollです。RecyclerViewやNestedScrollViewは初めからCoordinatorLayoutと連携する前提で作られていますし、ListViewなどでもSDK21からNestedScrollに関する処理が追加されています。ただし初期状態では無効化されているので、NestedScrollの処理を有効化してやる必要があり、それがandroid:nestedScrollingEnabled="true"になります。 NestedScrollの処理は子ViewのonTouchEvent(onTouchMove)でCoordinatorLayoutに伝わります。CoodinatorLayoutはonNestedPreScroll内でBehaviorが設定されている子Viewを探し、見つかったBehaviorに対してdispatchOnDependentViewChangedを呼び出します。今回の例ではScrollingViewBehaviorです。 最終的にAppBarのサイズを伸縮させる処理は、このBehaviorのonDependentViewChangedで行われているみたいです。 参考 Handling Scrolls with CoordinatorLayout