自定義View(3)-例項分析View與ViewGroup的觸控事件傳遞機制
例項分析View與ViewGroup的觸控事件傳遞機制
今天通過一個demo來分析一下,主要是自定義一個Button和layout,通過點選按鈕來測試事件的傳遞效果。
1.首先建立一個類RTButton繼承Button;重寫它的dispatchTouchEvent,onTouchEvent方法。分別在DOWN,MOVE,UP時列印句子,方便執行時檢視。如下程式碼:
public class RtButton extends Button{ public RtButton(Context context) { super(context); } public RtButton(Context context, AttributeSet attrs) { super(context, attrs); } public RtButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /**分發事件 * @param * @return */ @Override public boolean dispatchTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("RtButton---dispatchTouchEvent----DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("RtButton---dispatchTouchEvent----MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("RtButton---dispatchTouchEvent----UP"); break; default: break; } // 獲取了MotionEvent各個事件狀態 return super.dispatchTouchEvent(event); } /**處理事件 * @param * @return */ @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("RtButton----onTouchEvent----DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("RtButton----onTouchEvent----MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("RtButton----onTouchEvent----UP"); break; default: break; } return super.onTouchEvent(event); } }
2.首先建立一個類RtLayout繼承LinearLayout;重寫它的dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent方法。分別在DOWN,MOVE,UP時列印句子,方便執行時檢視。如下程式碼:
public class RtLayout extends LinearLayout { public RtLayout(Context context) { super(context); } public RtLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public RtLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /**分發事件 * @param * @return */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("RTLayout---dispatchTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("RTLayout---dispatchTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("RTLayout---dispatchTouchEvent---UP"); break; default: break; } return super.dispatchTouchEvent(ev); } /**處理事件 * @param * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("RtLayout---onInterceptTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("RtLayout---onInterceptTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("RtLayout---onInterceptTouchEvent---UP"); break; default: break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("RtLayout---onTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("RtLayout---onTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("RtLayout---onTouchEvent---UP"); break; default: break; } return super.onTouchEvent(event); } }
3.在佈局檔案中放入自定義控制元件:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.qzs.event.RtLayout android:layout_width="match_parent" android:background="@color/colorAccent" android:gravity="center" android:layout_height="match_parent"> <com.qzs.event.RtButton android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="自定義Button" android:textAllCaps="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </com.qzs.event.RtLayout> </android.support.constraint.ConstraintLayout>
4.在MainActivity中新增按鈕的點選事件,再新增按鈕的觸控事件,並重寫Activity的dispatchTouchEvent,onTouchEvent事件
public class MainActivity extends AppCompatActivity { private RtButton btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn=findViewById(R.id.btn); btn.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("onTouch----DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("onTouch----MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("onTouch----UP"); break; default: break; } return false; } }); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { System.out.println("<-onClick->"); } }); } // Activity的dispatchTouchEvent的事件 @Override public boolean dispatchTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("Activity----dispatchTouchEvent----DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("Activity----dispatchTouchEvent----MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("Activity----dispatchTouchEvent----UP"); break; default: break; } return super.dispatchTouchEvent(ev); } @Override // 對Activity的ontouchevent的方法事件 public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("Activity----ontouchevent----DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("Activity----ontouchevent----MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("Activity----ontouchevent----UP"); break; default: break; } return super.onTouchEvent(event); } }
5.測試
都是預設的狀態,我們直接執行一下,結果如下:

從執行結果我們會執行順序如下:
Activity----dispatchTouchEvent----DOWN RTLayout---dispatchTouchEvent---DOWN RtLayout---onInterceptTouchEvent---DOWN RtButton---dispatchTouchEvent----DOWN onTouch----DOWN RtButton----onTouchEvent----DOWN Activity----dispatchTouchEvent----UP RTLayout---dispatchTouchEvent---UP RtLayout---onInterceptTouchEvent---UP RtButton---dispatchTouchEvent----UP onTouch----UP RtButton----onTouchEvent----UP <-onClick->
由此我們印證了觸控事件是由外到內傳遞的。
大家是不是對 onTouch----DOWN
比較疑惑,ontouch事件為什麼早於onTouchEvent事件?其實我們上一節的View原始碼分析就已經講到了,截圖如下:

從原始碼中我們發現onTouch早於onTouchEvent
現在我們把RtLayout中的dispatchTouchEvent直接消費掉,也就是返回true
執行後:

說明了事件只傳遞到了RtLayout。大家也可以自己去試一試,這樣才能深入的瞭解。大家如果有不懂的可以問我。