一、事件分發概述
在Android中,事件分髮指的是Android系統把用戶的觸摸事件或按鍵事件從Activity分發到用戶界面中的各個View的過程。一個ViewGroup或View在收到事件後,會先進行一些相關的處理,然後再把事件傳遞給子View進行處理。
事件分發過程中,每個View都有自己的觸摸事件或按鍵事件處理方法,同時也可以設置是否攔截事件或者是否傳遞事件。這種機制使得開發者可以自由地設置View在事件流傳遞中的位置和相關的處理方法,從而實現各種複雜的交互效果。
二、事件分發流程
Android事件分發流程主要分為三個部分,分別是:事件的分發、事件的攔截和事件的處理。整個過程涉及到如下三個方法:
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false; if(onInterceptTouchEvent(ev)) { consume = onTouchEvent(ev); } else { consume = child.dispatchTouchEvent(ev); } return consume; } public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } public boolean onTouchEvent(MotionEvent ev) { return false; }
1、事件的分發
事件的分發流程是從Activity的dispatchTouchEvent方法開始,系統把用戶的觸摸事件或按鍵事件分發給根布局中的ViewGroup。在分發事件時,ViewGroup會先調用自己的onInterceptTouchEvent方法判斷是否要攔截事件,如果不攔截,則繼續將事件分發給自己的子View處理;否則,ViewGroup會直接調用自己的onTouchEvent方法處理事件。
2、事件的攔截
ViewGroup的onInterceptTouchEvent方法用來判斷事件是否需要被攔截。如果事件被攔截,則會直接交給ViewGroup的onTouchEvent方法處理;否則,事件繼續傳遞給子View進行處理。
3、事件的處理
View的onTouchEvent方法用來處理具體的觸摸事件或按鍵事件。如果View對事件進行了處理,則返回true;否則返回false,並允許事件繼續傳遞給上層View。
三、事件分發示例
為了更好地理解事件分發機制,下面我們通過代碼來模擬一個事件分發的示例。
1、首先,我們創建一個自定義View,用於展示事件流傳遞的過程:
public class MyView extends View { private static final String TAG = "MyView"; public MyView(Context context) { super(context); } public MyView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MyView(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.d(TAG, "MyView onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d(TAG, "MyView onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.d(TAG, "MyView onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(event); } }
2、然後,我們創建一個自定義ViewGroup,用於展示ViewGroup處理事件的流程:
public class MyViewGroup extends ViewGroup { private static final String TAG = "MyViewGroup"; public MyViewGroup(Context context) { super(context); } public MyViewGroup(Context context, AttributeSet attrs) { super(context, attrs); } public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); int childTop = 0; for (int i = 0; i < childCount; i++) { View child = getChildAt(i); child.layout(0, childTop, child.getMeasuredWidth(), childTop + child.getMeasuredHeight()); childTop += child.getMeasuredHeight(); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(TAG, "MyViewGroup onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d(TAG, "MyViewGroup onInterceptTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.d(TAG, "MyViewGroup onInterceptTouchEvent ACTION_UP"); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(TAG, "MyViewGroup onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d(TAG, "MyViewGroup onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.d(TAG, "MyViewGroup onTouchEvent ACTION_UP"); break; } return super.onTouchEvent(event); } }
3、最後,我們在Activity中創建MyViewGroup,並添加MyView作為其子View:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyViewGroup myViewGroup = findViewById(R.id.myViewGroup); MyView myView = new MyView(this); myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300)); myViewGroup.addView(myView); } }
運行後,我們可以看到在控制台輸出了如下事件流:
MyViewGroup onInterceptTouchEvent ACTION_DOWN MyView onTouchEvent ACTION_DOWN MyView onTouchEvent ACTION_MOVE MyView onTouchEvent ACTION_MOVE MyView onTouchEvent ACTION_MOVE MyView onTouchEvent ACTION_MOVE MyView onTouchEvent ACTION_MOVE MyView onTouchEvent ACTION_UP
從輸出中可以看出,事件首先被MyViewGroup的onInterceptTouchEvent方法攔截了,然後直接交給了MyView的onTouchEvent方法進行處理。在處理過程中,MyViewGroup並沒有再次處理事件。
四、事件分發機制的靈活運用
事件分發機制是Android應用程序中非常常用的一種機制,它可以被開發人員靈活運用於各種交互效果的實現中。下面我們以一個簡單的實例來講述如何使用事件分發機制來實現自定義View的滑動效果。
1、首先,我們創建一個自定義View:
public class DragView extends View { private int lastX; private int lastY; public DragView(Context context) { super(context); } public DragView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public DragView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; int left = getLeft() + dx; int top = getTop() + dy; int right = getRight() + dx; int bottom = getBottom() + dy; layout(left, top, right, bottom); lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); break; case MotionEvent.ACTION_UP: break; } return true; } }
2、然後,我們創建一個自定義ViewGroup用於包含DragView:
public class MyLayout extends FrameLayout { private DragView dragView; public MyLayout(Context context) { super(context); init(context); } public MyLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } public MyLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { dragView = new DragView(context); dragView.setBackgroundColor(Color.RED); dragView.setLayoutParams(new LayoutParams(200, 200)); addView(dragView); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: return false; case MotionEvent.ACTION_MOVE: return true; case MotionEvent.ACTION_UP: return false; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_MOVE: dragView.onTouchEvent(event); return true; case MotionEvent.ACTION_UP: return true; } return super.onTouchEvent(event); } }
3、最後,在Activity中創建MyLayout,即可實現滑動效果:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyLayout myLayout = findViewById(R.id.myLayout); } }
運行後,我們可以通過按下MyLayout並滑動來控制DragView的移動。
五、總結
通過上面的講解和示例,我們可以看到Android事件分發機制是一個非常重要的機制,它能夠靈活地幫助開發者實現各種複雜的交互效果。在實際開發過程中,我們需要根據具體需求合理地設置View在事件處理流程中的位置和攔截事件的位置,從而保證交互效果能夠正常實現。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/239701.html