AndroidはViewのイベント配信メカニズムを分析する
9298 ワード
Android開発者にとってViewイベントの配布メカニズムは理解せざるを得ない.イベント配信メカニズムを知らないと多くのカスタムコントロールが書けない.今日は、Viewのイベント配信メカニズムがどのように実現されているかを見てみましょう.
まず栗を見てみましょう.
MyButtonをカスタマイズしてButtonを継承し、dispatchTouchEventとonTouchEventメソッドを書き換える
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "dispatchTouchEvent ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchEvent ACTION_UP");
break;
}
return super.onTouchEvent(event);
}
xmlで呼び出す
MyButton mButton= (MyButton) findViewById(R.id.my_bt);
mButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouch ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouch ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouch ACTION_UP");
break;
}
return false;
}
});
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick");
}
});
mButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Log.e(TAG, "onLongClick");
return false;
}
});
クリックするだけでログが表示されます
08-14 11:47:38.173 15712-15712/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_DOWN
08-14 11:47:38.173 15712-15712/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_DOWN
08-14 11:47:38.174 15712-15712/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_DOWN
08-14 11:47:38.199 15712-15712/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_UP
08-14 11:47:38.199 15712-15712/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_UP
08-14 11:47:38.199 15712-15712/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_UP
08-14 11:47:38.200 15712-15712/fun.hxy.com.testalg E/MainActivity: onClick
時間はdispatchTouchEventから始まり、onTouch->onTouchEvent->onClick
どうしてこんなことになったの?Viewのソースコードを見てみましょう.
public boolean dispatchTouchEvent(MotionEvent event) {
1 ....
2 if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
3 if (!result && onTouchEvent(event)) {
result = true;
}
.....
return result;
}
ViewのdispatchTouchEventでは、Viewがliを先に判断していることがわかります.mOnTouchListener.onTouch(this,event)はまずonTouch()イベントを3行目のonTouchEvent()に実行し、onTouch()の戻り値がtrueを返すとonTouchEvent()メソッドを下に実行しません.
onTouchEvent()のソースコードを見てみましょう.
public boolean onTouchEvent(MotionEvent event) {
......
switch (action) {
case MotionEvent.ACTION_UP:
if (!focusTaken) {
if (!post(mPerformClick)) {
performClick();
}
}
break;
}
return true;
}
return false;
}
onClickの方法はonTouch()の方法のupの中で実行したことを発見した.その理由は、ビューがクリックしたときにonTouch()->onTouchEvent()->onClick()を呼び出し、面接でよく聞かれるからです
長押事件を見てみましょう.
buttonボタンを長押しします
08-14 14:16:16.725 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_DOWN
08-14 14:16:16.726 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_DOWN
08-14 14:16:16.726 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_DOWN
08-14 14:16:16.875 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE
08-14 14:16:16.875 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_MOVE
08-14 14:16:16.875 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_MOVE
08-14 14:16:16.890 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE
08-14 14:16:16.890 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_MOVE
08-14 14:16:16.890 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_MOVE
08-14 14:16:16.906 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE
......
08-14 14:16:17.142 11084-11084/fun.hxy.com.testalg E/MainActivity: onLongClick
08-14 14:16:17.189 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_MOVE
08-14 14:16:17.190 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_MOVE
08-14 14:16:17.190 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_MOVE
.....
08-14 14:16:17.948 11084-11084/fun.hxy.com.testalg E/MyButton: dispatchTouchEvent ACTION_UP
08-14 14:16:17.949 11084-11084/fun.hxy.com.testalg E/MainActivity: onTouch ACTION_UP
08-14 14:16:17.949 11084-11084/fun.hxy.com.testalg E/MyButton: onTouchEvent ACTION_UP
08-14 14:16:17.954 11084-11084/fun.hxy.com.testalg E/MainActivity: onClick
長押しするとACTIONが実行されることに気づきました.DOWNは指を押すと少しブレてしまう可能性があるので、不等次のACTION_を実行しますMOVE、それから先にonLongClickを実行した後に手を放してonClickを実行する.
onTouchEvent()のソースコードをもう一度確認します.
public boolean onTouchEvent(MotionEvent event) {
switch (action) {
case MotionEvent.ACTION_UP:
......
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
if (!post(mPerformClick)) {
performClick();
}
}
......
break;
case MotionEvent.ACTION_DOWN:
......
checkForLongClick(0, x, y);
......
break;
}
return false;
}
ACTIONでDOWNの場合、システムはcheckForLongClickメソッドを実行します
private void checkForLongClick(int delayOffset, float x, float y) {
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
}
このcheckForLongClickでは遅延操作が500ミリ秒遅れるとCheckForLongPressという遅延イベントが実行されます
@Override
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
}
}
この方法は実行された
public boolean performLongClick(float x, float y) {
mLongClickX = x;
mLongClickY = y;
final boolean handled = performLongClick();
mLongClickX = Float.NaN;
mLongClickY = Float.NaN;
return handled;
}
performLongClickというメソッドの戻り値がbooleanタイプであることが分かるので、onLongClickListenerメソッドの戻り値をtrueに変更すると、mHasPerformedLongPressがtrueに設定、mHasPerformedLongPressがfalseの場合のみperformClick()メソッドが実行されることが上からわかる.
さて、ここまでViewのイベントメカニズムは悪くないので、一度見てみましょう.Viewのイベントメカニズムは簡単です.次の編では、View Groupのイベントメカニズムを皆さんと一緒に勉強します.みんなが何か収穫があることを望んでいます.