android:onClick XML属性与setOnClickListener有什么不同?
从我读过,你可以分配一个onClick
处理程序的方式有两种方式。
使用android:onClick
XML属性,您只需使用具有签名void name(View v)
的公共方法的名称,或者使用setOnClickListener
方法在其中传递实现OnClickListener
接口的对象。 后者往往需要一个匿名的类,我个人不喜欢(个人品味)或定义一个实现OnClickListener
的内部类。
通过使用XML属性,您只需要定义一个方法而不是一个类,所以我想知道是否可以通过代码,而不是在XML布局完成。
不,这不可能通过代码。 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
方法。
我注意到另一件重要的事情 你提到你不喜欢匿名方法 。 你的意思是说你不喜欢匿名课程 。
当我看到最上面的答案,让我意识到我的问题,没有把参数(视图v)的花式方法
public void myFancyMethod(View v) {}
当试图从XML访问它
android:onClick="myFancyMethod"/>
希望能帮助别人
android:onClick
适用于API级别4以上,因此如果您定位到<1.6,则无法使用它。
检查你是否忘记把方法公开!
指定android:onClick
属性会导致Button
实例在内部调用setOnClickListener
。 因此绝对没有区别。
为了清楚理解,让我们看看框架如何处理XML onClick
属性。
当一个布局文件膨胀时,其中指定的所有视图都被实例化。 在这个特定的情况下, Button
实例是使用public Button (Context context, AttributeSet attrs, int defStyle)
构造函数创build的。 XML标签中的所有属性都从资源包中读取,并作为AttributeSet
传递给构造函数。
Button
类inheritance自View
类,这会导致View
构造函数被调用,它负责通过setOnClickListener
设置点击callback处理程序。
在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
被调用来注册callback,就像我们在代码中所做的那样。 唯一不同的是它使用Java Reflection
来调用在我们的Activity中定义的callback方法。
以下是其他答案中提到的问题的原因:
- callback方法应该是公开的 :由于使用
Java Class getMethod
,所以只search具有公共访问说明符的函数。 否则,准备好处理IllegalAccessException
exception。 - 在Activity中使用Button和onClick时,应该在Activity中定义callback :
getContext().getClass().getMethod()
调用将方法search限制在当前上下文中,在Fragment情况下为Activity。 因此,方法在Activity类中search而不是Fragment类。 - callback方法应接受查看参数 :由于
Java Class getMethod
search接受View.class
作为参数的方法。
请注意,如果要使用onClick XMLfunction,则相应的方法应该有一个参数,其types应与XML对象匹配。
例如, button将通过其名称string链接到您的方法: android:onClick="MyFancyMethod"
但方法声明应该显示: ...MyFancyMethod(View v) {...
如果您试图将此function添加到菜单项 ,它将在XML文件中具有完全相同的语法,但您的方法将声明为: ...MyFancyMethod(MenuItem mi) {...
这里有很好的答案,但我想添加一行:
在android:onclick
在XML中,Android使用场景后面的javareflection来处理这个。
正如这里所解释的那样,反思总是会减慢性能。 (尤其是在Dhalvik VM上)。 注册onClickListener
是一个更好的方法。
使用Java 8,你可以使用方法参考来实现你想要的。
假设这是一个button的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的方法参考实际上只是一个语法糖。 它实际上为你创build了一个匿名类的实例(就像lambdaexpression式一样),因此当你去注册你的事件处理程序的时候,应用lambdaexpression式风格的事件处理程序是相当谨慎的。 这篇文章解释它非常好。
PS。 对于那些对如何在Android中真正使用Java 8语言function感到好奇的人来说,这是一个retrolambda库的礼貌。
设置点击监听器的另一种方法是使用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属性的好处:
- 使用匿名Java类我们总是要为我们的元素指定一个id,但是用XML属性id可以省略。
- 有了匿名Java类,我们必须主动search视图内部的元素(findViewById部分),但是Android属性为我们提供了XML属性。
- 正如我们所看到的,匿名Java类至less需要5行代码,但是对于XML属性,3行代码就足够了。
- 使用匿名Java类,我们必须命名我们的方法“onClick”,但是使用XML属性,我们可以添加任何我们想要的名字,这将大大帮助我们的代码的可读性。
- Google在API级别4版本中添加了Xml“onClick”属性,这意味着它是一种更现代的语法,现代语法几乎总是更好。
当然,使用Xml属性并不总是可能的,下面是我们不select它的原因:
- 如果我们正在处理碎片。 onClick属性只能添加到一个活动,所以如果我们有一个片段,我们将不得不使用一个匿名类。
- 如果我们想将onClick监听器移动到一个单独的类(也许如果它是非常复杂的和/或我们想在我们的应用程序的不同部分重用),那么我们不希望使用xml属性无论是。
支持Ruivo的答案,是的,你必须声明方法为“公共”,才能够在Android的XML onclick中使用 – 我正在开发一个应用程序从API级别8(minSdk …)到16(targetSdk …)的目标。
我宣布我的方法是私人的,它造成了错误,只是宣布它作为公共工程伟大。
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属性,您只需要定义一个方法而不是一个类,所以我想知道是否可以通过代码,而不是在XML布局完成。
是的,你可以让你的fragment
或activity
实现View.OnClickListener
当你在代码中初始化新的视图对象时,你可以简单地使用mView.setOnClickListener(this);
这会自动设置代码中的所有视图对象,以使用fragment
或activity
等所具有的onClick(View v)
方法。
要区分哪个视图调用了onClick
方法,可以在v.getId()
方法上使用switch语句。
这个答案不同于那个说“不可能通过代码”
假设你想添加像这个main.xml
一样的click事件
<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) { }
我写这个代码在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 } });
要小心,尽pipeandroid:onClick
XML似乎是一种处理点击的便捷方式,但setOnClickListener
实现还是做了一些比添加setOnClickListener
更多的function。 的确,它把view属性clickable
为true。
虽然在大多数Android实现中可能不是问题,但根据电话构造函数,button始终默认为clickable = true,但某些电话型号上的其他构造函数在非Buttonbutton上可能具有默认clickable = false。
因此,设置XML是不够的,你必须一直认为在非button上添加android:clickable="true"
,如果你有一个默认设置为clickable = true的设备,并且你忘记了这个XML属性,在运行时你不会注意到这个问题,但是当它将在你的客户手中的时候,会在市场上得到反馈!
另外,我们永远无法确定proguard是如何混淆和重命名XML属性和类方法的,所以不是100%安全的,他们永远不会有bug。
所以,如果你不想有麻烦,也从来没有想过,最好使用setOnClickListener
或像ButterKnife这样的库来注释@OnClick(R.id.button)