android的widget在鼠标划过时自动聚焦
来源:互联网 发布:游戏编程之从零开始 编辑:程序博客网 时间:2024/04/29 23:58
android系统现在被移植到了各式各样的设备上,但它毕竟是为手机设计的操作系统,一些功能不是很完善。比如一些launcher模仿苹果做的dock,在使用鼠标的设备上,当鼠标放到dock里的图标上时,没有任何反应,只能用键盘的方向键激活它的动画。这篇文章就是尝试解决这个问题的笔记。
首先要明确一下android对鼠标是怎样支持的。据说原生的android是不支持鼠标的,后来在hacker们的努力下诞生了x86版本的系统并支持鼠标。
这里只大概描述一下framework层对鼠标的支持。第一次接收到鼠标事件后,WindowManagerService开始绘制鼠标光标,并处理鼠标事件。
RawInputEvent.CLASS_MOUSE为鼠标事件,属于Pointer事件,使用dispatchPointer函数分发事件。左键按下映射为ACTION_DOWN,左键释放映射为ACTION_UP,滑动映射为ACTION_MOVE。
参考:http://hi.baidu.com/haijiaoshu/blog/item/b65591084da31f24e824884e.html
捕获事件并传递的代码:
WindowManagerService.java:
case RawInputEvent.CLASS_MOUSE:
MotionEvent mmev = (MotionEvent)ev.event;
int mcx = (int)mmev.getX();
int mcy = (int)mmev.getY();
if (mMlx != mcx || mMly != mcy) {
mMlx = mcx;
mMly = mcy;
if (!mMouseDisplayed)
mMouseDisplayed = true;
requestAnimationLocked(0);
}
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
对于ACTION_DOWN,WindowManagerService会为它查找target window,同时把mKeyWaiter.mMotionTarget设置为找到的窗口。
WindowManagerService.java:dispatchPointer函数:
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, true, false, pid, uid);
mKeyWaiter.waitForNextEventTarget函数:
Object target = findTargetWindow(nextKey, qev, nextMotion, isPointerEvent, callingPid, callingUid);
对于ACTION_UP,会直接使用mKeyWaiter.mMotionTarget的值,并把mKeyWaiter.mMotionTarget重新设置为NULL——因为ACTION_UP之前肯定有ACTION_DOWN,并且在UP事件后就应该释放对象处理其他事件了。
WindowManagerService.java:dispatchPointer函数:
if (action == MotionEvent.ACTION_UP) {
// let go of our target
mKeyWaiter.mMotionTarget = null;
mPowerManager.logPointerUpEvent();
} else if (action == MotionEvent.ACTION_DOWN) {
mPowerManager.logPointerDownEvent();
}
对于ACTION_MOVE,如果mKeyWaiter.mMotionTarget不为NULL则传递事件,否则直接丢弃事件。因此平时鼠标的滑动不应该产生任何效果,但按下左键后滑动鼠标意味着要拖动对象。
最后,向窗口传递事件。
WindowManagerService.java:dispatchPointer函数:
target.mClient.dispatchPointer(ev, eventTime, true);
事件由窗口里的ViewRoot承接,并传递给窗口中的View部件。ViewRoot是一个处理消息的handler,具体的请参考其他android资料。
ViewRoot.java:handleMessage函数:
handled = mView.dispatchTouchEvent(event);
第一个接收到消息的是ViewGroup——在android里,各个View部件是附着在ViewGroup里的,ViewGroup互相嵌套成为View层次树。ViewGroup根据事件类型和坐标,传递到子部件上,直到被处理。具体可参看ViewGroup.java的代码。
参考:http://blog.csdn.net/ddna/article/details/5473293
最后是View部件处理按键事件。如果注册了回调函数则回调,否则就用默认的方法处理。
View.java:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
了解以上过程后,就知道怎么做了。首先要修改的是WindowManagerService.java的dispatchPointer方法,为ACTION_MOVE寻找target window——可以把处理ACTION_DOWN的部分代码拷过来,注意不要去设置mKeyWaiter.mMotionTarget的值。为了避免影响其他事件的传递,我把事件码改成了12,并把代码放到return前执行。
然后修改ViewGroup.java,为这个事件寻找View子部件并传递事件。这样事件便可以传递到app上了。
最后修改app,检测到这个事件就requestFocus——重写OnTouch函数,不过为了不影响其他事件的处理,需要先调用父类的OnTouch函数。
首先要明确一下android对鼠标是怎样支持的。据说原生的android是不支持鼠标的,后来在hacker们的努力下诞生了x86版本的系统并支持鼠标。
这里只大概描述一下framework层对鼠标的支持。第一次接收到鼠标事件后,WindowManagerService开始绘制鼠标光标,并处理鼠标事件。
RawInputEvent.CLASS_MOUSE为鼠标事件,属于Pointer事件,使用dispatchPointer函数分发事件。左键按下映射为ACTION_DOWN,左键释放映射为ACTION_UP,滑动映射为ACTION_MOVE。
参考:http://hi.baidu.com/haijiaoshu/blog/item/b65591084da31f24e824884e.html
捕获事件并传递的代码:
WindowManagerService.java:
case RawInputEvent.CLASS_MOUSE:
MotionEvent mmev = (MotionEvent)ev.event;
int mcx = (int)mmev.getX();
int mcy = (int)mmev.getY();
if (mMlx != mcx || mMly != mcy) {
mMlx = mcx;
mMly = mcy;
if (!mMouseDisplayed)
mMouseDisplayed = true;
requestAnimationLocked(0);
}
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
对于ACTION_DOWN,WindowManagerService会为它查找target window,同时把mKeyWaiter.mMotionTarget设置为找到的窗口。
WindowManagerService.java:dispatchPointer函数:
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, true, false, pid, uid);
mKeyWaiter.waitForNextEventTarget函数:
Object target = findTargetWindow(nextKey, qev, nextMotion, isPointerEvent, callingPid, callingUid);
对于ACTION_UP,会直接使用mKeyWaiter.mMotionTarget的值,并把mKeyWaiter.mMotionTarget重新设置为NULL——因为ACTION_UP之前肯定有ACTION_DOWN,并且在UP事件后就应该释放对象处理其他事件了。
WindowManagerService.java:dispatchPointer函数:
if (action == MotionEvent.ACTION_UP) {
// let go of our target
mKeyWaiter.mMotionTarget = null;
mPowerManager.logPointerUpEvent();
} else if (action == MotionEvent.ACTION_DOWN) {
mPowerManager.logPointerDownEvent();
}
对于ACTION_MOVE,如果mKeyWaiter.mMotionTarget不为NULL则传递事件,否则直接丢弃事件。因此平时鼠标的滑动不应该产生任何效果,但按下左键后滑动鼠标意味着要拖动对象。
最后,向窗口传递事件。
WindowManagerService.java:dispatchPointer函数:
target.mClient.dispatchPointer(ev, eventTime, true);
事件由窗口里的ViewRoot承接,并传递给窗口中的View部件。ViewRoot是一个处理消息的handler,具体的请参考其他android资料。
ViewRoot.java:handleMessage函数:
handled = mView.dispatchTouchEvent(event);
第一个接收到消息的是ViewGroup——在android里,各个View部件是附着在ViewGroup里的,ViewGroup互相嵌套成为View层次树。ViewGroup根据事件类型和坐标,传递到子部件上,直到被处理。具体可参看ViewGroup.java的代码。
参考:http://blog.csdn.net/ddna/article/details/5473293
最后是View部件处理按键事件。如果注册了回调函数则回调,否则就用默认的方法处理。
View.java:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
了解以上过程后,就知道怎么做了。首先要修改的是WindowManagerService.java的dispatchPointer方法,为ACTION_MOVE寻找target window——可以把处理ACTION_DOWN的部分代码拷过来,注意不要去设置mKeyWaiter.mMotionTarget的值。为了避免影响其他事件的传递,我把事件码改成了12,并把代码放到return前执行。
然后修改ViewGroup.java,为这个事件寻找View子部件并传递事件。这样事件便可以传递到app上了。
最后修改app,检测到这个事件就requestFocus——重写OnTouch函数,不过为了不影响其他事件的处理,需要先调用父类的OnTouch函数。
详情可参看附件的代码。
WindowManagerService.java
private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG, "dispatchPointer " + ev); if (MEASURE_LATENCY) { lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano); } Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, true, false, pid, uid); if (MEASURE_LATENCY) { lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano); } int action = ev.getAction(); if (action == MotionEvent.ACTION_UP) { // let go of our target mKeyWaiter.mMotionTarget = null; mPowerManager.logPointerUpEvent(); } else if (action == MotionEvent.ACTION_DOWN) { mPowerManager.logPointerDownEvent(); } if (targetObj == null) { // In this case we are either dropping the event, or have received // a move or up without a down. It is common to receive move // events in such a way, since this means the user is moving the // pointer without actually pressing down. All other cases should // be atypical, so let's log them. if (action != MotionEvent.ACTION_MOVE) { Slog.w(TAG, "No window to dispatch pointer action " + ev.getAction()); }
if (action == MotionEvent.ACTION_MOVE) { long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents; long now = SystemClock.uptimeMillis(); if (now < nextEventTime) { if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); return INJECT_FAILED; } else { mLastTouchEventTime = now; } } synchronized (mWindowMap) { dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true); }///////////////////////////////bingbowan action = ev.getAction(); final float xf = ev.getX(); final float yf = ev.getY(); final long eventTime = ev.getEventTime(); final int x = (int)xf; final int y = (int)yf; final ArrayList windows = mWindows; final int N = windows.size(); WindowState topErrWindow = null; final Rect tmpRect = mTempRect; for (int i=N-1; i>=0; i--) { WindowState child = (WindowState)windows.get(i); //Slog.i(TAG, "Checking dispatch to: " + child); final int flags = child.mAttrs.flags; if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { if (topErrWindow == null) { topErrWindow = child; } } if (!child.isVisibleLw()) { //Slog.i(TAG, "Not visible!"); continue; } tmpRect.set(child.mFrame); if (child.mTouchableInsets == ViewTreeObserver .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) { // The touch is inside of the window if it is // inside the frame, AND the content part of that // frame that was given by the application. tmpRect.left += child.mGivenContentInsets.left; tmpRect.top += child.mGivenContentInsets.top; tmpRect.right -= child.mGivenContentInsets.right; tmpRect.bottom -= child.mGivenContentInsets.bottom; } else if (child.mTouchableInsets == ViewTreeObserver .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) { // The touch is inside of the window if it is // inside the frame, AND the visible part of that // frame that was given by the application. tmpRect.left += child.mGivenVisibleInsets.left; tmpRect.top += child.mGivenVisibleInsets.top; tmpRect.right -= child.mGivenVisibleInsets.right; tmpRect.bottom -= child.mGivenVisibleInsets.bottom; } final int touchFlags = flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); if (tmpRect.contains(x, y) || touchFlags == 0) { //Slog.i(TAG, "Using this target!"); final boolean screenOff = qev != null && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; if (!screenOff || (flags & WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) { targetObj = child; } break; } if ((flags & WindowManager.LayoutParams .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { child.mNextOutsideTouch = mKeyWaiter.mOutsideTouchTargets; targetObj = child; //Slog.i(TAG, "Adding to outside target list: " + child); } } // if there's an error window but it's not accepting // focus (typically because it is not yet visible) just // wait for it -- any other focused window may in fact // be in ANR state. if (topErrWindow != null && targetObj != topErrWindow) { targetObj = null; } if (action == MotionEvent.ACTION_MOVE) { long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents; long now = SystemClock.uptimeMillis(); if (now < nextEventTime) { try { Thread.sleep(nextEventTime - now); } catch (InterruptedException e) { } mLastTouchEventTime = nextEventTime; } else { mLastTouchEventTime = now; } } ev.setAction(12); try { if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) { Slog.v(TAG, "Delivering pointer " + qev + " Ev " + ev + " to " + (WindowState)targetObj); } ((WindowState)targetObj).mClient.dispatchPointer(ev, eventTime, true); } catch (android.os.RemoteException e) { Slog.i(TAG, "WINDOW DIED during motion dispatch: " + (WindowState)targetObj); mKeyWaiter.mMotionTarget = null; try { removeWindow(((WindowState)targetObj).mSession, ((WindowState)targetObj).mClient); } catch (java.util.NoSuchElementException ex) { // This will happen if the window has already been // removed. } } if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle();//////////////////bingbowan return INJECT_FAILED; } if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { synchronized (mWindowMap) { dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true); } if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); return INJECT_SUCCEEDED; } WindowState target = (WindowState)targetObj; final long eventTime = ev.getEventTime(); final long eventTimeNano = ev.getEventTimeNano(); //Slog.i(TAG, "Sending " + ev + " to " + target); if (uid != 0 && uid != target.mSession.mUid) { if (mContext.checkPermission( android.Manifest.permission.INJECT_EVENTS, pid, uid) != PackageManager.PERMISSION_GRANTED) { Slog.w(TAG, "Permission denied: injecting pointer event from pid " + pid + " uid " + uid + " to window " + target + " owned by uid " + target.mSession.mUid); if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); return INJECT_NO_PERMISSION; } } if (MEASURE_LATENCY) { lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano); } if ((target.mAttrs.flags & WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { //target wants to ignore fat touch events boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev); //explicit flag to return without processing event further boolean returnFlag = false; if((action == MotionEvent.ACTION_DOWN)) { mFatTouch = false; if(cheekPress) { mFatTouch = true; returnFlag = true; } } else { if(action == MotionEvent.ACTION_UP) { if(mFatTouch) { //earlier even was invalid doesnt matter if current up is cheekpress or not mFatTouch = false; returnFlag = true; } else if(cheekPress) { //cancel the earlier event ev.setAction(MotionEvent.ACTION_CANCEL); action = MotionEvent.ACTION_CANCEL; } } else if(action == MotionEvent.ACTION_MOVE) { if(mFatTouch) { //two cases here //an invalid down followed by 0 or moves(valid or invalid) //a valid down, invalid move, more moves. want to ignore till up returnFlag = true; } else if(cheekPress) { //valid down followed by invalid moves //an invalid move have to cancel earlier action ev.setAction(MotionEvent.ACTION_CANCEL); action = MotionEvent.ACTION_CANCEL; if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE"); //note that the subsequent invalid moves will not get here mFatTouch = true; } } } //else if action if(returnFlag) { //recycle que, ev if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); return INJECT_FAILED; } } //end if target // Enable this for testing the "right" value if (false && action == MotionEvent.ACTION_DOWN) { int max_events_per_sec = 35; try { max_events_per_sec = Integer.parseInt(SystemProperties .get("windowsmgr.max_events_per_sec")); if (max_events_per_sec < 1) { max_events_per_sec = 35; } } catch (NumberFormatException e) { } mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec; } /* * Throttle events to minimize CPU usage when there's a flood of events * e.g. constant contact with the screen */ if (action == MotionEvent.ACTION_MOVE) { long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents; long now = SystemClock.uptimeMillis(); if (now < nextEventTime) { try { Thread.sleep(nextEventTime - now); } catch (InterruptedException e) { } mLastTouchEventTime = nextEventTime; } else { mLastTouchEventTime = now; } } if (MEASURE_LATENCY) { lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano); } synchronized(mWindowMap) { if (!target.isVisibleLw()) { // During this motion dispatch, the target window has become // invisible. dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false); if (qev != null) { mQueue.recycleEvent(qev); } ev.recycle(); return INJECT_SUCCEEDED; } if (qev != null && action == MotionEvent.ACTION_MOVE) { mKeyWaiter.bindTargetWindowLocked(target, KeyWaiter.RETURN_PENDING_POINTER, qev); ev = null; } else { if (action == MotionEvent.ACTION_DOWN) { WindowState out = mKeyWaiter.mOutsideTouchTargets; if (out != null) { MotionEvent oev = MotionEvent.obtain(ev); oev.setAction(MotionEvent.ACTION_OUTSIDE); do { final Rect frame = out.mFrame; oev.offsetLocation(-(float)frame.left, -(float)frame.top); try { out.mClient.dispatchPointer(oev, eventTime, false); } catch (android.os.RemoteException e) { Slog.i(TAG, "WINDOW DIED during outside motion dispatch: " + out); } oev.offsetLocation((float)frame.left, (float)frame.top); out = out.mNextOutsideTouch; } while (out != null); mKeyWaiter.mOutsideTouchTargets = null; } } dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false); final Rect frame = target.mFrame; ev.offsetLocation(-(float)frame.left, -(float)frame.top); mKeyWaiter.bindTargetWindowLocked(target); } } // finally offset the event to the target's coordinate system and // dispatch the event. try { if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) { Slog.v(TAG, "Delivering pointer " + qev + " Ev " + ev + " to " + target); } if (MEASURE_LATENCY) { lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano); } target.mClient.dispatchPointer(ev, eventTime, true); if (MEASURE_LATENCY) { lt.sample("7 after svr->client ipc ", System.nanoTime() - eventTimeNano); } return INJECT_SUCCEEDED; } catch (android.os.RemoteException e) { Slog.i(TAG, "WINDOW DIED during motion dispatch: " + target); mKeyWaiter.mMotionTarget = null; try { removeWindow(target.mSession, target.mClient); } catch (java.util.NoSuchElementException ex) { // This will happen if the window has already been // removed. } } return INJECT_FAILED; }
ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) { Log.i("bingbowan", "action"+ev.getAction()+"reach viewgroup"); final int action = ev.getAction(); final float xf = ev.getX(); final float yf = ev.getY(); final float scrolledXFloat = xf + mScrollX; final float scrolledYFloat = yf + mScrollY; final Rect frame = mTempRect; boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;////////////////bingbowan if (action == 12) { // If we're disallowing intercept or if we're allowing and we didn't // intercept if (disallowIntercept || !onInterceptTouchEvent(ev)) { // We know we want to dispatch the event down, find a child // who can handle it, start with the front-most child. final int scrolledXInt = (int) scrolledXFloat; final int scrolledYInt = (int) scrolledYFloat; final View[] children = mChildren; final int count = mChildrenCount; for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { // offset the event to the view's coordinate system final float xc = scrolledXFloat - child.mLeft; final float yc = scrolledYFloat - child.mTop; ev.setLocation(xc, yc); if (child.dispatchTouchEvent(ev)) { return true; } // The event didn't get handled, try the next view. // Don't reset the event's location, it's not // necessary here. } } } }}/////////////////bingbowan if (action == MotionEvent.ACTION_DOWN) { if (mMotionTarget != null) { // this is weird, we got a pen down, but we thought it was // already down! // XXX: We should probably send an ACTION_UP to the current // target. mMotionTarget = null; } // If we're disallowing intercept or if we're allowing and we didn't // intercept if (disallowIntercept || !onInterceptTouchEvent(ev)) { // reset this event's action (just to protect ourselves) ev.setAction(MotionEvent.ACTION_DOWN); // We know we want to dispatch the event down, find a child // who can handle it, start with the front-most child. final int scrolledXInt = (int) scrolledXFloat; final int scrolledYInt = (int) scrolledYFloat; final View[] children = mChildren; final int count = mChildrenCount; for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { // offset the event to the view's coordinate system final float xc = scrolledXFloat - child.mLeft; final float yc = scrolledYFloat - child.mTop; ev.setLocation(xc, yc); child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; if (child.dispatchTouchEvent(ev)) { // Event handled, we have a target now. mMotionTarget = child; return true; } // The event didn't get handled, try the next view. // Don't reset the event's location, it's not // necessary here. } } } } } boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { // Note, we've already copied the previous state to our local // variable, so this takes effect on the next event mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } // The event wasn't an ACTION_DOWN, dispatch it to our target if // we have one. final View target = mMotionTarget; if (target == null) { // We don't have a target, this means we're handling the // event as a regular view. ev.setLocation(xf, yf); if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { ev.setAction(MotionEvent.ACTION_CANCEL); mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; } return super.dispatchTouchEvent(ev); } // if have a target, see if we're allowed to and want to intercept its // events if (!disallowIntercept && onInterceptTouchEvent(ev)) { final float xc = scrolledXFloat - (float) target.mLeft; final float yc = scrolledYFloat - (float) target.mTop; mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; ev.setAction(MotionEvent.ACTION_CANCEL); ev.setLocation(xc, yc); if (!target.dispatchTouchEvent(ev)) { // target didn't handle ACTION_CANCEL. not much we can do // but they should have. } // clear the target mMotionTarget = null; // Don't dispatch this event to our own view, because we already // saw it when intercepting; we just want to give the following // event to the normal onTouchEvent(). return true; } if (isUpOrCancel) { mMotionTarget = null; } // finally offset the event to the target's coordinate system and // dispatch the event. final float xc = scrolledXFloat - (float) target.mLeft; final float yc = scrolledYFloat - (float) target.mTop; ev.setLocation(xc, yc); if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { ev.setAction(MotionEvent.ACTION_CANCEL); target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; mMotionTarget = null; } return target.dispatchTouchEvent(ev); }
测试用的button
button1.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { Log.i("bingbowan", "button1 in app"); v.onTouchEvent(event); if (event.getAction() == 12) { //button1.setFocusableInTouchMode(true); if (button1.requestFocus()==false) Log.i("bingbowan", "button1 focus fail"); } return true; } });
- android的widget在鼠标划过时自动聚焦
- 鼠标划过时整行变色代码
- datagridview 鼠标划过时字体变粗。
- 《div图层被鼠标划过时其背景色变色的五种方式》
- Div被鼠标划过时其背景色变色的第六种方式
- DataWindow.net中如何实现鼠标划过时变颜色
- 鼠标划过时,预览大图片(浮动层)
- android系统的自动划屏测试
- 鼠标从datagrid上划过时,背景色发生变化,点击任何一个单元格的时候选中一行
- android摄像机自动聚焦
- android Recycle view 放置在能够滑动的view(scrollerview等)自动上划显示的问题
- android widget 自动删除
- 使用js在网页上记录鼠标划圈的小程序
- html代码实现自动滚动,鼠标滑过时停止滚动
- html代码实现自动滚动,鼠标滑过时停止滚动
- 简单实现android camera自动聚焦
- jquery dialog打开的时候,自动聚焦在第一个控件上
- openCV基于图像处理的自动聚焦
- 似乎应该说些什么——记人生第一场regional
- SQLite的SQL语法
- Lisp语言发明者 人工智能奠基人 John McCarthy昨日病逝
- Emacs Lisp 功能扩展集锦
- 利用quagga实现动态路由
- android的widget在鼠标划过时自动聚焦
- 读取U盘物理序列号
- City0
- 添加字体
- 动态创建数据窗口
- 使用SQL语句创建数据窗口
- 将图片逆(顺)时针旋转任意角度的函数
- 称呼自己的方法
- Log4j配置