View的onClick源码分析

1. 说明


这篇文章记录下我们经常使用的onClick的点击事件的源码,我们经常写点击事件时有2种写法:
1>:第一种是在布局文件中给一个控件写一个id,然后在Activity或者Fragment中findviewbyid找到这个控件,然后设置setOnClickListener点击事件;
2>:第二种就是直接在xml文件中设置一个 onClick =test 属性,然后在代码中这样写:

public void test(View view){
    Toast.makeText(MainActivity.this , "点击了图片了" , Toast.LENGTH_SHORT).show();
}

在这里需要注意两点:
1>:在xml文件中设置一个 onClick =test 属性时,这个 test的值必须是有值的,不能为空;
2>:在代码中的onClick(View view) 中,必须写View view,那么接下来我们就来看下为什么需要这样写,如果不去这样写,为什么会报错。

2. 源码分析


View的部分源码如下:

第一部分源码:

case R.styleable.View_onClick:
         if (context.isRestricted()) {
              throw new IllegalStateException("The android:onClick attribute cannot "
                + "be used within a restricted context");
        }
    
       // handlerName 是xml文件中写的 test的值
       final String handlerName = a.getString(attr);
       if (handlerName != null) {
            // this:当前Activity , handlerName:xml文件中写的test的值
            setOnClickListener(new DeclaredOnClickListener(this, handlerName));
       }
            break;

分析以上源码可知:
这里的handlerName 就是上边在xml文件中写的 test 的值,这个必须要写,否则在执行setOnClickListener(new DeclaredOnClickListener(this, handlerName));方法时传递的handlerName 就是空的,就会报错;

第二部分源码:

private static class DeclaredOnClickListener implements OnClickListener {
        private final View mHostView;
        private final String mMethodName;

        private Method mResolvedMethod;
        private Context mResolvedContext;

        public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
            mHostView = hostView;
            mMethodName = methodName;
        }

        @Override
        public void onClick(@NonNull View v) {
            if (mResolvedMethod == null) {
                resolveMethod(mHostView.getContext(), mMethodName);
            }

            try {
                // mResolvedContext:当前Activity , v: test(View view)中的 view
                mResolvedMethod.invoke(mResolvedContext, v);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException(
                        "Could not execute non-public method for android:onClick", e);
            } 
        }

分析上边源码可知:
在点击第一部分源码中的DeclaredOnClickListener后进入第二部分源码,在第二部分源码中的

// mResolvedContext:当前Activity , v:test(View view)中的 view
mResolvedMethod.invoke(mResolvedContext, v);

mResolvedContext代表当前的 Activity,v 就是我们自己在代码中传递 view

public void test(View view){
    Toast.makeText(MainActivity.this , "点击了图片了" , Toast.LENGTH_SHORT).show();
}

v就是我们自己上边给传递的view

总结上边所讲的:

1>:如果在布局文件中的 onClick = test ,如果你不写这个test,程序会报错;
2>:如果在自己写的 public void test(View view)方法中,不去传递 View view ,程序会报错;因为源码中调用的mResolvedMethod.invoke(mResolvedContext, v);方法,如果不传递 view,那么就会报错;

所以我们在写代码过程中,如果点击事件采用的是第二种方式,那么在布局文件中的onClick = test,一定要写值,并且在onClick(View view)中,一定要传递 View,这样就不会出现任何的一个问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容