Androidタッチスクリーンイベント配布メカニズムの詳細とソース分析一(View編)
25101 ワード
記事参照先:http://blog.csdn.net/yanbober/article/details/45887547作者の无私な分かち合いに感谢して、私はここでただ自分の学习と総括をします.まず、サンプルプログラムcodeです.
activity_main.xmlファイル:
システムからのButtonクリックイベントについては説明しませんが、最上位のリンクを参照してください.ここでは、まず構造:java.lang.Object|--android.view.View|--android.widget.TextView|--android.widget.widget.TextView|--android.widget.Button をまとめます.buttonはViewのサブクラスに属し、LinearLayoutはView Groupのサブクラスに属する.Buttonを書き換えていない場合:1.onTouch実行はonClickの前に行われます.dispatchTouchEventで呼び出され、onTouchEventを呼び出すかどうかを決定します.また、action=0は下にaction=2を押してaction=1を上に移動します.2.onClick実行はaction=1も弾けた後に3.onTouchを実行するのは戻り値があり、デフォルトでfalseを返します.onClickイベントは実行され、trueが返されます.onClickイベントは、カスタムButtonでcodeを深く理解する実行プロセスを実行しません.
図から完全な実行プロセスを見ることができます.まず、dispatchTouchEvent-->onTouch->>onTouchEvent->onClickの下に、ソースコードがどのように書かれているかを見てみましょう.
Androidでのクリックイベント配信はまずdispatchTouchEventを呼び出すためこの関数から見ると他のクラスでは実現していないので、Viewでの定義を見ると、onTouchはonClickの前で実行されていると言います.li.mOnTouchListener.onTouch(this,event)という言葉がif文で呼び出されているのがわかります.この判断条件liはListenerInfo li=mListenerInfo;——->mListenerInfo:
mListenerInfoが単一のインスタンスであることに相当し、li.mOnTouchListener付与関数を使用します.
これにより、Activityで実装されたインタフェースオブジェクトが渡されるので、if判定文の前の2つの条件はtrueになります.3番目の条件Buttonのデフォルト属性はenableです.そのため、決定権は4番目の条件の上にあります.onTouchメソッドを呼び出して、戻り値を判定します.trueであれば、次のifに入ることなく直接戻ります.(onTouchEvent)では、これまでonClick関数の呼び出しは見られなかったので、onClickはonTouchEventで呼び出されたに違いないと考えられています.では、codeを追いかけ続けます.
ソースコードが少し長いので、大きなif判断if((((viewFlags&CLICKABLE)==CLICKABLE|(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE))を見て、おなじみのswitch(event.getAction(){case MotionEvent.ACTION_UP:.....私たちのclickイベントの呼び出しをスキップし続けるとperformClick();関数については、onClickがupイベントの後に印刷されたと思うので、upでonClick呼び出しをしたに違いありません.
やっとli.mOnClickListener.onClick(this)を見ました;この文では、上記のListerインタフェースの割り当てにより、ActivityでListerオブジェクトが設定され、up後にonClickイベントが呼び出され、最後にonClickの内容が印刷されると判断しました.そのため、プロセス全体が終了しました.buttonをカスタマイズした後、次の状況を見てみましょう.1.dispatchTouchEventがsuを呼び出さずに直接trueに戻るとper.dispatchTouchEvent(event);親メソッドを呼び出さずにtrueを直接返すと、私たちは何もしていないに違いない.onTouchもonClickメソッドも呼び出さない.falseに直接戻ると、 downイベントがトリガーされただけで、upイベントもトリガーされていない.次はなぜこんなに多くのイベントが出てくるのか.注意深く観察する必要があります.このオブジェクトはLinearLayoutオブジェクトであり、私たちのbuttonオブジェクトではありません.そのため、View Groupに関連しています.ここではdispatchTouchEventがfalseに戻ることを知っていれば、次のサブクラスはこのクリックイベントを消費できないことを示し、親クラスに戻ってクリックイベントを消費することを示しています.そのため、LinearLayoutのクリックイベントの実行がトリガーされ、falseに戻った後残りの移動や上向きのクリックイベントは上には伝わりませんので、0しか見えません.2 1は見えません.
2.super.dispatchTouchEvent(event)が呼び出され、trueが返されると、onTouchおよびonClick動作が多くなるだけである:上図のように、buttonをカスタマイズしていないときの呼び出し順序と同じであるかどうか、オブジェクトがLinearLayoutではなくMyButtonで印刷されていることに注意してください.呼び出しはtrueを返し、super.dispatchTouchEvent(event)に直接戻るためです.;親クラスを呼び出してデフォルトで返されるのもtrueなので、違いはありません.
3.return super.dispatchTouchEvent(event);の場合、onTouchはfalseを返します.dispatchTouchEventのactionが切断されているかどうかは、onTouchEventという関数の戻り値を見てみましょう.trueを直接返す場合
でonTouchEventの呼び出しが表示されなかったのは、onTouchが直接trueに戻ったためであり、まだtrueに戻っていないためであり、onTouchを変更してfalseに戻ると、でonTouchEventイベントが呼び出されたことが明らかになったが、onClickは呼び出されていない.親クラスを呼び出すこの方法がないため、直接faに戻るとlse、イベントは必ず遮断されます.onTouchはfalseを返し、onTouchEventメソッドに入り、falseを返します.dispatchTouchEvent全体がfalseを返します.
は、MyButtonオブジェクトに対してsuper.onTouchEvent(event)が呼び出された場合、押下されたイベントのみを見ることができます.;その後falseに戻り、上記と何の変化もなく、onClickは実行しないため、upはそこに渡されず、trueを返すと、onClickイベントの呼び出しが1つ増えただけで**
まとめ:
**1.onTouchEventを呼び出すかどうかはonTouch戻り値に依存し、trueは呼び出されず、falseが呼び出されます.2.切断されるかどうかはdispatchTouchEvent戻り値に依存し、falseは切断され、trueは切断されません.3.onClickを呼び出すかどうかはonTouchEventが呼び出されたかどうかに依存します.4.dispatchTouchEventが戻り値であり、親を呼び出さない場合、onTouchEventは呼び出されない5.onTouchEventが親クラスを呼び出さずに値を返すだけである場合、onClickは呼び出されません.
何か悪いところがあったら、皆さんに教えてください.ありがとうございます.
package com.qs.clickeventdispatchdemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.LinearLayout;
public class MainActivity extends Activity implements OnClickListener, OnTouchListener{
private MyButton myButton ;
private LinearLayout myLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myButton = (MyButton) findViewById(R.id.myButton);
myLayout = (LinearLayout) findViewById(R.id.myLayout);
myButton.setOnClickListener(this);
myButton.setOnTouchListener(this);
myLayout.setOnTouchListener(this);
myLayout.setOnClickListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
PrintLog.show_parent(" onTouch "+event.getAction()+" "+v+"----->");
return false;
}
@Override
public void onClick(View v) {
PrintLog.show_parent("onClick execute "+v+"----->");
}
}
activity_main.xmlファイル:
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<com.qs.clickeventdispatchdemo.MyButton
android:id="@+id/myButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=" " />
システムからのButtonクリックイベントについては説明しませんが、最上位のリンクを参照してください.ここでは、まず構造:java.lang.Object|--android.view.View|--android.widget.TextView|--android.widget.widget.TextView|--android.widget.Button をまとめます.buttonはViewのサブクラスに属し、LinearLayoutはView Groupのサブクラスに属する.Buttonを書き換えていない場合:1.onTouch実行はonClickの前に行われます.dispatchTouchEventで呼び出され、onTouchEventを呼び出すかどうかを決定します.また、action=0は下にaction=2を押してaction=1を上に移動します.2.onClick実行はaction=1も弾けた後に3.onTouchを実行するのは戻り値があり、デフォルトでfalseを返します.onClickイベントは実行され、trueが返されます.onClickイベントは、カスタムButtonでcodeを深く理解する実行プロセスを実行しません.
public class MyButton extends Button {
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
PrintLog.show_myButton(event.getAction()+" dispatchTouchEvent ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
PrintLog.show_myButton(event.getAction()+" onTouchEvent ");
return super.onTouchEvent(event);
}
}
図から完全な実行プロセスを見ることができます.まず、dispatchTouchEvent-->onTouch->>onTouchEvent->onClickの下に、ソースコードがどのように書かれているかを見てみましょう.
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}
Androidでのクリックイベント配信はまずdispatchTouchEventを呼び出すためこの関数から見ると他のクラスでは実現していないので、Viewでの定義を見ると、onTouchはonClickの前で実行されていると言います.li.mOnTouchListener.onTouch(this,event)という言葉がif文で呼び出されているのがわかります.この判断条件liはListenerInfo li=mListenerInfo;——->mListenerInfo:
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
mListenerInfoが単一のインスタンスであることに相当し、li.mOnTouchListener付与関数を使用します.
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
これにより、Activityで実装されたインタフェースオブジェクトが渡されるので、if判定文の前の2つの条件はtrueになります.3番目の条件Buttonのデフォルト属性はenableです.そのため、決定権は4番目の条件の上にあります.onTouchメソッドを呼び出して、戻り値を判定します.trueであれば、次のifに入ることなく直接戻ります.(onTouchEvent)では、これまでonClick関数の呼び出しは見られなかったので、onClickはonTouchEventで呼び出されたに違いないと考えられています.では、codeを追いかけ続けます.
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true);
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true);
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
break;
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
ソースコードが少し長いので、大きなif判断if((((viewFlags&CLICKABLE)==CLICKABLE|(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE))を見て、おなじみのswitch(event.getAction(){case MotionEvent.ACTION_UP:.....私たちのclickイベントの呼び出しをスキップし続けるとperformClick();関数については、onClickがupイベントの後に印刷されたと思うので、upでonClick呼び出しをしたに違いありません.
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
return true;
}
return false;
}
やっとli.mOnClickListener.onClick(this)を見ました;この文では、上記のListerインタフェースの割り当てにより、ActivityでListerオブジェクトが設定され、up後にonClickイベントが呼び出され、最後にonClickの内容が印刷されると判断しました.そのため、プロセス全体が終了しました.buttonをカスタマイズした後、次の状況を見てみましょう.1.dispatchTouchEventがsuを呼び出さずに直接trueに戻るとper.dispatchTouchEvent(event);親メソッドを呼び出さずにtrueを直接返すと、私たちは何もしていないに違いない.onTouchもonClickメソッドも呼び出さない.falseに直接戻ると、 downイベントがトリガーされただけで、upイベントもトリガーされていない.次はなぜこんなに多くのイベントが出てくるのか.注意深く観察する必要があります.このオブジェクトはLinearLayoutオブジェクトであり、私たちのbuttonオブジェクトではありません.そのため、View Groupに関連しています.ここではdispatchTouchEventがfalseに戻ることを知っていれば、次のサブクラスはこのクリックイベントを消費できないことを示し、親クラスに戻ってクリックイベントを消費することを示しています.そのため、LinearLayoutのクリックイベントの実行がトリガーされ、falseに戻った後残りの移動や上向きのクリックイベントは上には伝わりませんので、0しか見えません.2 1は見えません.
2.super.dispatchTouchEvent(event)が呼び出され、trueが返されると、onTouchおよびonClick動作が多くなるだけである:上図のように、buttonをカスタマイズしていないときの呼び出し順序と同じであるかどうか、オブジェクトがLinearLayoutではなくMyButtonで印刷されていることに注意してください.呼び出しはtrueを返し、super.dispatchTouchEvent(event)に直接戻るためです.;親クラスを呼び出してデフォルトで返されるのもtrueなので、違いはありません.
3.return super.dispatchTouchEvent(event);の場合、onTouchはfalseを返します.dispatchTouchEventのactionが切断されているかどうかは、onTouchEventという関数の戻り値を見てみましょう.trueを直接返す場合
@Override
public boolean onTouchEvent(MotionEvent event) {
PrintLog.show_myButton(event.getAction()+" onTouchEvent ");
// return super.onTouchEvent(event);
return true;
}
でonTouchEventの呼び出しが表示されなかったのは、onTouchが直接trueに戻ったためであり、まだtrueに戻っていないためであり、onTouchを変更してfalseに戻ると、でonTouchEventイベントが呼び出されたことが明らかになったが、onClickは呼び出されていない.親クラスを呼び出すこの方法がないため、直接faに戻るとlse、イベントは必ず遮断されます.onTouchはfalseを返し、onTouchEventメソッドに入り、falseを返します.dispatchTouchEvent全体がfalseを返します.
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
PrintLog.show_myButton(event.getAction()+" dispatchTouchEvent ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
PrintLog.show_myButton(event.getAction()+" onTouchEvent ");
// return super.onTouchEvent(event);
return false;
}
は、MyButtonオブジェクトに対してsuper.onTouchEvent(event)が呼び出された場合、押下されたイベントのみを見ることができます.;その後falseに戻り、上記と何の変化もなく、onClickは実行しないため、upはそこに渡されず、trueを返すと、onClickイベントの呼び出しが1つ増えただけで**
まとめ:
**1.onTouchEventを呼び出すかどうかはonTouch戻り値に依存し、trueは呼び出されず、falseが呼び出されます.2.切断されるかどうかはdispatchTouchEvent戻り値に依存し、falseは切断され、trueは切断されません.3.onClickを呼び出すかどうかはonTouchEventが呼び出されたかどうかに依存します.4.dispatchTouchEventが戻り値であり、親を呼び出さない場合、onTouchEventは呼び出されない5.onTouchEventが親クラスを呼び出さずに値を返すだけである場合、onClickは呼び出されません.
何か悪いところがあったら、皆さんに教えてください.ありがとうございます.