まさかこんな実装をしているわけがない、と思います。僕くらいです。はい。おおonTouchメソッドよ死んでしまうとは情けない。
こうすると死ぬ
常識的に考えて以下のコードは気持ち悪いですよね。でもAndroid3.0未満、つまりGingerbread以前では問題なく動作します。しかしHoneycomb以降ではjava.lang.IllegalArgumentExceptionが発生してしまいます。エラーメッセージはpointerIndex out of rangeです。なんででしょうね?!
@Override public boolean onTouchEvent(MotionEvent event) { switch(event.getAction() & MotionEvent.ACTION_MASK){ case MotionEvent.ACTION_DOWN: int x = event.getX(1); break; } }
ソースを読む
MotionEventのソースを見ると・・・。げぇ全然違うぜぇえええ。
Android2.3.3_r1
public final float getX( int return mDataSamples[mLastDataSampleIndex + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset; }
Android4.0.1
public final float getX(int pointerIndex) { return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT); }
どう変わったか
上記の4.0.1のソースではもはやnativeメソッド。さすがに実装まで追うのはしんどいんで省略しますが、2.3.3_r1では配列アクセスをしている事がわかります。この配列はMotionEventのコンストラクタで初期化されます。配列サイズはコンストラクタに渡したpointerCount, sampleCountと定数NUM_SAMPLE_DATAを乗じた数となります。
private MotionEvent(int pointerCount, int sampleCount) { mPointerIdentifiers = new int[pointerCount]; mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA]; mEventTimeNanoSamples = new long[sampleCount]; }
現タッチ数は内部でmNumPointersで管理しています。2.3.3_r1のgetX(int)では利用していません。つまりgetX(int)でアクセスするインデックスに影響を与えません。MotionEventはイベントを受け取るViewで使い回しているので(一応MotionEventのhasCode()で確認した)、コンストラクタで確保された配列をオーバーしない限りは死なない事になります。
操作 | 実タッチ数 | Gingerbread以前 | Honeycomb以降 |
---|---|---|---|
event.getX(0) | 2 | ○ | ○ |
event.getX(2) | 2 | ○ | × |
event.getX(100000) | 2 | × | × |
結論
今までギリギリ動いていたけどHoneycomb、ICSで爆死、そんな事があるかもしれませんね。怖いわー。
初心者です。よろしくお願いします。
返信削除見事にはまってしまいました。
回避策のコーティングを、わかりやすく、お教えいただければ、幸いです。
通りすがりです。
返信削除ICSから厳しくなったからマルチタッチの数(指の数を数えて)処理を追加してね。
int count = event.getPointerCount();//指の数を取得
if(count == 2 && touchMode == TOUCH_MULTI) {
・・・・
}