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,这样就不会出现任何的一个问题。