背景
<Button
android:onClick="handleAction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击"/>
我们在写布局的时候,经常会给一个控件加上android:onClick="handleAction"
,然后在我们的代码定义handleAction
方法,编写需要进行响应的事件,那么,xml 里面定义的这个handleAction
是怎样跟代码里面的方法绑定起来的呢?
源码分析
其实原理很简单,细想一下,其实不看代码,也能够大概知道其原理。我们知道,在只定义view的实现过程中,我们会解析attributeSet
配置,将对应的属性设置到view 上,onClick
这个也是同样的原理,看到View的代码实现:
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new DeclaredOnClickListener(this, handlerName));
}
break;
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 {
//通过invoke 进行方法的调用。
mResolvedMethod.invoke(mResolvedContext, v);
} catch (IllegalAccessException e) {
throw new IllegalStateException(
"Could not execute non-public method for android:onClick", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(
"Could not execute method for android:onClick", e);
}
}
@NonNull
private void resolveMethod(@Nullable Context context, @NonNull String name) {
while (context != null) {
try {
if (!context.isRestricted()) {
final Method method = context.getClass().getMethod(mMethodName, View.class);
if (method != null) {
mResolvedMethod = method;
mResolvedContext = context;
return;
}
}
} catch (NoSuchMethodException e) {
// Failed to find method, keep searching up the hierarchy.
}
if (context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
} else {
// Can't search up the hierarchy, null out and fail.
context = null;
}
}
//如果找不到方法的定义,则抛出异常
final int id = mHostView.getId();
final String idText = id == NO_ID ? "" : " with id '"
+ mHostView.getContext().getResources().getResourceEntryName(id) + "'";
throw new IllegalStateException("Could not find method " + mMethodName
+ "(View) in a parent or ancestor Context for android:onClick "
+ "attribute defined on view " + mHostView.getClass() + idText);
}
}
整个流程其实就是,先通过变量的值,通过反射,找到对应的方法,如果找到了,直接带事件点击的时候进行响应,如果找不到,则抛出异常。