Androidタッチイベントの伝達メカニズムを初めて認識しました。

11309 ワード

前言
今日まとめた知識点はAndoridのViewイベント伝達メカニズムであり、核心知識点でもあります。多くの開発者はこの問題に直面して困惑すると信じています。また、Viewのもう一つの難問はスライド衝突します。例えばScrelViewの中にListViewが嵌められています。これはどうやって解決するべきですか?したがって、開発者はViewのイベント伝達メカニズムをより深く理解する必要がある。
目次
  • Activity、View、View Groupの3つの関係
  • タッチイベントタイプ
  • イベント伝達3段階
  • Viewイベント伝達機構
  • View Groupイベント伝達機構
  • まとめ
  • Activity、View、View Groupの3つの関係
    Androidで見られているページの多くはActivityコンポーネントであることを知っています。そしてActivityでコントロールがネストされています。例えばTextView、RelativeLayoutレイアウトなど、実はこれらのコントロールの基本はViewという抽象類です。View GroupもViewのサブクラスです。違いはViewGroupは他のサブクラスとして、図の関係です。

    これらのViewコントロールのキャリヤーはActivityで、ActivityはDecorViewから描画します。
    イベントタイプをタッチ
    ACTION_DOWN:ユーザが指を押して操作すると、一回のタッチイベントの開始を表します。
    ACTION_MOVE:ユーザの指が画面上を移動すると、通常は軽い移動が一連の移動イベントをトリガします。
    ACTION_POINTERDOWN:追加の指を押して操作します。
    ACTION_POINTERUP:余分な指の離れ操作
    ACTION_UP:ユーザの指が画面から離れる操作は、一回の操作で一度のタッチイベントの終了を示しています。
    一回のスクリーンタッチ操作で、ACTION_DOWNとACTION_UPが必要です。ACT ION_。MOVEは状況によって異なりますが、クリックするだけであれば、押したり持ち上げたりするだけの操作が検出されます。
    イベント転送の3段階
    配布(Disppatch):イベントの配布はdispatch Touch Event方式に対応しており、Andoridシステムでは、すべてのタッチイベントはこの方法で配布されています。
    java book dispatch TouchEvent(MotionEvent ev)
    この方法は、このイベントを直接消費するか、またはイベントをサブビューに配信し続けるかを決定することができる。
    ブロック(Intercept):イベントブロックはオンインテットタッチEvent方法に対応しています。この方法はView Groupとそのサブクラスの中にしか存在しません。ViewとActivityには存在しません。
    java book on Intercept Touch Event(MotionEvent ev)
    この方法は、あるイベントをブロックするかどうかを判断するために用いられ、あるイベントをブロックすると、同じシーケンスイベントでは、この方法は再起動されない。
    消費(Consme):イベント消費はワンタッチイベント方法に対応しています。
    java book ontouchEvent(MotionEvent)
    クリックしたイベントを処理します。その結果、現在のイベントが消費されているかどうかを表します。消費されていない場合、同じイベントのシーケンスにおいて、現在のビューはイベントを受信できません。
    Androidシステムでは、イベントの伝達処理能力を持つものは3つあります。
    Activity:dispatch Touch Event、ontouch Eventの二つの方法を持っています。
    View Group:dispatch Touch Event、one Intercept TouchEvent、ontouch Eventの3つの方法を持っています。
    View:dispatch TouchEvent、ontouch Eventの二つの方法を持っています。
    Viewイベント伝達メカニズム
    ここでViewとはView Group以外のViewコントロールのことで、例えばTextView、Button、CheckBoxなど、Viewコントロール自体が最小の単位であり、他のViewの容器としては使えません。ViewはdispatTouchEvent、ontouchEventの2つの方法を持っています。
    
    public class MyTextView extends TextView {
    
     private static final String TAG = "MyTextView";
    
     public MyTextView(Context context) {
      super(context);
     }
    
     public MyTextView(Context context, AttributeSet attrs) {
      super(context, attrs);
     }
    
     @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;
       case MotionEvent.ACTION_CANCEL:
        Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");
        break;
       default:
        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;
       case MotionEvent.ACTION_CANCEL:
        Log.e(TAG, "onTouchEvent ACTION_CANCEL");
        break;
       default:
        break;
      }
      return super.onTouchEvent(event);
     }
    
    }
    
    
    また、MainActivityクラスを定義してMyTextViewを展示しています。このActivityでは、MyTextViewのためにクリックonClickとonTouchを設定しています。イベントの流れを追跡しやすいです。
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {
    
     private static final String TAG = "MainActivity";
    
     private MyTextView mTextView;
    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      mTextView = (MyTextView) findViewById(R.id.my_text_view);
      mTextView.setOnClickListener(this); //   MyTextView     
      mTextView.setOnTouchListener(this); //   MyTextView     
     }
    
     @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;
       case MotionEvent.ACTION_CANCEL:
        Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");
        break;
       default:
        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;
       case MotionEvent.ACTION_CANCEL:
        Log.e(TAG, "onTouchEvent ACTION_CANCEL");
        break;
       default:
        break;
      }
      return super.onTouchEvent(event);
     }
    
     @Override
     public void onClick(View view) {
      switch (view.getId()) {
       case R.id.my_text_view:
        Log.e(TAG, "MyTextView onClick");
        break;
       default:
        break;
      }
     }
    
     @Override
     public boolean onTouch(View view, MotionEvent motionEvent) {
      switch(view.getId()) {
       case R.id.my_text_view:
        switch (motionEvent.getAction()) {
         case MotionEvent.ACTION_DOWN:
          Log.e(TAG, "MyTextView onTouch ACTION_DOWN");
          break;
         case MotionEvent.ACTION_MOVE:
          Log.e(TAG, "MyTextView onTouch ACTION_MOVE");
          break;
         case MotionEvent.ACTION_UP:
          Log.e(TAG, "MyTextView onTouch ACTION_UP");
          break;
         default:
          break;
        }
        break;
       default:
        break;
      }
      return false;
     }
    }
    
    
    表示結果:

    その中から、イベントはdown-move-upのように順次実行され、ontouchの方法はOClickの方が優先的に呼び出され、superの方法で伝達されたら、最後の結果はMyTextViewのワンタッチEventの方法で消費され、消費しないと、イベントを父のレベルに戻して消費します。最終的にはActivityに戻ります。
    View Groupイベント伝達メカニズム
    View GroupはViewコントロールの容器として存在し、VieGroupはdispatch TouchEvent、one Intercept TouchEvent、ontouch Eventの3つの方法を持っています。同じように、私たちはView Groupをカスタマイズし、RelativeLayoutから引き継ぎ、MyRelativeLayoutを実現します。
    
    public class MyRelativeLayout extends RelativeLayout {
    
     private static final String TAG = "MyRelativeLayout";
    
     public MyRelativeLayout(Context context) {
      super(context);
     }
    
     public MyRelativeLayout(Context context, AttributeSet attrs) {
      super(context, attrs);
     }
    
     @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;
       case MotionEvent.ACTION_CANCEL:
        Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");
        break;
       default:
        break;
      }
      return super.dispatchTouchEvent(ev);
     }
    
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
      switch (ev.getAction()) {
       case MotionEvent.ACTION_DOWN:
        Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
        break;
       case MotionEvent.ACTION_MOVE:
        Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
        break;
       case MotionEvent.ACTION_UP:
        Log.e(TAG, "onInterceptTouchEvent ACTION_UP");
        break;
       default:
        break;
      }
      return true;
     }
    
     @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;
       case MotionEvent.ACTION_CANCEL:
        Log.e(TAG, "onTouchEvent ACTION_CANCEL");
        break;
       default:
        break;
      }
      return super.onTouchEvent(event);
     }
    }
    
    
    表示結果:

    タッチイベントの伝達順序はActivityからView Groupまで見られ、またView Groupからそのサブビューに伝えられます。ViewGroupはオンインテットTouchEvent方法でイベントをブロックし、この方法でtrueに戻ると、イベントはサブViewに渡されず、falseまたはsuper.onIntercept TouchEventに戻ると、イベントはサブViewに伝え続けられます。サブビューでイベントを消費すると、View Groupはイベントを受信しません。
    結び目
    Androidシステムイベントでは、ViewとView Groupの疑似コードは以下の通りである。
    
    public boolean dispatchTouchEvent(MotionEvent ev){
     boolean consume = false;
     if(onInterceptTouchEvent(ev)){
     consume = onTouchEvent(ev);
     }
     else{
     consume = child.dispatchTouchEvent(ev);
     }
     return consume;
    }
    Androidにおけるタッチ機構の流れを3つの図で表します。
    1、View内のタッチイベントは消費しません。

    2、View内タッチイベント消費

    3、View Groupタッチイベントをブロックする

    いくつかのまとめ:
  • 同じイベントのシーケンスとは、指がスクリーンに触れた時点から指がスクリーンから離れた時点で終了することです。一般的にはダウンイベントから始まり、中には数不定のmoveイベントが含まれ、最終的にはupイベントで終了します。
  • 通常の状況では、一つのイベントシーケンスは一つのビューによってブロックされ、消費されるしかない。
  • のViewがブロックを決定すると、このイベントのシーケンスはそれだけで処理され、他のイベントは同じシーケンスで処理されなくなり、イベントは親要素に再処理されます。すなわち親要素のonetouchEventが呼び出されます。
  • もしViewが消耗しなければACT ION_を除く。DOWN以外のイベントは、このクリックイベントがなくなります。この時は親要素のオンタッチイベントが呼び出されず、最終的にはActivityに処理されます。
  • View Groupは、デフォルトではイベントをブロックしません。
  • ViewにはオンインテートTouchEventの方法がありません。
  • Viewのon TouchEventは、デフォルトではクリックできない限り消費されます。
  • イベントの伝達過程は、外部からのものであり、イベントは先に父要素に伝達され、その後、父要素から子Viewに配布される。
  • 以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。