天天看點

Android觸摸事件傳遞機制(一)

在開發過程中會經常遇到View與ViewGroup嵌套的問題,如ViewPager嵌套Fragment,而Fragment中又需要實作一個廣告滑動,此時廣告滑動就會與ViewPager的滑動事件産生沖突,而深入了解Android觸摸事件的傳遞機制則是解決問題的關鍵。

一.觸摸事件的類型(主要有三種)

1.MotionEvent.ACTION_DOWN:手指按下螢幕觸發。
2.MotionEvent.ACTION_MOVE:手指在螢幕上移動觸發。
3.MotionEvent.ACTION_UP:手指擡起時觸發。

二.事件傳遞的三個階段

1.方法介紹:

1).分發(DisPatch):對應的方法為:public boolean dispatchTouchEvent(MotionEvent ev),該方法的傳回值決定了是否将事件向子視圖傳遞:true:不傳遞;false:不傳遞;super.dispatchTouchEvent(ev):傳遞。
2).攔截(InterCept):對應的方法為:public boolean onInterceptTouchEvent(MotionEvent ev),該方法隻有ViewGroup和其子類才有,該方法的傳回值決定了是否攔截事件:true:攔截;false:不攔截;super.onInterceptTouchEvent(ev):不攔截。
3).消費(Consume):對應的方法為:public boolean onTouchEvent(MotionEvent event),該方法的傳回值決定了是否處理該事件:true:處理;false:不處理;super.onTouchEvent(event):将會調用onClick()方法。

2.Activity與View事件傳遞示範

1).View代碼(繼承TextView重寫onTouchEvent和dispatchTouchEvent方法)
public class MyTextView extends TextView {
    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e("MyTextView","onTouchEvent:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("MyTextView","onTouchEvent:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e("MyTextView","onTouchEvent:ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e("MyTextView","dispatchTouchEvent:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("MyTextView","dispatchTouchEvent:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e("MyTextView","dispatchTouchEvent:ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(event);
    }
}
           
2).Activity代碼(重寫dispatchTouchEvent和onTouchEvent并為MyTextView設定觸摸事件)
public class MainActivity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyTextView myTextView=findViewById(R.id.tv);
        myTextView.setOnClickListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e("MainActivity","onTouchEvent:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("MainActivity","onTouchEvent:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e("MainActivity","onTouchEvent:ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e("MainActivity","dispatchTouchEvent:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("MainActivity","dispatchTouchEvent:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e("MainActivity","dispatchTouchEvent:ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public void onClick(View v) {
        Log.e("MyTextView","onClick");
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e("MainActivity","onTouch:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("MainActivity","onTouch:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e("MainActivity","onTouch:ACTION_UP");
                break;
        }
        return false;
    }
}
           
3).設定不同傳回值時的結果
◇保持預設值
07-23 13:55:25.707 16976-16976/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
07-23 13:55:25.717 16976-16976/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_DOWN
onTouchEvent:ACTION_DOWN
07-23 13:55:25.797 16976-16976/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
07-23 13:55:25.797 16976-16976/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_UP
onTouchEvent:ACTION_UP
07-23 13:55:25.807 16976-16976/com.itfitness.viewandviewgroup E/MyTextView: onClick
           
◇改變Activity的dispatchTouchEvent方法傳回值
true:(事件沒有分發下去)
07-23 13:56:53.557 17565-17565/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
07-23 13:56:53.657 17565-17565/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
           
false:(事件同樣沒有分發下去)
07-23 13:58:16.377 18010-18010/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
07-23 13:58:16.457 18010-18010/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
           
◇改變MyTextView的dispatchTouchEvent方法傳回值
true:(事件在MyTextView的dispatchTouchEvent方法被阻斷)
07-23 13:59:39.087 18470-18470/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
07-23 13:59:39.087 18470-18470/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_DOWN
07-23 13:59:39.177 18470-18470/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
07-23 13:59:39.187 18470-18470/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_UP
           
false:(ACTION_DOWN事件在MyTextView的dispatchTouchEvent方法被阻斷,并且MainActivity中執行了onTouchEvent方法)
07-23 14:03:03.517 19115-19115/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
07-23 14:03:03.517 19115-19115/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_DOWN
07-23 14:03:03.517 19115-19115/com.itfitness.viewandviewgroup E/MainActivity: onTouchEvent:ACTION_DOWN
07-23 14:03:03.597 19115-19115/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
onTouchEvent:ACTION_UP
           
◇改變MyTextView的onTouchEvent傳回值
true:(ACTION_DOWN事件傳遞到MyTextView并沒有執行onClick方法)
07-23 14:07:47.317 20046-20046/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
07-23 14:07:47.317 20046-20046/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_DOWN
onTouchEvent:ACTION_DOWN
07-23 14:07:47.387 20046-20046/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
07-23 14:07:47.387 20046-20046/com.itfitness.viewandviewgroup E/MyTextView: dispatchTouchEvent:ACTION_UP
onTouchEvent:ACTION_UP
           
false:(MyTextView沒有消費事件反給了MainActivity并且沒有執行onClick方法)
07-23 14:11:44.027 20717-20717/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_DOWN
onTouchEvent:ACTION_DOWN
07-23 14:11:44.067 20717-20717/com.itfitness.viewandviewgroup E/MainActivity: dispatchTouchEvent:ACTION_UP
onTouchEvent:ACTION_UP
           

三.Activity與View事件傳遞總結

Android觸摸事件傳遞機制(一)

image

個人部落格: https://myml666.github.io

繼續閱讀