在xml中,可以给view设置”onClick”属性,属性的内容为一个形参为View类型的方法名字,这个方法要写xml对应的Activity中。
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="onClick"/>
<declare-styleable name="View">
···
<attr name="onClick" format="string" />
···
</declare-styleable>
view在初始化时,如果发现你设置了”onClick”属性,他会把这个方法名保存下来,然后给view设置一个点击监听器,监听器会通过反射去执行你在xml中设置的方法。
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
···
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case ···:
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;
···
}
如果你重新设置了点击监听器,就会把view自己设置的监听器替换掉。
下面是监听器的具体实现,看看onClick的部分,其中mResolvedMethod.invoke(mResolvedContext, v)这一句就是执行我们在xml里设置的方法。
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 {
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);
}
}