View的OnLongClickListener事件实现原理是什么,又是如何触发(postDelayed),长按时间多久呢(0.4s)?
1、通过源码我们发现View.setOnLLongClickListener()方法中:
setLongClickable(true);
getListenerInfo().mOnLongClickListener = l;
public void setOnLongClickListener(@Nullable OnLongClickListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
getListenerInfo().mOnLongClickListener = l;
}
2、那么它是如何触发长按监听回调的呢,通过监听我们发现它是在performLongClickInternal()触发回调的
private boolean performLongClickInternal(float x, float y) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
boolean handled = false;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
//长按回调
handled = li.mOnLongClickListener.onLongClick(View.this);
}
if (!handled) {
final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
}
if ((mViewFlags & TOOLTIP) == TOOLTIP) {
if (!handled) {
handled = showLongClickTooltip((int) x, (int) y);
}
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
}
3、那么让我来看看上层调用方法:
①、performLongClick()
public boolean performLongClick() {
return performLongClickInternal(mLongClickX, mLongClickY);
}
②、performLongClick(float x,float y)
public boolean performLongClick(float x, float y) {
mLongClickX = x;
mLongClickY = y;
final boolean handled = performLongClick();
mLongClickX = Float.NaN;
mLongClickY = Float.NaN;
return handled;
}
4、接下来,我们继续看performLongClick(float x,float y)却在CheckForLongPress runnable 中调用,那么长按用就是view.postDelayed()吗?
①、CheckForLongPress类
private final class CheckForLongPress implements Runnable {
private int mOriginalWindowAttachCount;
private float mX;
private float mY;
private boolean mOriginalPressedState;
/**
* The classification of the long click being checked: one of the
* FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants.
*/
private int mClassification;
@UnsupportedAppUsage
private CheckForLongPress() {
}
@Override
public void run() {
if ((mOriginalPressedState == isPressed()) && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
recordGestureClassification(mClassification);
if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
}
}
public void setAnchor(float x, float y) {
mX = x;
mY = y;
}
public void rememberWindowAttachCount() {
mOriginalWindowAttachCount = mWindowAttachCount;
}
public void rememberPressedState() {
mOriginalPressedState = isPressed();
}
public void setClassification(int classification) {
mClassification = classification;
}
}
②、CheckForLongPress创建,确实是通过postDelayed来延迟生效的
private void checkForLongClick(long delay, float x, float y, int classification) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
mPendingCheckForLongPress.rememberPressedState();
mPendingCheckForLongPress.setClassification(classification);
postDelayed(mPendingCheckForLongPress, delay);
}
}
③、哪些调用checkForLongClick方法呢?
④、我们发现前五都是View事件,来触发的。
⑤、那么第六个View.CheckForTap又是干什么呢?
发现其实也是View.onTouchEvent里面的 MotionEvent.ACTION_DOWN 触发的
自此我们可以说:View.OnLongClickListener 是通过View.postDelayed()触发长按回调。
3、触发时间
①、View.onKeyDown调用:ViewConfiguration.getLongPressTimeout()
public static final int DEFAULT_LONG_PRESS_TIMEOUT = 400;
public static int getLongPressTimeout() {
return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
DEFAULT_LONG_PRESS_TIMEOUT);
}
②、View.onTouchEvent()调用
MotionEvent.ACTION_DOWN
MotionEvent.ACTION_MOVE
通过源码我们发现,一般触发长按事件的时长围为0.4s。