android 点击 长按事件,Android系统中长按事件的实现机制解析

在Android的触摸消息中,已经实现了三种监测,它们分别是

1)pre-pressed:对应的语义是用户轻触(tap)了屏幕

2)pressed:对应的语义是用户点击(press)了屏幕

3)long pressed:对应的语义是用户长按(long press)了屏幕

下图是触摸消息随时间变化的时间轴示意图:

a02854d07ea44a9fc0be82e6d8de2271.png

其中,t0和t1定义在ViewConfiguration类中,标识了tap和longpress的超时时间,定义如下:

/**

* Defines the duration in milliseconds we will wait to see if a touch event

* is a tap or a scroll. If the user does not move within this interval, it is

* considered to be a tap.

*/

privatestaticfinalintTAP_TIMEOUT =115;// t0

/**

* Defines the duration in milliseconds before a press turns into

* a long press

*/

privatestaticfinalintLONG_PRESS_TIMEOUT =500;// t1代码中实现监测的地方在View类的OnTouchEvent函数中,当View监测到ACTION_DOWN事件时,首先发送一个延迟为t0的异步消息,代码如下:

caseMotionEvent.ACTION_DOWN:

if(mPendingCheckForTap ==null) {

mPendingCheckForTap =newCheckForTap();

}

mPrivateFlags |= PREPRESSED;

mHasPerformedLongPress =false;

postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());

break;如果在t0时间内用户释放了屏幕,即ACTION_UP事件在t0时间内发生,则本次触摸对应的是pre-pressed处理代码,语义是"用户轻触(TAP)了一下屏幕";如果用户在t1时间内释放了屏幕,那么本次操作是一个"press"操作;如果用户超过t1时间释放屏幕,则系统认为监测到了长按事件。其中处理"press"操作的代码在类CheckForTap类中,处理长按操作的代码在类CheckForLongPress类中。而处理pre-pressed的代码在ACTION_UP事件响应中,ACTION_UP事件响应中大部分代码用于处理触摸的状态变化,如下所示:

caseMotionEvent.ACTION_UP:

booleanprepressed = (mPrivateFlags & PREPRESSED) !=0;//获取prepressed状态

if((mPrivateFlags & PRESSED) !=0|| prepressed) {//如果是pressed状态或者是prepressed状态,才进行处理

// 如果当前view不具有焦点,则需要先获取焦点,因为我们当前处理触摸模式

booleanfocusTaken =false;

if(isFocusable() && isFocusableInTouchMode() && !isFocused()) {

focusTaken = requestFocus();// 请求获得焦点

}

if(!mHasPerformedLongPress) {// 是否处理过长按操作了,如果是,则直接返回

// 进入该代码段,说明这是一个tap操作,首先移除长按回调操作

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 =newPerformClick();

}

if(!post(mPerformClick)) {

performClick();// 执行点击的处理函数

}

}

}

if(mUnsetPressedState ==null) {

mUnsetPressedState =newUnsetPressedState();

}

if(prepressed) {

mPrivateFlags |= PRESSED;

refreshDrawableState();

// 发送重置触摸状态的异步延迟消息

postDelayed(mUnsetPressedState,

ViewConfiguration.getPressedStateDuration());

}elseif(!post(mUnsetPressedState)) {

// If the post failed, unpress right now

mUnsetPressedState.run();

}

removeTapCallback();// 移除tap的回调操作

}

break;在上面的代码分析中,可以看出,整个监测过程涉及到两个Runnable对象和一个利用Handler发送异步延迟消息的函数,下面就来分析一下:

1)PostDelayed函数

该函数的主要工作是获取UI线程的Handler对象,然后调用Handler类的postDelayed函数将指定的Runnable对象放到消息队列中。

publicbooleanpostDelayed(Runnable action,longdelayMillis) {

Handler handler;

if(mAttachInfo !=null) {

handler = mAttachInfo.mHandler;

}else{

// Assume that post will succeed later

ViewRoot.getRunQueue().postDelayed(action, delayMillis);

returntrue;

}

returnhandler.postDelayed(action, delayMillis);

}2)CheckForTap类

该类实现了Runnable接口,在run函数中设置触摸标识,并刷新Drawable的状态,同时用于发送一个检测长按事件的异步延迟消息,代码如下:

privatefinalclassCheckForTapimplementsRunnable {

publicvoidrun() {

// 进入该函数,说明已经过了ViewConfiguration.getTapTimeout()时间,

// 即pre-pressed状态结束,宣告触摸进入pressed状态

mPrivateFlags &= ~PREPRESSED;

mPrivateFlags |= PRESSED;

refreshDrawableState();// 刷新控件的背景Drawable

// 如果长按检测没有被去使能,则发送一个检测长按事件的异步延迟消息

if((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {

postCheckForLongClick(ViewConfiguration.getTapTimeout());

}

}

}

privatevoidpostCheckForLongClick(intdelayOffset) {

mHasPerformedLongPress =false;

// 实例化CheckForLongPress对象

if(mPendingCheckForLongPress ==null) {

mPendingCheckForLongPress =newCheckForLongPress();

}

mPendingCheckForLongPress.rememberWindowAttachCount();

// 调用PostDelayed函数发送长按事件的异步延迟消息

postDelayed(mPendingCheckForLongPress,

ViewConfiguration.getLongPressTimeout() - delayOffset);

}

3)CheckForLongPress类

该类定义了长按操作发生时的响应处理,同样实现了Runnable接口

classCheckForLongPressimplementsRunnable {

privateintmOriginalWindowAttachCount;

publicvoidrun() {

// 进入该函数,说明检测到了长按操作

if(isPressed() && (mParent !=null)

&& mOriginalWindowAttachCount == mWindowAttachCount) {

if(performLongClick()) {

mHasPerformedLongPress =true;

}

}

}

publicvoidrememberWindowAttachCount() {

mOriginalWindowAttachCount = mWindowAttachCount;

}

}

publicbooleanperformLongClick() {

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);

booleanhandled =false;

if(mOnLongClickListener !=null) {

// 回调用户实现的长按操作监听函数(OnLongClickListener)

handled = mOnLongClickListener.onLongClick(View.this);

}

if(!handled) {

// 如果OnLongClickListener的onLongClick返回false

// 则需要继续处理该长按事件,这里是显示上下文菜单

handled = showContextMenu();

}

if(handled) {

// 长按操作事件被处理了,此时应该给用户触觉上的反馈

performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);

}

returnhandled;

}0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值