暗号化について勉強中

Androidでユーザデータを暗号化して保護するにはどうしたらいいのでしょうか。

これまで「暗号化のやり方がよくわからないから、機密情報を保存しないようにしよう」と避けてきたのですが、それにも限界があるのかなぁと思って、手を広げてみる気になったのです。

まあ本当の出発点は、ユーザデータの保護ではなく、アプリで扱うデータを秘匿するにはどうしたらいいのだろうだったんですけれど・・・。

例えば、Twitterクライアントを作るとして、そのAPIキーをどうやって秘匿するのかということです。

Androidでこの手のAPIキーを扱う場合、サンプルではJavaのコードに直接書いてありますが、実際に自分でリリースするアプリでもそのままでいいのかという話です。

AndroidではAPKを簡単に抜き出して、しかも簡単にソースコードを確認することもできます。ProGuardで難読化をしても、Javaのコードに埋め込んだ文字列はそのままです。

String api_key = "hogehoge";のapi_keyの部分はProGuardによってaとか意味のない文字に書き換わりますが、”hogehoge”の部分は書き換わりません。(書き換わったらプログラムの動作が変わってしまうので当たり前です)

つまり、やろうと思えばAPIキーは見放題ということになります。

だからここの部分、どうするのがセオリーなんだろうってのから始まって、暗号化すれば秘匿できるのかなと思ってみたのです。

多分、ユーザに見せたくないデータはアプリで保持しないのが正解であって、暗号化は関係ないと思いますが、それでも今まで放置していた暗号化に関して勉強するいい機会ではあるのでそのまま続けております。

どう勉強してるのかって話ですが、https://developer.android.com/training/articles/keystore.htmlを見たり、Android Studioのimport Sampleで取り込めるGoogleのサンプルコードを使ったりしてます。

実際に手を動かしてみて初めて知ったのですが、同じ文字列を同じ暗号化鍵で暗号化しても、得られるバイト列が毎回異なることに驚きました。”abc”を同じ暗号化鍵を使って暗号化したら、毎回123になるというイメージを持っていたものですから、なんで毎回異なるバイト配列になるのか不思議です。しかも異なるバイト配列になるのに、復号化したらちゃんと”abc”に戻るのですからなお不思議です。

これは一体どういう仕組なのか。そもそも暗号化の仕組みを理解せずに使うのは逆に危険なんじゃないのかと思い始め、今度はこんな本を読み始めました。

まあまだ読んでいる最中なんですけど面白いです。

本書には

弱い暗号は暗号化しないより危険である
という考え方が序盤で紹介されています。仕組みを理解せずに暗号化するのもまた良くないと言えるのではないでしょうか。

安定の結城先生なので非常に分かりやすく、楽しく読めそうです。

それにしても、初めはAPIキーを秘匿することがきっかけだったのに、我ながらよく脱線してきたものだなぁと思います。

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

Application Contextを渡してはいけないという記事を読みました。

https://ytrino.hatenablog.com/entry/2016/05/26/033936

この記事を読んで改めて自分のContextに対する認識があまいことを実感したので、思ったことをまとめてみようと思います。

趣旨は「僕はContextに対してこういうふうに思ってるんだけど、違う・違わないが自信持ててないから誰か突っ込んでくれ」です。たぶんそんなに間違ってないと思うんですけど、相談できるような相手がいないんだ、察しておくれ。

「私もそういう認識です」と言ってもらえれば自信になるし、違う部分があれば指摘をもらって学びの機会になり私のためになります。また、こういう変遷を経て学んできたという情報が初学者の役に立てばなぁなんて思ってもいます。

私のAndroidレベルは初学者というわけでもないですが、Contextにまつわる話を聞くと理解が曖昧でモヤモヤするレベルです。上記記事に倣えばAndroid2級でしょうか。

私は最近でこそライフサイクル考えてContextを使い分けるようになりましたが、ちょっと前まで「困ったらApplication Context渡しとけば問題ない」という考えの人でした。前までというか、今もそういう部分があるんですけどね。だからこそ「Application Contextを渡してはいけない」というのを見て不安になってしまいました。

渡してはいけないの意味

Application Contextを渡してはいけないというのは、どういう意味なのでしょうか。

  1. アプリがクラッシュするから渡してはいけない
  2. 表示がおかしくなるから渡してはいけない
  3. 渡しても動作上は問題ないが、コードの見た目上・意味的に美しくないので渡してはいけない

実は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のメモリリークです。

Read full post gblog_arrow_right

Kotlin使ってみて感じる便利さと葛藤

