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

从我所读到的,你可以用两种方式将onClick处理程序分配给按钮。

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

通过使用XML属性,你只需要定义一个方法而不是一个类,所以我是 想知道同样的事情是否可以通过代码而不是在XML布局中完成
439684 次浏览

android:onClick是API级别4以上的,所以如果你的目标是<1.6,那你就不能用了。

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

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

代码实现

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也不会在用于添加XML的片段的.java文件中查找onClick方法。

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

检查一下是否忘记将方法设置为public!

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

我宣称我的方法是私人的,它造成了错误,只是宣称它是公共工程。

注意,如果您想使用onClick XML特性,相应的方法应该有一个参数,其类型应该与XML对象匹配。

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

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

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

public void myFancyMethod(View v) {}

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

android:onClick="myFancyMethod"/>

希望这能帮助到别人。

通过使用XML属性,您只需要定义一个方法而不是 一个类,所以我想知道如果同样可以通过代码而不是在

. XML布局

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

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

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

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

这个答案不同于“不,通过代码是不可能的”

指定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中定义的回调方法。

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

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

这里有很好的答案,但我想补充一句:

在XML中的android:onclick中,Android在后台使用java反射来处理这个问题。

并且正如这里所解释的,反射总是降低性能。(特别是Dalvik VM)。注册onClickListener是一种更好的方法。

假设,你想添加像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) {
}
   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;
}}

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

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

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

在Java 8中,你可以使用方法引用来实现你想要的。

假设这是按钮的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的Method Reference实际上只是一个语法糖。它实际上为您创建了一个匿名类的实例(就像lambda表达式所做的那样),因此当您要取消事件处理程序的注册时,会应用lambda表达式风格的事件处理程序。这个文章解释得很好。

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

另一种设置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
}
});

下面是使用XML属性而不是匿名Java类的好处:

    对于匿名Java类,我们总是必须为我们的 元素,但带有XML属性id可以省略 使用匿名Java类,我们必须主动搜索元素 在视图内部(findViewById部分),但带有XML属性
  • 匿名Java类需要至少5行代码,就像我们可以的那样 但是有了XML属性,3行代码就足够了
  • 匿名Java类,我们必须命名我们的方法“onClick”, 但对于XML属性,我们可以添加任何我们想要的名称,这将 极大地提高了代码的可读性
  • Xml“onClick”属性已被谷歌在API级别添加 4 release,这意味着它的语法更现代 语法总是更好。

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

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

注意,虽然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)注释的库

为了让你的生活更简单,并避免setOnClicklistener()中的匿名类,实现一个视图。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;
}
}