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

后端 未结 17 2108
野趣味
野趣味 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:01

    Suppose, You want to add click event like this main.xml

    <Button
        android:id="@+id/btn_register"
        android:layout_margin="1dp"
        android:layout_marginLeft="3dp"
        android:layout_marginTop="10dp"
        android:layout_weight="2"
        android:onClick="register"
        android:text="Register"
        android:textColor="#000000"/>
    

    In java file, you have to write a method like this method.

    public void register(View view) {
    }
    
    0 讨论(0)
  • 2020-11-21 12:03

    I am Write this code in xml file ...

    <Button
        android:id="@+id/btn_register"
        android:layout_margin="1dp"
        android:layout_marginLeft="3dp"
        android:layout_marginTop="10dp"
        android:layout_weight="2"
        android:onClick="register"
        android:text="Register"
        android:textColor="#000000"/>
    

    And write this code in fragment...

    public void register(View view) {
    }
    
    0 讨论(0)
  • 2020-11-21 12:04

    With Java 8, you could probably use Method Reference to achieve what you want.

    Assume this is your onClick event handler for a button.

    private void onMyButtonClicked(View v) {
        if (v.getId() == R.id.myButton) {
            // Do something when myButton was clicked
        }
    }
    

    Then, you pass onMyButtonClicked instance method reference in a setOnClickListener() call like this.

    Button myButton = (Button) findViewById(R.id.myButton);
    myButton.setOnClickListener(this::onMyButtonClicked);
    

    This will allow you to avoid explicitly defining an anonymous class by yourself. I must however emphasize that Java 8's Method Reference is actually just a syntactic sugar. It actually create an instance of the anonymous class for you (just like lambda expression did) hence similar caution as lambda-expression-style event handler was applied when you come to the unregistering of your event handler. This article explains it really nice.

    PS. For those who curious about how can I really use Java 8 language feature in Android, it is a courtesy of retrolambda library.

    0 讨论(0)
  • 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.
    0 讨论(0)
  • 2020-11-21 12:07

    No, that is not possible via code. Android just implements the OnClickListener for you when you define the android:onClick="someMethod" attribute.

    Those two code snippets are equal, just implemented in two different ways.

    Code Implementation

    Button btn = (Button) findViewById(R.id.mybutton);
    
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            myFancyMethod(v);
        }
    });
    
    // some more code
    
    public void myFancyMethod(View v) {
        // does something very interesting
    }
    

    Above is a code implementation of an OnClickListener. And this is the XML implementation.

    XML Implementation

    <?xml version="1.0" encoding="utf-8"?>
    <!-- layout elements -->
    <Button android:id="@+id/mybutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click me!"
        android:onClick="myFancyMethod" />
    <!-- even more layout elements -->
    

    In the background, Android does nothing else than the Java code, calling your method on a click event.

    Note that with the XML above, Android will look for the onClick method myFancyMethod() only in the current Activity. This is important to remember if you are using fragments, since even if you add the XML above using a fragment, Android will not look for the onClick method in the .java file of the fragment used to add the XML.

    Another important thing I noticed. You mentioned you don't prefer anonymous methods. You meant to say you don't like anonymous classes.

    0 讨论(0)
  • 2020-11-21 12:07

    Note that if you want to use the onClick XML feature, the corresponding method should have one parameter, whose type should match the XML object.

    For example, a button will be linked to your method through its name string : android:onClick="MyFancyMethod" but the method declaration should show: ...MyFancyMethod(View v) {...

    If you are trying to add this feature to a menu item, it will have the exact same syntax in the XML file but your method will be declared as: ...MyFancyMethod(MenuItem mi) {...

    0 讨论(0)
提交回复
热议问题