android:onClick XML属性与setOnClickListener有什么区别?


416

从我读到的内容中,您可以通过onClick两种方式将处理程序分配给按钮。

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

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


4
我读了你的问题,我想你和我一样被困在同一个地方。我看了一段非常棒的视频,这对解决我的问题非常有帮助。在以下链接上找到视频:youtube.com/watch?
v=MtmHURWKCmg&feature=youtu.be

9
对于那些想节省时间观看上面评论中发布的视频的人,它只是演示了两个按钮如何onClick在布局文件中为其属性提供相同的方法。这要归功于参数View v。您只需选中if (v == findViewById(R.id.button1))等。
CodyBugstein

13
@Imray我认为最好使用v.getId() == R.id.button1,因为您不必查找实际控件并进行比较。而且您可以使用a switch代替很多if。
Sami Kuhmonen 2014年

3
本教程将对您有很大帮助。单击此处
c49

使用xml android:onClick会导致崩溃。

Answers:


604

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

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

代码实施

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

<?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代码外没有执行其他操作,而是在click事件上调用您的方法。

请注意,使用上述XML,Android 只会在当前Activity中查找该onClick方法myFancyMethod()。请记住,如果您使用的是片段,则很重要,因为即使您在上面使用片段添加XML,Android也不会onClick.java用于添加XML的片段文件中寻找方法。

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


4
我不是Java专家,但是,我的意思是匿名类。感谢您的回复。非常清楚。
emitrax

118
请注意,如果使用XML onclick,则必须将onclick方法(myFancyMethod())放入当前的Activity中。这一点很重要,如果你使用的是碎片,因为设置的onclick听众的编程方式将可能有方法处理点击的片段的onCreateView()......哪里会没有,如果从XML简称被发现。
彼得·阿杰泰

12
是的,该方法必须公开。
Octavian A. Damiean

12
有趣的是,在代码中执行此操作确实允许通过将方法设置为私有来屏蔽方法访问,而以xml方式进行操作会导致方法公开。
bgse

5
函数(以XML方法)必须在Activity中这一事实不仅在您考虑片段时很重要,而且在自定义视图(包含按钮)中也很重要。当您拥有一个可在多个活动中重复使用的自定义视图,但又想在所有情况下都使用相同的onClick方法时,XML方法并不是最方便的方法。您将需要在每个使用自定义视图的活动中将此onClickMethod(具有相同的主体)放入。
Bartek Lipinski 2014年

87

当我看到最佳答案时,这使我意识到我的问题不是将参数(View v)放在fancy方法上:

public void myFancyMethod(View v) {}

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

android:onClick="myFancyMethod"/>

希望能对某人有所帮助。


74

android:onClick 适用于4级以上的API,因此如果您的目标是<1.6,则不能使用它。


33

检查您是否忘记公开该方法!


1
为什么必须公开?
eRaisedToX

3
@eRaisedToX我认为这很清楚:如果它不是公开的,则不能从Android框架中调用。
m0skit0

28

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

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

放大布局文件时,将实例化其中指定的所有视图。在这种特定情况下,Button实例是使用public Button (Context context, AttributeSet attrs, int defStyle)构造函数创建的。从资源束中读取XML标记中的所有属性,并将其传递AttributeSet给构造函数。

Buttonclass从Viewclass 继承而来,导致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中定义的回调方法。

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

  • 回调方法应为public:由于Java Class getMethod使用了回调方法,因此仅搜索具有公共访问说明符的函数。否则准备处理IllegalAccessException异常。
  • 在Fragment中将Button与onClick结合使用时,应在Activity中定义回调getContext().getClass().getMethod()call会将方法搜索限制为当前上下文,在Fragment的情况下为Activity。因此,方法是在Activity类而不是Fragment类中搜索的。
  • 回调方法应接受View parameter:因为Java Class getMethod搜索接受View.class作为参数的方法。

1
这对我来说是缺失的部分-Java使用Reflection查找以getContext()开头的单击处理程序。对于我来说,点击是如何从片段传播到活动的,这让我有点神秘。
Andrew Queisser 2014年

15

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

android:onclickXML中,Android 在后台使用Java反射来处理此问题。

而且这里解释,反思总是会减慢性能。(尤其是在Dalvik VM上)。注册onClickListener是更好的方法。


5
它会使应用程序减慢多少速度?:)半毫秒;甚至不?与实际
夸张

14

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

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

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


6

设置点击监听器的另一种方法是使用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"/>

Java部分

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

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

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

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

并且请注意,使用XML属性调用的函数应始终是公共的,如果将其声明为私有,则将导致异常。
Bestin John

5

在Java 8中,您可能会使用“ 方法参考”来实现所需的功能。

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

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

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

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

这将使您避免自己明确定义匿名类。但是,我必须强调Java 8的Method Reference实际上只是一个语法糖。它实际上为您创建了一个匿名类的实例(就像lambda表达式所做的一样),因此,当您注销事件处理程序时,应采取与lambda-expression-style事件处理程序类似的警告。这篇文章解释了它真的很好。

PS。对于那些对如何真正在Android中真正使用Java 8语言功能感到好奇的人,它由Retrolambda库提供。


5

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

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

当您在代码中初始化新的视图对象时,您只需 mView.setOnClickListener(this);

并自动将代码中的所有视图对象设置为使用onClick(View v)fragmentactivity etc拥有。

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

这个答案与“没有,不可能通过代码”这样的答案不同。


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

3

支持Ruivo的答案,是的,您必须将方法声明为“ public”才能在Android的XML onclick中使用-我正在开发一个应用程序,其目标级别是API级别8(minSdk ...)到16级(targetSdk ...)。

我在声明我的方法为私有方法时,它导致了错误,只是在声明它为公共方法时才出错。


似乎在托管Activity的类中声明的变量无法在声明的回调范围内使用;如果在myFancyMethod()中使用了Bundle Activity.mBundle,它将抛出IllegalStateException / NullPointerException。
Quasaur 2013年

2

请注意,尽管android:onClickXML似乎是处理点击的一种便捷方法,但是该setOnClickListener实现除了添加之外还做了其他事情onClickListener。确实,它使view属性clickable为true。

尽管在大多数Android实现中这可能不是问题,但根据电话构造函数的说法,button始终默认为clickable = true,但某些电话模型上的其他构造函数在非Button视图上可能具有默认的clickable = false。

因此,仅设置XML是不够的,您必须一直考虑添加android:clickable="true"非按钮,并且如果您的设备的默认设置为clickable = true,并且甚至忘记一次放置此XML属性,您都不会注意到在运行时出现问题,但是当它掌握在客户手中时,它将获得市场反馈!

另外,我们永远无法确定proguard将如何混淆和重命名XML属性和类方法,因此不能百分百保证它们一天不会出错。

因此,如果您从不希望遇到麻烦而从未考虑过,最好使用setOnClickListener带有注释的ButterKnife之类的库或@OnClick(R.id.button)


onClickXML属性还规定clickable = true,因为它要求setOnClickListenerView内部
弗洛里安·瓦尔特

1

假设您要添加这样的click事件 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) {
}

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) {
}

这在片段中是可能的。
user2786249 2015年

0

最好的方法是使用以下代码:

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

我看不出如何回答这个问题-询问者希望避免创建匿名类,而是使用类方法。
ajshort

0

为了使您的生活更轻松并避免setOnClicklistener()中的Anonymous Class,请实现一个View.OnClicklistener接口,如下所示:

公共类YourClass扩展CommonActivity实现View.OnClickListener,...

这样可以避免:

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        yourMethod(v);
    }
});

并直接转到:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.