欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-06-29 09:42:53
提示:在阅读本文章之前,请确保您对touch事件的分发机制有一定的了解 在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()。

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