有人问我 事件分发是分发给View的吗?
我笃定的说是的,结果不是的。
其实View有一个TouchDelegate,可以给他设置这样一个东西,来使得你的事件触摸范围会被增大!简直牛X!
这个类的全部源码及部分注释
public class TouchDelegate { /** * View that should receive forwarded touch events */ 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. */ 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. */ private Rect mSlopBounds;//给mBounds稍微扩大点,其目的是消除触摸误差 /** * True if the delegate had been targeted on a down event (intersected mBounds). */ private boolean mDelegateTargeted; /** * The touchable region of the View extends above its actual extent. */ public static final int ABOVE = 1; /** * The touchable region of the View extends below its actual extent. */ public static final int BELOW = 2; /** * The touchable region of the View extends to the left of its * actual extent. */ public static final int TO_LEFT = 4; /** * The touchable region of the View extends to the right of its * actual extent. */ 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 */ public TouchDelegate(Rect bounds, View delegateView) {//构造函数:rect是实际触摸区域,view是要修改触摸区域的view mBounds = bounds; mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();//这里就是设定一个值,如果你的滑动距离超过这个值,那么这一次就是有效滑动 mSlopBounds = new Rect(bounds); 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. */ public boolean onTouchEvent(MotionEvent event) {//这里是核心代码了:如果你落在这个rect区域内,就判定你是落在这个view内了,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)) { mDelegateTargeted = true; sendToDelegate = true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_MOVE: sendToDelegate = mDelegateTargeted; if (sendToDelegate) {//这里的代码需要解释下,溢出区域,其实是反溢出,是通过比我们设定的rect区域小来进行的一个防止误差,所以你触摸到了rect,不一定有效 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);//如果说是在rect区域内,那么就把event的位置修改在view的中心,模拟触摸事件是真的落在了view内了
} 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); } return handled; } }
看看view是如何使用TouchDelegate的,在它的onTouchEvent里,首先就进行了一个判断
if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } }
但是实战的时候,又发现不是这么一回事了,我的理解完全出现了偏差
其真正含义是,当你触摸一个view的时候,而且touch delegate的矩形区域也在这个view里,你触摸到了这块区域,那么这个view就可以把事件转发给delegate类中维护的那个view
实战演练
代码
重写了activity的onWindowFousChanged
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); TextView tv = (TextView) root.getChildAt(0); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "666", Toast.LENGTH_SHORT).show(); } }); Rect rect = new Rect(); rect.top = tv.getTop() - 100; rect.bottom = tv.getBottom() + 100; rect.left = tv.getLeft() - 100; rect.right = tv.getRight() + 100; root.setTouchDelegate(new TouchDelegate(rect, tv)); }