详细介绍Android中回调函数机制

提示:在阅读本文章之前,请确保您对Touch事件的分发机制有一定的了解

在Android的学习过程中经常会听到或者见到“回调”这个词,那么什么是回调呢?所谓的回调函数就是:在A类中定义了一个方法,这个方法中用到了一个接口和该接口中的抽象方法,但是抽象方法没有具体的实现,需要B类去实现,B类实现该方法后,它本身不会去调用该方法,而是传递给A类,供A类去调用,这种机制就称为回调。

下面我们拿具体的Button的点击事件进行模拟分析:

首先,在View类中我们能找到setOnClickListener(OnClickListener l)方法:

复制代码 代码如下:
public void setOnClickListener(OnClickListener l) {

if (!isClickable()) {

setClickable(true);

}

getListenerInfo().mOnClickListener = l;

}

可以看到,在该方法中将OnClickListener赋值给了mOnClickListener,那么我们继续向下找,会看到在performClick()方法中执行了我们实现的onClick()方法。

复制代码 代码如下:
public boolean performClick() {

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnClickListener != null) {

playSoundEffect(SoundEffectConstants.CLICK);

li.mOnClickListener.onClick(this);

return true;

}

return false;

}

由此我们可以清楚的看不到,在父类中我们要用到onClick()方法,但是父类却没有去实现该方法,而是定义了一个方法setOnClickListener(OnClickListener l),如果子类想要自己能够响应点击事件,则它就必须重写父类的该方法,实现OnClickListener接口和它的onClick()方法。在子类实现该接口和方法后,将其通过参数传递给父类,在父类中执行onClick()方法。

那么,为什么会在父类中执行到该方法呢,这就要说到Android中的另一个重要的机制——触摸事件的传递机制。

我们知道,只要我们的手指触摸到手机屏幕,就一定会执行dispatchTouchEvent(MotionEvent event)方法,接下来我们就看一下dispatchTouchEvent方法中都有哪些内容:

复制代码 代码如下:
public boolean dispatchTouchEvent(MotionEvent event) {

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onTouchEvent(event, 0);

}

if (onFilterTouchEventForSecurity(event)) {

//noinspection SimplifiableIfStatement

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnTouchListener != null

&& (mViewFlags & ENABLED_MASK) == ENABLED

&& li.mOnTouchListener.onTouch(this, event)) {

return true;

}

if (onTouchEvent(event)) {

return true;

}

}

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);

}

return false;

}

这里我们不细讲Touch事件的分发机制,因为网上有哥们已经讲的很清楚了。请参看篇首提供的链接。

我们看一下第17行,由于我们没有实现OnTouchListener接口,而onTouch()方法的默认返回值为false,所以第一个if语句中的代码不会被执行到,进入第二个if语句中,执行了onTouchEvent()方法。那么我们再来看一下该方法:

复制代码 代码如下:
public boolean onTouchEvent(MotionEvent event) {

final int viewFlags = mViewFlags;

if ((viewFlags & ENABLED_MASK) == DISABLED) {

if (event.getAction() == MotionEvent.ACTION_UP

&& (mPrivateFlags & PFLAG_PRESSED) != 0) {

setPressed(false);

}

// 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) {

if (mTouchDelegate.onTouchEvent(event)) {

return true;

}

}

if (((viewFlags & CLICKABLE) == CLICKABLE ||

(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

switch (event.getAction()) {

case MotionEvent.ACTION_UP:

boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;

if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {

// 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);

}

if (!mHasPerformedLongPress) {

// 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();

}

if (!post(mPerformClick)) {

performClick();

}

}

}

if (mUnsetPressedState == null) {

mUnsetPressedState = new UnsetPressedState();

}

if (prepressed) {

postDelayed(mUnsetPressedState,

ViewConfiguration.getPressedStateDuration());

} else if (!post(mUnsetPressedState)) {

// If the post failed, unpress right now

mUnsetPressedState.run();

}

removeTapCallback();

}

break;

case MotionEvent.ACTION_DOWN:

mHasPerformedLongPress = false;

if (performButtonActionOnTouchDown(event)) {

break;

}

// 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();

}

postDelayed(mPendingCheckForTap,

ViewConfiguration.getTapTimeout());

} else {

// Not inside a scrolling container, so show the feedback right away

setPressed(true);

checkForLongClick(0);

}

