在View.java中,使用TouchDelegate的代码很少,它的实现机制也非常的简单。先看看它在View.java中的代码:
/**
* The delegate to handle touch events that are physically in this view
* but should be handled by another view.
*/
private TouchDelegate mTouchDelegate = null;//说明了这个类对象的应用意义:把此View的触摸事件交由另外的View处理
public boolean onTouchEvent(MotionEvent event) {
...
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {//如果设置了代理,那么事件交由代理处理,并且如果代理成功处理了事件,那么事件结束
return true;
}
}
public void setTouchDelegate(TouchDelegate delegate) {
mTouchDelegate = delegate;
}
public TouchDelegate getTouchDelegate() {
return mTouchDelegate;
}
下面我们再来看看TouchDelegate.java源码:
/**
* 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>
*/
//上面这段话注释在类的开头,它交代了TouchDelegate的使用意义——当我们希望View的触摸区域大于它实际的大小时可以使用这个代理实现和使用方法——它作为基类(即我们可以继承它写自己的代理),创建一个实例,这个实例需要指定一个区域和一个代理视图,代理视图可以是它本身。
//接下来看看它的构造函数,基本上就了解了如何使用这个辅助类。
public TouchDelegate(Rect bounds, View delegateView) {
mBounds = bounds;
mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
mSlopBounds = new Rect(bounds);
mSlopBounds.inset(-mSlop, -mSlop);
mDelegateView = delegateView;
}
//最重要的是onTouchEvent()这个函数,看看它是如何把触摸事件交给代理去处理的。首先说明一点,TouchDelegate是一个基类,我们可以继承它并按照自己的方式去实现代理
public boolean onTouchEvent(MotionEvent event) {
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)) {//如果触摸点坐标在我们指定的代理的区域内,那么事件交给代理View处理
mDelegateTargeted = true;
sendToDelegate = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
sendToDelegate = mDelegateTargeted;
if (sendToDelegate) {
Rect slopBounds = mSlopBounds;
if (!slopBounds.contains(x, y)) {
hit = false;
}
}
break;
case MotionEvent.ACTION_CANCEL:
sendToDelegate = mDelegateTargeted;
mDelegateTargeted = false;
break;
}
if (sendToDelegate) {
final View delegateView = mDelegateView;
if (hit) {
// Offset event coordinates to be inside the target 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));
}
handled = delegateView.dispatchTouchEvent(event);//事件交由代理View去处理
}
return handled;
}
通过上面这些源码可知,TouchDelegate有两种应用:
1.仅是拓宽(或缩小)触摸区域,把DelegateView设置为它本身,Bounds设置的区域就是代理点击的区域。这里有个疑惑,当点击区域在View本身的范围内时,ViewGroup才会把触摸事件分配给它。而在Bounds区域内,View获取不到触摸事件,又如何把触摸事件交给代理呢?
2.把触摸事件交给另外的View——DelegateView处理