[读书笔记]监听事件的四种方式原理分析(接口回调)与比较

时间:2021-01-24 22:34:27
  • 方式一:在布局文件的控件上设置onClick属性

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

    <Button  android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="sayHello" android:text="HELLO"/>
</LinearLayout>

在源文件中绑定点击事件:

/** * ============================================================================= * Copyright (c) 2016 yuxin All rights reserved. * Packname work * Created by yuxin. * Created time 2016/9/21 0021 上午 8:38. * Version 1.0; * Describe : * History: * ============================================================================== */
public class LoginOkActivity2 extends Activity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dome);
    }

    public void doClick(View view) {
        //do something
    }
}

这种方式代码简洁,结构清晰,唯一的缺点是android:onClick注册在debug时能运行正常,切换成release版本时release版本通常会混淆代码,方法的名称就改变了,所以这种方式虽然简单,但是不推荐。
在介绍后面几种方法前我们先提下java中方法的回调,下面这图是我对回调的理解:
[读书笔记]监听事件的四种方式原理分析(接口回调)与比较
如果还没看懂或则觉得我的理解有问题的,可以baidu看下别人的解释

  • 方式二:匿名内部类作为事件监听器类
/** * ============================================================================= * Copyright (c) 2016 yuxin All rights reserved. * Packname com.jju.yuxin.disanzhou * Created by yuxin. * Created time 2016/9/21 0021 上午 10:50. * Version 1.0; * Describe : * History: * ============================================================================== */
public class ClickDome extends Activity {

    private Button bt_click;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_click);
        bt_click = (Button) findViewById(R.id.bt_click);
        bt_click.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do something
            }
        });
    }
}

很多监听事件只是临时的使用一次,或则有的一类监听操作只有一个控件执行,那么可以使用这种,也是使用比较广泛的一种监听方式,但是对于java来说,这种耦合有点紧,没有模块化的。

  • 方式三:内部类作为监听器
public class ClickDome extends Activity {

    private Button bt_click;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_click);
        bt_click = (Button) findViewById(R.id.bt_click);
        MyClickListener listtener = new MyClickListener():
        bt_click.setOnClickListener(listtener);

    }

    private class MyClickListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.bt_click:

                    break;
                default:
                    break;
            }
        }
    }
}

当然也可以将内部类放置出来另写一个监听类,这种方式在java上符合单一职责原则,耦合度低,但是带来的确实更多的代码编写量,庆幸的是带来了可复用性,只要同一类监听操作我们都只传入一个监听类对象搞定,然后在switch中写上代码,但是记得的是,不同的activity千万别放在一个监听类里面操作,可能一时爽,痛苦起来会发疯。

  • 方式四:Activity继承监听事件,Activity本身作为事件监听器
/** * ============================================================================= * Copyright (c) 2016 yuxin All rights reserved. * Packname com.jju.yuxin.disanzhou * Created by yuxin. * Created time 2016/9/21 0021 上午 10:50. * Version 1.0; * Describe : * History: * ============================================================================== */
public class ClickDome extends Activity implements View.OnClickListener {


    private Button bt_click;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_click);
        bt_click = (Button) findViewById(R.id.bt_click);
        bt_click.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_click:
        //do something
                break;
            default:
                break;
        }

    }
}

这种方法其实和上面的方式三差不多,在这种方式里,监听类的对象都不用new了,因为就是本身,传入一个this就行了,但是你不觉得Activity一个界面类来实现监听接口有点不伦不类嘛?

综合上面四种方式,各有各的优势与劣势,在实际场景中灵活运用才是正道。
这博客就写完了嘛??不不不,,本博主和哪些妖艳贱货不一样,肯定要写点其他的东西啊,不然前面的方法回调不是白白铺垫了嘛,我们仔细的看下后面三种方式,其实原理都一样,都是为了实现onClick()这个方法,我们来看下setOnClickListener();这个方法,因为Buttom是View的子类,我们看下View里面的setOnClickListener()方法,

 /** * Register a callback to be invoked when this view is clicked. If this view is not * clickable, it becomes clickable. * * @param l The callback that will run * * @see #setClickable(boolean) */
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

他就做了两件事情,一、把控件置为可点击的,二、将OnClickListener 对象赋值给getListenerInfo方法里面的mOnClickListener属性,我们看下这两个方法:

  /**
     * Enables or disables click events for this view. When a view
     * is clickable it will change its state to "pressed" on every click.
     * Subclasses should set the view clickable to visually react to
     * user's clicks.
     *
     * @param clickable true to make the view clickable, false otherwise
     *
     * @see #isClickable()
     * @attr ref android.R.styleable#View_clickable
     */
    public void setClickable(boolean clickable) {
        setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
    }

他在这里立了个flag

   ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

这里也是非空判断下,返回个属性
这下我慌了,下一步它往哪里走呢??后来我看到了这个方法boolean performAccessibilityActionInternal(int action, Bundle arguments)(名字有点长)


   /** * @see #performAccessibilityAction(int, Bundle) * * Note: Called from the default {@link AccessibilityDelegate}. * * @hide */
    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
                      .
                      .
                      .
                      .

        switch (action) {
            case AccessibilityNodeInfo.ACTION_CLICK: {
                if (isClickable()) {
                    performClick();
                    return true;
                }
            } break;
                  .
                  .
                  .
        return false;
    }

中间其他的我们都不关系,我们看下注释Note: Called from the default {@link AccessibilityDelegate}.大概意思就是说它会被另一个方法默认调用,我们看下switch

 /** * Indicates whether this view reacts to click events or not. * * @return true if the view is clickable, false otherwise * * @see #setClickable(boolean) * @attr ref android.R.styleable#View_clickable */
    @ViewDebug.ExportedProperty
    public boolean isClickable() {
        return (mViewFlags & CLICKABLE) == CLICKABLE;
    }

这个isClickable()就是返回之前setClickable()的值,我记得方法一进来就设置为了true,那我们看下performClick();

 /** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing * a sound, etc. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);


            li.mOnClickListener.onClick(this);


            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

其他的都不重要。我们看到它通过各种传递和判断,将我们传进来的OnClickListener对象调用它的onClick()方法;。。而我们来看下onClick()

   /** * Interface definition for a callback to be invoked when a view is clicked. */
    public interface OnClickListener {
        /** * Called when a view has been clicked. * * @param v The view that was clicked. */
        void onClick(View v);
    }

他是View类里面的OnClickListener接口的一个没有实现的方法,而这个方法就是我们在监听类里面实现的void onClick(View v)方法,结合之前的方法回调的图,这个应该知道工作模式了把!
关于绑定监听事件的原理应该是在LayoutInflater.from(this).inflate();实现,里面有对资源XML的解析生成View树图,但是原理我还没看,有兴趣的可以自己看下

我的博客网站http://huyuxin.top/欢迎大家访问!评论!