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可以更好地被触摸到(被点击到).