最近アプリや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だとそんな面倒くさいことをせずとも、文字列中に変数を埋め込めるので便利です。こんな感じに書けるわけです。

Read full post gblog_arrow_right

Android Studio用のプラグインADB Friendlyを作ってみた

以前から作ってみたいなぁとは思っていたのですが、このたびやや必要性が増したこともあってAndroid Studio用のプラグインを作りました。

作成時間の半分くらいは環境構築に手間取っていたと思います。

ソースコードはGitHubで公開していますが、クローンしてもそのままビルドしたりできるのかよく分かりません。一応自分でgit cloneして確認したりしてみましたが、Androidのプロジェクトと比べると一手間必要で面倒くさい感じです。

参考にさせていただいたプラグインはほぼほぼgit cloneしただけでは動かせなかったので、Gradleは偉大だなということを再認識しました。

ADB Friendly

ADB Friendlyという名前ですが、現状では端末の画面を回転させることしかできません。どんな感じかはYouTubeをご覧ください。

私は端末の画面を回転させながらメモリ使用量のグラフを見て、作成したアプリがメモリリークしていないかどうかを確認することがあります。グラフが右肩上がりに上がり続けていると、それはメモリリークが発生しているということです。

今までは端末を手に持って、縦横縦横・・・とやっていました。ところがつい最近、こんなものを買いました。Macbookのモニタ横に、スマホを固定するクリップです。

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を使って作りました。既にやってくださっている方がいらしたので非常に助かりました。

Read full post gblog_arrow_right

式の即時評価を利用してオブジェクトの状態を調べる

式の即時評価が便利だよねという話です。

私は以前Calendarクラスを使って日付の処理をしようとしていました。そのとき、どのフィールドを参照すれば目的の値が引っ張ってこれるかを確認するのに、愚直にLog.d()を使っていました。1つ1つメソッドの返り値を出力して(文字列の連結でさらにカオスになる)、目的の数値がちゃんと取れているのか確認していたのです。

ドキュメントを読めよっていう話なんですが、読んでもどういう値が取れるのかいまいち分からなかったんですよね・・・。

まあそんなアホなことをやっていたので、当然のようにバグを仕込んでいました。そんなバグに気づくきっかけとなったのが式の即時評価機能です。以来、とてもお世話になっています。

式の即時評価を使えば、ブレークポイントを設定してデバッグ実行するだけで、任意のメソッドや変数の確認ができるようになります。

式の即時評価

メソッド呼び出しとその結果が確認できる

ブレークポイントで一時停止させないと使えないので、状態の変化を追うのには向かないかもしれません。それでもlogcat頼みのデバッグより捗る場面があると思います。

どんなときに便利か

今日遭遇したエラーで、なんかのタイミングでNullPointerExceptionが発生してクラッシュする現象が発生しました。

特定の状況で例外発生によるクラッシュ

例外の発生する箇所はわかっているものの、どういう状況でそれが生じているのかがよく分かりませんでした。

そこで例外の発生する部分をtry-catch文で囲み、例外をキャッチした所にブレークポイントを置いて調べてみることにしました。

try-catchで止めてみる

ブレークポイントで止めればコールスタックを遡ってオブジェクトの状態を確認できますが、目当ての変数を探すのが大変なので、そういうときに式の即時評価が便利です。だと思います。

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

今回の記事のサンプルコードは、GitHubで公開しています。

お絵かきアプリを作ろうと思って格闘中です。とりあえず線を描くだけでも学びがいろいろあったのでまとめておこうと思います。

Pathを使って描画するとカクカクする問題

線を描くにはPathを使うのがオーソドックスのようですが、何も考えずにパスを使った描画を行うと、線がカクカクしてしまいます。(サンプルコードのPathPaintView)

path.lineTo(e.getX(), e.getY()); drawCanvas.drawPath(path, paint);

path.lineToによる描画

これはなぜ起こるのでしょうか。

MotionEventが配信される間隔の問題

その理由はまず線をPathではなく点で描画してみると分かります。(DotPaintView)

drawCanvas.drawPoint(e.getX(), e.getY(), paint);

drawPointによる描画

描画される点がまばらになっています。このドットは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);

getHistoricalによる描画

ドットの間隔が狭まりました。指をゆっくり動かせばキレイな線が描画できます。しかしこの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);

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.skill}"
            />

    </LinearLayout>

</layout>

ポイントはこんな感じ。

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とポインタインデックスの値は、指を押した順番と反対に離す分には一致したままですが、押した順番とは異なる離し方をすると値がズレます。

ヒストリー

タッチイベントはリアルタイムに配信されるわけではありません。

Read full post gblog_arrow_right