在View的onTouchEvent()中会涉及到这个类,就顺便看了一看,虽然之前没有接触过。
用途
如果有两个View,分别是ViewA,ViewB,可以给ViewA setTouchDelegate(bounds, ViewB),这样当ViewA中落在bounds的事件就会传给ViewB。
使用场景见官方文档。
源码分析
这个类主要就是一个diapatchTouchEvent(),用来将事件分发给委托View。
/**
* Helper class to handle situations where you want a view to have a larger touch area than its
* actual view bounds. The view whose touch area is changed is called the delegate view. This
* class should be used by an ancestor of the delegate. To use a TouchDelegate, first create an
* instance that specifies the bounds that should be mapped to the delegate and the delegate
* view itself.
* <p>
* The ancestor should then forward all of its touch events received in its
* {@link android.view.View#onTouchEvent(MotionEvent)} to {@link #onTouchEvent(MotionEvent)}.
* </p>
*/
// 如果你需要一个View拥有比自己真实区域更大的区域接受触摸事件,你就可以使用这个工具类。
// 触摸区域改变的View叫做委托View。
// 这个类应该被包含委托View的父View使用,父View就应该将它所有在区域内的事件都传递给它。
public class TouchDelegate {
/**
* View that should receive forwarded touch events
*/
// 接受额外区域事件的View,委托View。
private View mDelegateView;
/**
* Bounds in local coordinates of the containing view that should be mapped to the delegate
* view. This rect is used for initial hit testing.
*/
// 构造器传入的需要传递给委托View的坐标区域。
private Rect mBounds;
/**
* mBounds inflated to include some slop. This rect is to track whether the motion events
* should be considered to be be within the delegate view.
*/
// mBounds扩充一些最小滑动距离后的区域,用来判断事件是否应该被考虑在委托View的区域中
private Rect mSlopBounds;
/**
* True if the delegate had been targeted on a down event (intersected mBounds).
*/
// 如果被指向一个ACTION_DOWN事件,就为true。
private boolean mDelegateTargeted;
/**
* The touchable region of the View extends above its actual extent.
*/
// 委托View的扩充区域在真实区域之上
public static final int ABOVE = 1;
/**
* The touchable region of the View extends below its actual extent.
*/
// 委托View的扩充区域在真实区域之下
public static final int BELOW = 2;
/**
* The touchable region of the View extends to the left of its
* actual extent.
*/
// 委托View的扩充区域在真实区域左边
public static final int TO_LEFT = 4;
/**
* The touchable region of the View extends to the right of its
* actual extent.
*/
// 委托View的扩充区域在真实区域右边
public static final int TO_RIGHT = 8;
// 被看作移动事件的最小滑动距离
private int mSlop;
/**
* Constructor
*
* @param bounds Bounds in local coordinates of the containing view that should be mapped to
* the delegate view
* @param delegateView The view that should receive motion events
*/
/**
* 构造器,初始化。
* @param bounds 需要映射到委托View的区域
* @param delegateView 委托View
*/
public TouchDelegate(Rect bounds, View delegateView) {
mBounds = bounds;
// 获得最小滑动参数
mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
mSlopBounds = new Rect(bounds);
// 将区域的每条边向外扩大移动mSlop的距离
mSlopBounds.inset(-mSlop, -mSlop);
mDelegateView = delegateView;
}
/**
* Will forward touch events to the delegate view if the event is within the bounds
* specified in the constructor.
*
* @param event The touch event to forward
* @return True if the event was forwarded to the delegate, false otherwise.
*/
/**
* 如果事件在构造器中指定的区域内,就将该事件委托传递给构造器指定的View。
* @param event 事件
* @return 是否成功的传递给委托View并消耗事件。
*/
public boolean onTouchEvent(MotionEvent event) {
// 获取事件相对于事件分发到的View的坐标
int x = (int)event.getX();
int y = (int)event.getY();
boolean sendToDelegate = false;
boolean hit = true;
boolean handled = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Rect bounds = mBounds;
// 判断事件是否在委派控制的区域内
if (bounds.contains(x, y)) {
// 在区域内就置为true
mDelegateTargeted = true;
sendToDelegate = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
// ACTION_UP和ACTION_MOVE可能位于一个ACTION_MOVE事件之后,所以使用mSlopBounds判断。
sendToDelegate = mDelegateTargeted;
// 如果接受过ACTION_DOWN事件,并且在区域内。
if (sendToDelegate) {
Rect slopBounds = mSlopBounds;
// 判断如果不在扩展最小滑动距离的区域中,就将hit置为false。
if (!slopBounds.contains(x, y)) {
hit = false;
}
}
break;
case MotionEvent.ACTION_CANCEL:
// 如果是取消事件,sendToDelegate赋值为ACTION_DOWN时赋的值
sendToDelegate = mDelegateTargeted;
// 再将mDelegateTargeted重新赋值false,因为此时事件序列应该是要结束了。
mDelegateTargeted = false;
break;
}
// 如果上面赋值了true,就去将事件传递给委托View。
if (sendToDelegate) {
final View delegateView = mDelegateView;
// 如果该事件为ACTION_MOVE事件后,并且还在区域内。
if (hit) {
// Offset event coordinates to be inside the target view
// 将事件的坐标重新设置为委托View的中心。
event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);
} else {
// Offset event coordinates to be outside the target view (in case it does
// something like tracking pressed state)
int slop = mSlop;
// 如果在区域外了就设置在委托区域之外两倍的最小滑动距离上。
event.setLocation(-(slop * 2), -(slop * 2));
}
// 调用委托View去分发处理该事件。
handled = delegateView.dispatchTouchEvent(event);
}
// 返回值true表示将事件分发给了委托View并且委托View处理了该事件。
// 返回值为false表示委托View没有处理事件,或者因为事件坐标的原因没有交给委托View处理。
return handled;
}
}