Android OnTouchEvent和OnClick、OnLongClick、OnTouch、TouchDelegate關係
阿新 • • 發佈:2019-02-05
說明:本部落格為原創,轉載請註明出處 http://blog.csdn.net/gucun4848
由於作者水平有限,錯誤在所難免,請見諒,可以留言,本人會及時改正
索引
基於上篇Android Touch事件總結一,本篇介紹Touch事件和Click、LongClick、Touch、TouchDelegate的關係。
OnClick
public void setOnClickListener(OnClickListener l){
//註冊點選事件
}
用法很簡單這裡只介紹下View什麼時候觸發這個回撥事件,原始碼如下:
public boolean onTouchEvent(MotionEvent event)
...
switch (action) {
case MotionEvent.ACTION_UP:
...
// take focus if we don't have it already and we should in touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent){
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//觸發OnClick事件
if (!post(mPerformClick)) {
performClick();
}
}
}
...
// 可以看到,在OnTouchEvent方法中,接收到ACTION_UP事件後,判斷如果View當前不是FocusableInTouchMode狀態,沒有LongPressed狀態,觸發OnClick事件。
OnLongClick
public void setOnLongClickListener(OnLongClickListener l){
//註冊長按事件
}
用法很簡單這裡只介紹下View什麼時候觸發這個回撥事件,原始碼如下:
public boolean onTouchEvent(MotionEvent event)
...
//還是在OnTouchEvent方法,接收到DOWN事件後,通過PostDelayed()實現
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
// mPendingCheckForTap中觸發事件
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
// 觸發事件
checkForLongClick(0, x, y);
}
break;
OnTouchListener
public void setOnTouchListener(OnTouchListener l){
//註冊觸控事件,這個回撥會攔截View自身的OnTouchEvent方法。
}
用法很簡單這裡只介紹下View什麼時候觸發這個回撥事件,原始碼如下:
public boolean dispatchTouchEvent(MotionEvent event) {
...
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
}
// 在分發事件中先觸發OnTouch回撥,如果OnTouch回撥中不消費事件,才會觸發View自身的OnTouchEvent方法。
setTouchDelegate
public void setTouchDelegate(TouchDelegate l){
//註冊觸控委派事件,這個回撥常用於增加自身的可觸控面積。
//例項TouchDelegate物件需要兩個引數Rect bounds, View delegateView
//bounds 觸控區域大小
//delegateView 委派事件的View
//這裡需要**注意**的是需要delegateView的ParentView呼叫setTouchDelegate()方法!
}
這裡看下View的原始碼:
public boolean onTouchEvent(MotionEvent event) {
...
//在View的OnTouchEvent方法中,優先呼叫的委派事件
//這裡需要注意的是委派事件的View的ParentView必須保證自身的onTouchEvnet方法會被呼叫!
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
...
}
//這裡是TouchDelegate中的onTouchEvent方法
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)) {
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));
}
//呼叫了委派事件的View的dispatchTouchEvent方法.
handled = delegateView.dispatchTouchEvent(event);
}
return handled;
}
Demo
GitHub地址: GitHub
環境: Windows7+JAVA8
IDE: AndroidStdio2.2.2
compileSdkVersion:24
測試裝置:Nexus5(6.0.1)
執行Demo的時候需要在Manifest.xml中啟動項設定成MainViewTouchClickActivity
點選灰色區域,可以觸發黃色View的OnClick事件