首页 文章

android:onClick XML属性与setOnClickListener有何不同?

提问于
浏览
361

从我读过,您可以通过两种方式为按钮分配 onClick 处理程序 .

使用 android:onClick XML属性,您只需使用带有签名 void name(View v) 的公共方法的名称,或使用 setOnClickListener 方法传递实现 OnClickListener 接口的对象 . 后者通常需要一个我个人不喜欢的匿名类(个人品味)或定义实现 OnClickListener 的内部类 .

通过使用XML属性,您只需要定义一个方法而不是一个类,所以我想知道是否可以通过代码而不是XML布局来完成相同的操作 .

16 回答

  • 11

    请注意,如果要使用onClick XML功能,则相应的方法应该有一个参数,其类型应与XML对象匹配 .

    例如,一个按钮将通过其名称字符串链接到您的方法: android:onClick="MyFancyMethod" 但方法声明应显示: ...MyFancyMethod(View v) {...

    如果您尝试将此功能添加到菜单项,它将在XML文件中具有完全相同的语法,但您的方法将声明为: ...MyFancyMethod(MenuItem mi) {...

  • 3

    通过使用XML属性,您只需要定义一个方法而不是一个类,所以我想知道是否可以通过代码而不是XML布局来完成相同的操作 .

    是的,你可以让 fragmentactivity 实施 View.OnClickListener

    当您在代码中初始化新的视图对象时,您可以简单地执行 mView.setOnClickListener(this);

    这会自动设置代码中的所有视图对象,以使用 fragmentactivity 等具有的 onClick(View v) 方法 .

    要区分哪个视图调用 onClick 方法,可以在 v.getId() 方法上使用switch语句 .

    This answer is different from the one that says "No that is not possible via code"

  • 71

    android:onClick 适用于API级别4以上,因此如果您_557568使用它 .

  • 13

    要小心,虽然 android:onClick XML似乎是处理click的一种方便的方法, setOnClickListener 实现除了添加 onClickListener 之外还做了一些其他的事情 . 实际上,它将视图属性 clickable 设置为true .

    虽然在大多数Android实现中它可能不是问题,但根据电话构造函数,按钮始终默认为clickable = true但某些手机型号上的其他构造函数可能在非Button视图上具有默认的clickable = false .

    因此设置XML是不够的,你必须一直认为在非按钮上添加 android:clickable="true" ,如果你有一个设备,其中默认值是clickable = true而你忘了甚至一次放置这个XML属性,你就不会在运行时注意到这个问题,但是当它出现在您的客户手中时,它将获得市场反馈!

    此外,我们永远无法确定proguard将如何对XML属性和类方法进行模糊处理和重命名,因此不能100%安全地认为它们有一天永远不会有错误 .

    因此,如果您从未想过遇到麻烦而从未想过它,最好使用 setOnClickListener 或像ButterKnife这样的库注释 @OnClick(R.id.button)

  • 21

    不,这不可能通过代码 . Android只在您定义 android:onClick="someMethod" 属性时为您实现 OnClickListener .

    这两个代码片段是相同的,只是以两种不同的方式实现 .

    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
    }
    

    以上是 OnClickListener 的代码实现 . 这是XML实现 .

    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 -->
    

    在后台,Android只执行Java代码,在单击事件上调用您的方法 .

    请注意,使用上面的XML,Android将仅在当前的Activity中查找 onClick 方法 myFancyMethod() . 如果您正在使用片段,这一点很重要,因为即使您使用片段添加上面的XML,Android也不会在用于添加XML的片段的 .java 文件中查找 onClick 方法 .

    我注意到的另一件重要事情你提到你不喜欢匿名方法 . 你的意思是说你不喜欢匿名 classes .

  • 0

    我在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"/>
    

    并在片段中编写此代码...

    public void register(View view) {
    }
    
  • 0

    使用Java 8,您可以使用Method Reference来实现您想要的功能 .

    假设这是一个按钮的 onClick 事件处理程序 .

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

    然后,在这样的 setOnClickListener() 调用中传递 onMyButtonClicked 实例方法引用 .

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

    这样可以避免自己明确定义匿名类 . 但我必须强调Java 8的方法参考实际上只是一个语法糖 . 它实际上为您创建了一个匿名类的实例(就像lambda表达式一样),因此当您注册事件处理程序时,应用了lambda-expression-style事件处理程序时的类似注意事项 . 这article解释得非常好 .

    PS . 对于那些对如何在Android中真正使用Java 8语言功能感到好奇的人来说,它是retrolambda库的礼貌 .

  • 0

    支持Ruivo的答案,是的,您必须将方法声明为“公共”才能在Android的XML onclick中使用 - 我正在开发一个针对API Level 8的应用程序(minSdk ...)到16(targetSdk ...) .

    我宣称我的方法是私有的,它引起了错误,只是宣称它是公共作品 .

  • 4

    检查您是否忘记将该方法公之于众!

  • 4

    这里有很好的答案,但我想添加一行:

    在XML的 android:onclick 中,Android在场景后面使用java reflection来处理这个问题 .

    并且as explained here,反射总是会降低性能 . (特别是在Dhalvik VM上) . 注册 onClickListener 是一种更好的方法 .

  • 548

    指定 android:onClick 属性会导致 Button 实例在内部调用 setOnClickListener . 因此绝对没有区别 .

    为了清楚地理解,让我们看看框架如何处理XML onClick 属性 .

    当布局文件膨胀时,实例化其中指定的所有视图 . 在此特定情况下, Button 实例是使用public Button (Context context, AttributeSet attrs, int defStyle)构造函数创建的 . XML标记中的所有属性都从资源包中读取,并作为 AttributeSet 传递给构造函数 .

    Button 类继承自 View 类,这导致调用 View 构造函数,它负责通过 setOnClickListener 设置单击回调处理程序 .

    attrs.xml中定义的onClick属性在View.java中称为 R.styleable.View_onClick .

    以下是View.java的代码,它通过单独调用 setOnClickListener 来完成大部分工作 .

    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;
    

    如您所见,调用 setOnClickListener 来注册回调,就像我们在代码中一样 . 唯一的区别是它使用 Java Reflection 来调用我们的Activity中定义的回调方法 .

    以下是其他答案中提到的问题的原因:

    • Callback method should be public :由于使用了Java Class getMethod,因此仅搜索具有公共访问说明符的函数 . 否则准备好处理 IllegalAccessException 异常 .

    • While using Button with onClick in Fragment, the callback should be defined in ActivitygetContext().getClass().getMethod() 调用将方法搜索限制为当前上下文,在Fragment的情况下为Activity . 因此,在Activity类中搜索方法而不是Fragment类 .

    • Callback method should accept View parameter :由于Java Class getMethod搜索接受 View.class 作为参数的方法 .

  • 29

    设置单击侦听器的另一种方法是使用XML . 只需将android:onClick属性添加到您的标记即可 .

    尽可能在匿名Java类上使用xml属性“onClick”是一种很好的做法 .

    首先,让我们来看看代码的区别:

    XML Attribute / onClick attribute

    XML部分

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button1" 
        android:onClick="showToast"/>
    

    Java部分

    public void showToast(View v) {
        //Add some logic
    }
    

    Anonymous Java Class / setOnClickListener

    XML部分

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    

    Java部分

    findViewById(R.id.button1).setOnClickListener(
        new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Add some logic
            }
    });
    

    以下是在匿名Java类上使用XML属性的好处:

    • 使用Anonymous Java类时,我们总是必须为元素指定一个id,但是可以省略XML属性id .

    • 使用匿名Java类,我们必须主动搜索视图内部的元素(findViewById部分),但是使用Android属性,Android会为我们做 .

    • 正如我们所看到的,匿名Java类至少需要5行代码,但使用XML属性时,3行代码就足够了 .

    • 使用Anonymous Java类,我们必须命名我们的方法“onClick”,但是使用XML属性我们可以添加我们想要的任何名称,这将极大地帮助我们的代码的可读性 .
      Google在API级别4发布期间添加了

    • Xml“onClick”属性,这意味着它的语法更加现代,现代语法几乎总是更好 .

    当然,并不总是可以使用Xml属性,这就是为什么我们不选择它的原因:

    • 如果我们正在使用片段 . onClick属性只能添加到一个活动中,所以如果我们有一个片段,我们就必须使用一个匿名类 .

    • 如果我们想将onClick侦听器移动到一个单独的类(可能是因为它非常复杂和/或我们想在应用程序的不同部分重用它),那么我们就不想使用xml了属性要么 .

  • 4

    假设,你想添加像这样的点击事件 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"/>
    

    在java文件中,您必须编写类似此方法的方法 .

    public void register(View view) {
    }
    
  • 4
    Add Button in xml and give onclick attribute name that is the name of Method.
       <!--xml --!>
       <Button
      android:id="@+id/btn_register"
      android:layout_margin="1dp"
      android:onClick="addNumber"
      android:text="Add"
      />
    
    
        Button btnAdd = (Button) findViewById(R.id.mybutton); btnAdd.setOnClickListener(new View.OnClickListener() {
       @Override
        public void onClick(View v) {
          addNumber(v);
        }
        });
    
      Private void addNumber(View v){
      //Logic implement 
        switch (v.getId()) {
        case R.id.btnAdd :
            break;
         default:
            break;
        }}
    
  • 0

    执行此操作的最佳方法是使用以下代码:

    Button button = (Button)findViewById(R.id.btn_register);
     button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //do your fancy method
                }
            });
    
  • 75

    当我看到最顶层的答案时,它让我意识到我的问题不是将参数(View v)放在花哨的方法上:

    public void myFancyMethod(View v) {}
    

    当试图从xml访问它时,应该使用

    android:onClick="myFancyMethod"/>
    

    希望能帮助别人 .

相关问题