在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; }
/** * 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处理