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

emi*_*rax 400 android onclick

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

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

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

Oct*_*ean 583

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

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

代码实现

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
}
Run Code Online (Sandbox Code Playgroud)

以上是一个代码实现OnClickListener.这是XML实现.

XML实现

<?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 -->
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 请注意,如果使用XML onclick,则必须将onclick方法(`myFancyMethod()`)放在当前的Activity中.如果您正在使用片段,这一点很重要,因为设置onclick侦听器的编程方式可能会使用方法处理片段的onCreateView()中的点击...如果从XML引用它将不会被找到. (116认同)
  • 是的,该方法必须公开. (12认同)
  • 有趣的是,在代码中执行它确实允许通过使方法私有来屏蔽方法访问,而以xml方式执行它会导致方法的暴露. (12认同)
  • 事务(在XML方法中)必须在Activity中这一事实不仅在考虑片段时很重要,而且在自定义视图(包含按钮)时也很重要.当您拥有在多个活动中重用的自定义视图,但是您希望对所有情况使用相同的onClick方法时,XML方法不是最方便的方法.您需要在使用自定义视图的每个活动中放置此onClickMethod(具有相同的主体). (5认同)
  • 我不是Java大师,但是,我的意思是匿名课程.感谢您的回复.非常清楚. (4认同)

jp0*_*121 86

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

public void myFancyMethod(View v) {}
Run Code Online (Sandbox Code Playgroud)

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

android:onClick="myFancyMethod"/>
Run Code Online (Sandbox Code Playgroud)

希望能帮助别人.


Jam*_*mes 74

android:onClick 适用于API级别4以上,因此如果您的目标是<1.6,那么您就无法使用它.


Rui*_*ivo 30

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

  • @eRaisedToX我认为很清楚:如果它不公开,就无法从Android框架调用它. (3认同)

Man*_*ani 26

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

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

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

Buttonclass继承自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;
Run Code Online (Sandbox Code Playgroud)

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

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

  • 回调方法应该是公共的:自Java Class getMethod使用以来,只搜索具有公共访问说明符的函数.否则准备处理IllegalAccessException异常.
  • 在片段中使用带有onClick的Button时,应在Activity中定义回调:getContext().getClass().getMethod()call将方法搜索限制为当前上下文,在Fragment的情况下为Activity.因此,在Activity类中搜索方法而不是Fragment类.
  • 回调方法应该接受View参数:自Java Class getMethod搜索接受View.class为参数的方法.


Ant*_*zée 14

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

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

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


Kru*_*hah 13

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

android:onclickXML中,Android使用场景后面的java反射来处理这个问题.

而且这里解释,反思总是会减慢性能.(特别是在Dhalvik VM上).注册onClickListener是一种更好的方法.

  • 它可以减慢多少钱?:)半毫秒; 甚至不?与实际膨胀布局相比,它就像羽毛和鲸鱼 (4认同)

CQM*_*CQM 5

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

是的,您可以制作fragmentactivity实施View.OnClickListener

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

这会自动将代码中的所有视图对象设置为使用onClick(View v)fragment或其他人activity拥有的方法。

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

这个答案与说“不,这不可能通过代码实现”的答案不同


one*_*iew 5

使用 Java 8,您可能可以使用方法引用来实现您想要的。

假设这是onClick按钮的事件处理程序。

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

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

Button myButton = (Button) findViewById(R.id.myButton);
myButton.setOnClickListener(this::onMyButtonClicked);
Run Code Online (Sandbox Code Playgroud)

这将允许您避免自己显式定义匿名类。然而我必须强调,Java 8 的方法引用实际上只是一个语法糖。它实际上为您创建了一个匿名类的实例(就像 lambda 表达式所做的那样),因此当您取消注册事件处理程序时,应用了与 lambda 表达式样式的事件处理程序类似的警告。这篇文章解释得非常好。

附注。对于那些对如何在 Android 中真正使用 Java 8 语言功能感到好奇的人,这是由retrolambda库提供的。


Bes*_*ohn 5

设置点击监听器的另一种方法是使用XML。只需将android:onClick属性添加到您的标签即可。

最好在匿名Java类上使用xml属性“ onClick”。

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

XML属性/ onClick属性

XML部分

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/button1" 
    android:onClick="showToast"/>
Run Code Online (Sandbox Code Playgroud)

Java部分

public void showToast(View v) {
    //Add some logic
}
Run Code Online (Sandbox Code Playgroud)

匿名Java类别/ setOnClickListener

XML部分

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
Run Code Online (Sandbox Code Playgroud)

Java部分

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

与匿名Java类相比,使用XML属性的好处如下:

  • 对于Anonymous Java类,我们总是必须为元素指定一个id,但是对于XML属性,可以省略id。
  • 使用Anonymous Java类,我们必须主动搜索视图内部的元素(findViewById部分),但是使用XML属性,Android会为我们做到这一点。
  • 如我们所见,匿名Java类至少需要5行代码,但是使用XML属性,3行代码就足够了。
  • 使用Anonymous Java类,我们必须将方法命名为“ onClick”,但是使用XML属性,我们可以添加所需的任何名称,这将大大有助于代码的可读性。
  • Google在API级别4发行期间添加了Xml“ onClick”属性,这意味着它的语法更加现代,现代语法几乎总是更好。

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

  • 如果我们正在处理片段。onClick属性只能添加到活动中,因此,如果有片段,则必须使用匿名类。
  • 如果我们想将onClick侦听器移到一个单独的类(也许如果它很复杂和/或我们想在应用程序的不同部分中重用它),那么我们就不想使用xml属性要么。