break;

case MotionEvent.ACTION_CANCEL:

setPressed(false);

removeTapCallback();

removeLongPressCallback();

break;

case MotionEvent.ACTION_MOVE:

final int x = (int) event.getX();

final int y = (int) event.getY();

// Be lenient about moving outside of buttons

if (!pointInView(x, y, mTouchSlop)) {

// Outside button

removeTapCallback();

if ((mPrivateFlags & PFLAG_PRESSED) != 0) {

// Remove any future long press/tap checks

removeLongPressCallback();

setPressed(false);

}

}

break;

}

return true;

}

return false;

}

代码太长,我们只看重点,在ACTION_UP这个case当中,我们找到了关键的代码(第109行):performClick()。

至此,我们已经基本搞清楚了回调机制的整个过程。

时间: 2024-10-26 14:04:09

详细介绍Android中回调函数机制的相关文章

详细介绍Android中回调函数机制_Android

提示:在阅读本文章之前,请确保您对Touch事件的分发机制有一定的了解 在Android的学习过程中经常会听到或者见到"回调"这个词,那么什么是回调呢?所谓的回调函数就是:在A类中定义了一个方法,这个方法中用到了一个接口和该接口中的抽象方法,但是抽象方法没有具体的实现,需要B类去实现,B类实现该方法后,它本身不会去调用该方法,而是传递给A类,供A类去调用,这种机制就称为回调. 下面我们拿具体的Button的点击事件进行模拟分析: 首先,在View类中我们能找到setOnClickLis

Android中回调接口的使用介绍

MainActivity如下: 复制代码 代码如下: package cn.testcallback; import android.os.Bundle; import android.widget.Toast; import android.app.Activity; /** * Demo描述: * Android中回调接口的使用 */ public class MainActivity extends Activity { @Override protected void onCreate(

Android中Handler消息传递机制_Android

Handler 是用来干什么的? 1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以在你的子线程中拿到父线程中创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面. 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发

对js中回调函数的一些看法_javascript技巧

最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做native App  和Web App是主流,也就是说现在各种基于浏览器的web app框架也会越来越火爆了,做js的也越来越有前途.我也决定从后端开发渐渐向前端开发和手机端开发靠拢,废话不说了,我们来切入正题"js的回调函数"相关的东西. 说起回调函数,好多人虽然知道意思,但是还是一知半解.至于怎么用,还是有点糊涂.网上的一些

Android中Handler消息传递机制

Handler 是用来干什么的? 1)执行计划任务,可以在预定的时间执行某些任务,可以模拟定时器 2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以在你的子线程中拿到父线程中创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面. 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发

js中回调函数问题,程序进不了回调函数

问题描述 js中回调函数问题,程序进不了回调函数 我这段代码的回调函数哪出错,请各位大神指教 function req(){ createRequest(); var zipcode=document.getElementById(""zipcode"").value; var url=""b.jsp?zipcode="" + escape(zipcode); request.open(""POST&quo

消息-Android中的handler机制

问题描述 Android中的handler机制 请教大神,handler发消息可以从这个activity发送到另外一个activity吗? 解决方案 Handler 变量的定义,一般在一个 Activity 中,想要在两个 Activity 之间通过 Handler 变量,也就是说两个 Activity 都必须能看到此 Handler 变量. 这样的实现方式,就算是能实现,也不好! Activity 之间,还是通过 Receiver,如:BroadcastReceiver 来进行通讯比较好. 解

jsonp-小白:js中回调函数处理后的数据如何保存?

问题描述 小白:js中回调函数处理后的数据如何保存? 主要的函数为: function Ajax(asc, url, data, func) { $.ajax({ // asc: false, async: asc, //type: "post", //dataType: "json", type: "get", dataType: "jsonp", //jsonp: "Callback", // json

js中回调函数的执行顺序

问题描述 js中回调函数的执行顺序 代码是调用了地图api函数,里面有个回调函数,想在回调函数里把值赋给全局变量,但是回调函数之后的代码里全局变量的值没变,具体见代码: <script type="text/javascript"> //基本地图加载 var map = new AMap.Map("container", { resizeEnable: true, //地图中心点 zoom: 13 //地图显示的缩放级别 }); var g=18; AM