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

时间:2010-11-11 10:39:00

标签: android onclick

据我所知,您可以通过两种方式为按钮分配onClick处理程序。

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

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

18 个答案:

答案 0 :(得分:565)

不,这是不可能通过代码。 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
}

以上是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代码,在点击事件上调用你的方法。

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

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

答案 1 :(得分:82)

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

public void myFancyMethod(View v) {}

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

android:onClick="myFancyMethod"/>

希望能有所帮助。

答案 2 :(得分:72)

android:onClick适用于API级别4以上,因此,如果您的目标是&lt; 1.6,那你就不能用了。

答案 3 :(得分:30)

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

答案 4 :(得分:26)

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

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

当布局文件膨胀时,其中指定的所有视图都会被实例化。在此特定情况下,使用public Button (Context context, AttributeSet attrs, int defStyle)构造函数创建Button实例。 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中定义的回调方法。

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

  • 回调方法应该是公开的:由于使用了Java Class getMethod,因此只搜索具有公共访问说明符的函数。否则准备处理IllegalAccessException例外。
  • 在片段中使用带onClick的Button时,应在Activity 中定义回调:getContext().getClass().getMethod()调用将方法搜索限制为当前上下文,如果是片段,则为Activity。因此,在Activity类中搜索方法而不是Fragment类。
  • 回调方法应接受查看参数:由于Java Class getMethod搜索接受View.class作为参数的方法。

答案 5 :(得分:13)

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

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

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

答案 6 :(得分:11)

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

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

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

答案 7 :(得分:5)

设置on click侦听器的另一种方法是使用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。
  • 使用匿名Java类,我们必须主动搜索元素 在视图内部(findViewById部分),但具有XML属性 Android为我们做了。
  • 匿名Java类需要至少5行代码,我们可以 请参阅,但使用XML属性3行代码就足够了。
  • 使用Anonymous Java类,我们必须命名我们的方法“onClick&#34;”, 但是使用XML属性,我们可以添加我们想要的任何名称 极大地帮助了我们代码的可读性。
  • Google在API级别添加了Xml“onClick”属性 4发布,这意味着它更具现代语法和现代感 语法几乎总是更好。

当然,并不总是可以使用Xml属性,以下是我们不选择它的原因:

  • 如果我们正在使用片段。 onClick属性只能添加 一个活动,所以如果我们有一个片段,我们将不得不使用一个 匿名课程。
  • 如果我们想将onClick侦听器移动到单独的类中 (也许如果它非常复杂和/或我们想重新使用它 我们的应用程序的不同部分),那么我们就不想使用了 xml属性。

答案 8 :(得分:4)

  

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

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

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

并自动设置代码中的所有视图对象,以使用onClick(View v)fragment等具有的activity方法。

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

这个答案不同于说#34;不能通过代码&#34;

答案 9 :(得分: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;
    }}

答案 10 :(得分:4)

使用Java 8,您可以使用Method Reference来实现您想要的目标。

假设这是一个按钮的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的方法参考实际上只是一个语法糖。它实际上为您创建了一个匿名类的实例(就像lambda表达式一样),因此当您取消注册事件处理程序时,应用了类似于lambda-expression-style事件处理程序的注意事项。这个article解释得非常好。

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

答案 11 :(得分:3)

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

我将我的方法声明为私有,并且导致错误,只是将其声明为公共作品。

答案 12 :(得分:1)

假设您要添加点击事件,例如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) {
}

答案 13 :(得分:1)

How android:onclick xml attribute works in Android

只需在android:onClick属性值中使用方法的名称即可。确保在方法名称之前使用了公共关键字。

Android onClick XML example

答案 14 :(得分:1)

请注意,虽然android:onClick XML似乎是处理点击的便捷方式,但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)

答案 15 :(得分:0)

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

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

答案 16 :(得分: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) {
}

答案 17 :(得分: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;
  }
}