public void setTouchDelegate(TouchDelegate delegate) { mTouchDelegate = delegate; }
第一步:首先来看看TouchDelegate类的一段原文说明,在文件TouchDelegate.java有如下说明:
Helper class to handle situations where you want a view to have a larger touch area than its
第二步:要深刻理解,我觉得需要查看源码,看看他是如何实现的.
查看TouchDelegate可以知道,TouchDelegate是一个很简单的类,主要有四个变量和两个方法:/** * View that should receive forwarded touch events */ private View mDelegateView;//需要扩大触摸区域的view /** * 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溢出的触摸区域:实际上就是比mBounds大一点的区域(宽高分别大8),其目的时消除触摸误差. public TouchDelegate(Rect bounds, View delegateView) {//构造函数:bounds 表示触摸的区域;delegateView 需要扩大触摸区域的view mBounds = bounds; mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();//这里获取的是触摸滑动距离的判断:就是触摸滑动距离为mSlop时候裁判为是在触摸滑动move.其默认值为8 mSlopBounds = new Rect(bounds); mSlopBounds.inset(-mSlop, -mSlop);//这里扩大触摸滑动区域(宽高扩大8),其目的时消除触摸时候的误差.达到一个触摸安全处理. mDelegateView = delegateView; //需要扩大触摸区域的view(需要改变触摸区域的view) } public boolean onTouchEvent(MotionEvent event) {//这是核心代码:判断当前触摸是否在这个区域:mBounds.如果在是则会让这次的触摸事件真的在mDelegateView真实的区域内. int x = (int)event.getX(); int y = (int)event.getY();//记录这次触摸点位置 boolean sendToDelegate = false;//标记这次触摸是否有效(应该传递给mDelegateView) boolean hit = true; //标记这次触摸是否在这个区域内(mBounds) boolean handled = false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Rect bounds = mBounds; if (bounds.contains(x, y)) {//ACTION_DOWN是否在这个区域内 mDelegateTargeted = true;//标记这一次触摸事件在这个区域(在mDelegateView内) 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) {//这次触摸有效:则把触摸事件传递给 mDelegateView final View delegateView = mDelegateView; //模拟这次触摸真的在mDelegateView区域内:从新计算event的触摸点,以保证这次的event事件的触摸点在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);//把计算后的触摸事件传递给mDelegateView的dispatchTouchEvent使其相应触摸事件. } return handled; }
public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if (DBG_MOTION) { Xlog.d(VIEW_LOG_TAG, "(View)onTouchEvent 1: event = " + event + ",mTouchDelegate = " + mTouchDelegate + ",enable = " + isEnabled() + ",clickable = " + isClickable() + ",isLongClickable = " + isLongClickable() + ",this = " + this); } if ((viewFlags & ENABLED_MASK) == DISABLED) { /// M: we need to reset the pressed state or remove prepressed callback either up or cancel event happens. final int action = event.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if ((mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } else if ((mPrivateFlags & PFLAG_PREPRESSED) != 0) { Xlog.d(VIEW_LOG_TAG, "View onTouch event, if view is DISABLED & PFLAG_PREPRESSED, remove callback mPrivateFlags = " + mPrivateFlags + ", this = " + this); removeTapCallback(); } } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); } if (mTouchDelegate != null) {//判断是否有TouchDelegate对象 if (mTouchDelegate.onTouchEvent(event)) {执行onTouchEvent return true;//如果这个触摸事件在TouchDelegate设置的区域内,这返回.不会执行其他 } } .....
最后,来看看一个实例.下面是一个父view夸大自己view触摸区域的方法:
public static void enlargeBtnTouchScope(View parent, Button btn, Rect rect, float width, float height){ rect.top = btn.getTop(); rect.bottom = btn.getBottom(); rect.left = btn.getLeft(); rect.right = btn.getRight(); rect.top -= height; rect.bottom += height; rect.left -= width; rect.right += width; parent.setTouchDelegate(new TouchDelegate(rect, btn)); }上面的代码可以使得在parent_view的button可以更好地被触摸到(被点击到).