Setting up a click monitor for a View Event is a very common thing, such as
view.setOnClickListener(onClickListener);
Another way is to directly specify the View click in the XML layout When using the callback method, you first need to write the method for callback in the Activity, such as
public void onClickView(View view){ // do something }
and then set the android:onClick
attribute of the View in XML
<View android:layout_width="match_parent" android:layout_height="match_parent" android:onClick="onClickView" />
Sometimes it is more convenient to directly set the click event from the XML layout (especially when writing DEMO projects). Not many people usually use this method. You can roughly guess it from the usage method. The View should use reflection to find the "onClickView" method from the Activity and call it when it is running, because this approach does not use any interface.
Next, we can analyze how View triggers the callback method from the source code.
View has 5 construction methods. The first one is used internally. The second method is usually used to create View instances directly in Java code. The View instance rendered from the XML layout will eventually call the fifth method.
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { this(context); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { …… // 处理onClick属性 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) { // 给当前View实例设置一个DeclaredOnClickListener监听器 setOnClickListener(new DeclaredOnClickListener(this, handlerName)); } break; } } }
When processing the onClick attribute, first determine whether the View's Context isRestricted, and if so, throw an IllegalStateException. Take a look at the isRestricted method
/** * Indicates whether this Context is restricted. * * @return {@code true} if this Context is restricted, {@code false} otherwise. * * @see #CONTEXT_RESTRICTED */ public boolean isRestricted() { return false; }
isRestricted is used to determine whether the current Context instance is in a restricted state. According to the official explanation, a Context in a restricted state will ignore certain features, such as XML. Certain properties, obviously the android:onClick
property we're looking at are also ignored.
a restricted context may disable specific features. For instance, a View associated with a restricted context would ignore particular XML attributes.
However, the isRestricted method is one of the few in Context Most of them have concrete implementation methods (the rest are basically abstract methods), here they return false directly, and this method is only overridden in ContextWrapper and MockContext
public class ContextWrapper extends Context { Context mBase; @Override public boolean isRestricted() { return mBase.isRestricted(); } } public class MockContext extends Context { @Override public boolean isRestricted() { throw new UnsupportedOperationException(); } }
ContextWrapper is also only a proxy Call mBase's isRestricted, and MockContext is only used when writing unit tests, so isRestricted here will basically only return false, unless a custom ContextWrapper is used and isRestricted is rewritten.
Go back to View, then final String handlerName = a.getString(attr);
In fact, we get the "onClickView" in android:onClick="onClickView"
##String, then use the instance of the current View and "onClickView" to create a DeclaredOnClickListener instance and set it as the click listener of the current View.
/** * An implementation of OnClickListener that attempts to lazily load a * named click handling method from a parent or ancestor context. */ private static class DeclaredOnClickListener implements OnClickListener { private final View mHostView; private final String mMethodName; private Method mMethod; public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) { mHostView = hostView; mMethodName = methodName; } @Override public void onClick(@NonNull View v) { if (mMethod == null) { mMethod = resolveMethod(mHostView.getContext(), mMethodName); } try { mMethod.invoke(mHostView.getContext(), 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 Method resolveMethod(@Nullable Context context, @NonNull String name) { while (context != null) { try { if (!context.isRestricted()) { return context.getClass().getMethod(mMethodName, View.class); } } 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); } }
android:onClick is to find the callback method from the Context, so if for the View created in the Fragment's XML, It is impossible to bind the callback method in Fragment in this way, because Fragment itself is not a Context. The Context of the View here is actually FragmentActivity, which also means that this method can be used to quickly call back from Fragment to FragmentActivity.
android:onClick,
mainly plays the role of lazy loading, only when the View is clicked , you will know which method is used for click callback.
android:onClick to set a click event for View means adding a non-interface public method to the Activity. The current Android development trend is "Don't write business logic in the Activity class." This is beneficial to project maintenance and prevents Activity explosion, so try not to have non-interface and non-lifecycle public methods in Activity. Therefore, rash use of
android:onClick may "pollute" the Activity.
The above is the detailed content of Case sharing on setting click events for View in XML layout. For more information, please follow other related articles on the PHP Chinese website!