How exactly does the android:onClick XML attribute differ from setOnClickListener?

后端 未结 17 2492
野趣味
野趣味 2020-11-21 11:50

From that I\'ve read you can assign a onClick handler to a button in two ways.

Using the android:onClick XML attribute where you just use t

17条回答
  •  耶瑟儿~
    2020-11-21 12:06

    Specifying android:onClick attribute results in Button instance calling setOnClickListener internally. Hence there is absolutely no difference.

    To have clear understanding, let us see how XML onClick attribute is handled by the framework.

    When a layout file is inflated, all Views specified in it are instantiated. In this specific case, the Button instance is created using public Button (Context context, AttributeSet attrs, int defStyle) constructor. All of the attributes in the XML tag are read from the resource bundle and passed as AttributeSet to the constructor.

    Button class is inherited from View class which results in View constructor being called, which takes care of setting the click call back handler via setOnClickListener.

    The onClick attribute defined in attrs.xml, is referred in View.java as R.styleable.View_onClick.

    Here is the code of View.java that does most of the work for you by calling setOnClickListener by itself.

     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 OnClickListener() {
                        private Method mHandler;
    
                        public void onClick(View v) {
                            if (mHandler == null) {
                                try {
                                    mHandler = getContext().getClass().getMethod(handlerName,
                                            View.class);
                                } catch (NoSuchMethodException e) {
                                    int id = getId();
                                    String idText = id == NO_ID ? "" : " with id '"
                                            + getContext().getResources().getResourceEntryName(
                                                id) + "'";
                                    throw new IllegalStateException("Could not find a method " +
                                            handlerName + "(View) in the activity "
                                            + getContext().getClass() + " for onClick handler"
                                            + " on view " + View.this.getClass() + idText, e);
                                }
                            }
    
                            try {
                                mHandler.invoke(getContext(), View.this);
                            } catch (IllegalAccessException e) {
                                throw new IllegalStateException("Could not execute non "
                                        + "public method of the activity", e);
                            } catch (InvocationTargetException e) {
                                throw new IllegalStateException("Could not execute "
                                        + "method of the activity", e);
                            }
                        }
                    });
                }
                break;
    

    As you can see, setOnClickListener is called to register the callback, as we do in our code. Only difference is it uses Java Reflection to invoke the callback method defined in our Activity.

    Here are the reason for issues mentioned in other answers:

    • Callback method should be public : Since Java Class getMethod is used, only functions with public access specifier are searched for. Otherwise be ready to handle IllegalAccessException exception.
    • While using Button with onClick in Fragment, the callback should be defined in Activity : getContext().getClass().getMethod() call restricts the method search to the current context, which is Activity in case of Fragment. Hence method is searched within Activity class and not Fragment class.
    • Callback method should accept View parameter : Since Java Class getMethod searches for method which accepts View.class as parameter.

提交回复
热议问题