PS:もしあなたが努力していると感じているなら、あなたの努力の結果は何か教えてください!
前の記事では、Android のイベント分配の大まかな流れについて説明しました。以下では、Activity、ViewGroup、View の 3 つの側面からイベントに関連するメソッドを紹介します。小節は以下の通りです:
- Activity
- ViewGroup
- View
Activity#
Activity には、主にイベント伝播に関連する 2 つのメソッド、dispatchTouchEvent () と onTouchEvent () があります。イベントの伝播は Activity の dispatchTouchEvent () メソッドから始まります。
イベント分配#
Activity のイベント分配メソッド:dispatchTouchEvent ()、そのソースコードは以下の通りです:
//イベント分配
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//空のメソッド
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//onTouchEvent()メソッドはデフォルトでfalseを返す
return onTouchEvent(ev);
}
上記のコードでは明らかに ACTION_DOWN イベントのみを処理しており、ACTION_DOWN イベントがイベントの分配を引き起こすことを示しています。次に、Window クラスの superDispatchTouchEvent (ev) メソッドが呼び出されます。これは抽象メソッドであり、このメソッドが呼び出されると、具体的なサブクラスのメソッドが呼び出されます。Window クラスの具体的なサブクラスは PhoneWindow クラスであり、その中の具体的な実装の superDispatchTouchEvent (ev) メソッドは以下の通りです:
//Windowクラスの抽象メソッド
public abstract boolean superDispatchTouchEvent(MotionEvent event);
//WindowサブクラスPhoneWindowクラスのsuperDispatchTouchEvent()メソッドの具体的な実装
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
明らかに、ここで mDecor.superDispatchTouchEvent (event) が呼び出されています。mDecor は PhoneWindow.DecorView オブジェクトであり、これがウィンドウの最上位ビューです。これは本当の Activity のルートビューであり、FrameLayout を継承しています。super.dispatchTouchEvent を通じて、タッチイベントが各 Activity の子ビューに配信されます。これは、Activity.onCreate メソッドで setContentView 時に設定されたビューです。コードは以下の通りです:
//DecorViewクラスの宣言
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
...
public boolean superDispatchTouchEvent(MotionEvent event) {
//ここでFrameLayoutのdispatchTouchEventメソッドが呼び出されます
return super.dispatchTouchEvent(event);
}
...
}
FrameLayout では dispatchTouchEvent (event) メソッドがオーバーライドされていないため、FrameLayout の親クラスである ViewGroup のこのメソッドの実装が必要です。このメソッドがイベントの具体的な分配を行います。ここでの具体的なイベント分配プロセスは研究が必要です。
イベント処理#
Activity のイベント処理メソッド:onTouchEvent ()、そのソースコードは以下の通りです:
//イベント処理、デフォルトでfalseを返す
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
//デフォルトでfalseを返す
return false;
}
onTouchEvent () メソッドにとって、イベント伝播は親コントロールに伝播されます。たとえ false を返しても、イベントは消費されたことになります。
注意:Activity がイベントを分配する際、return super.dispatchTouchEvent (ev) のときだけ、イベントは下に伝播し続けます。true または false を返すと、イベントは消費され、イベントの伝播は終了します。
ViewGroup#
ViewGroup には、主にイベント伝播に関連する 3 つのメソッド:dispatchTouchEvent ()、onInterceptTouchEvent ()、onTouchEvent () があります。具体的には以下の通りです:
イベント分配#
ViewGroup のイベント分配メソッド:dispatchTouchEvent ()、そのソースコードは以下の通りです:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
//dispatchTouchEvent()メソッドはデフォルトでfalseを返す
boolean handled = false;
//メソッドがイベントの分配を遮断するかどうかを決定します
onInterceptTouchEvent(ev);
...
//デフォルトではcanceledとinterceptedはfalse
if (!canceled && !intercepted) {
...
//このメソッドはイベントを子Viewに渡します
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
}
return handled;
}
このメソッドの役割は、ViewGroup 内の子 View を遍歴し、イベント (ACTION_DOWN) を子 View に処理させることです。ここでは主に onInterceptTouchEvent () と dispatchTransformedTouchEvent () メソッドが呼び出されます。onInterceptTouchEvent () はデフォルトで false を返します。以下は dispatchTransformedTouchEvent () メソッドの主要なソースコードです:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits){
...
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
//子Viewのイベント分配を行います
handled = child.dispatchTouchEvent(event);
}
...
return handled;
}
明らかに、dispatchTransformedTouchEvent () メソッドは主に子 View のイベント分配を行います。子 View がない場合は、親 View の dispatchTouchEvent (event) メソッドが呼び出されます。
イベント遮断#
ViewGroup のイベント分配メソッド onInterceptTouchEvent ()、そのソースコードは以下の通りです:
public boolean onInterceptTouchEvent(MotionEvent ev) {
...
//デフォルトでfalseを返す
return false;
}
このメソッドはデフォルトで false を返し、子 View へのイベント分配を遮断しないことを示します。このメソッドは ViewGroup の dispatchTouchEvent () メソッド内で呼び出されます。
イベント処理#
ViewGroup には独自の onTouchEvent () イベント処理メソッドはなく、View を継承しているため、そのイベント処理メソッドは View のイベント処理メソッドです。そのメソッドは以下の通りです:
public boolean onTouchEvent(MotionEvent event) {
...
//デフォルトでfalseを返す
return false;
}
このメソッドは関連するイベントの処理を行います。true を返すと、イベントが処理されたことを示します。具体的な使用状況は後の文で記録されます。
View#
View には、主にイベント分配に関連する 2 つのメソッド:dispatchTouchEvent () と onTouchEvent () があります。具体的には以下の通りです:
イベント分配#
View のイベント分配メソッドは dispatchTouchEvent () であり、主なコードは以下の通りです:
public boolean dispatchTouchEvent(MotionEvent event) {
...
//デフォルトの返り値はfalse
boolean result = false;
...
if (onFilterTouchEventForSecurity(event)) {
...
//onTouchEventメソッドが呼び出されます
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
上記のコードでは、dispatchTouchEvent () メソッドのデフォルトの返り値は false であり、イベントは引き続き分配されます。実際には dispatchTouchEvent メソッドの返り値は onTouchEvent メソッドの返り値に依存します。onTouchEvent が true を返すと、dispatchTouchEvent の返り値 result は true になります。この時、イベントは消費されたことを示します。もちろん、onTouchEvent の値が true であること自体がイベントが消費されたことを示します。以下は onTouchEvent メソッドが実行される条件です:
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
//ウィンドウが遮蔽されているため、タッチイベントを破棄します(ほとんど実行されません)
return false;
}
return true;
}
明らかに、上記のメソッドは一般的な状況では true を返すため、onTouchEvent メソッドを実行することが確実です。View の dispatchTouchEvent メソッドを呼び出すことは、実際には以下のように簡略化できます:
public boolean dispatchTouchEvent(MotionEvent event) {
...
//デフォルトの返り値はfalse
boolean result = false;
...
return onTouchEvent(event)
}
イベント処理#
View のイベント処理メソッドは onTouchEvent () であり、主なコードは以下の通りです:
public boolean onTouchEvent(MotionEvent event) {
...
//ここでtrueを返す条件はTouchDelegateがデフォルトで空であることです。この値はViewのタッチ領域に関するものです
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//クリックイベントが設定されている場合、この条件はtrueを返します。つまり、イベントが消費されます
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
//各イベントの処理
...
}
return true;
}
//デフォルトでfalseを返し、イベントを消費しないことを示します
return false;
}
イベントが子 View に伝わるとき、2 つの結果があります。現在の View がそのイベントを消費するか、消費せずに上に返すかです。もし遮断せず、Activity まで到達し、何の処理も行わなければ、最終的にそのイベントは破棄されます。
この記事は Android のイベント分配メカニズムの第 2 篇であり、主に Activity、ViewGroup、View 内のイベントに関連するメソッド、すなわち dispatchTouchEvent ()、onTouchEvent ()、onInterceptTouchEvent () メソッドの Activity、ViewGroup、View における異なる表現を記録しています。コードの観点から Android のイベント関係を理解し、イベントの分配プロセスについては次の記事で詳しく説明します。