How exactly does the android:onClick XML attribute differ from setOnClickListener?
我已经读过了,您可以通过两种方式将
使用
通过使用XML属性,您只需要定义一个方法而不是类,所以我想知道是否可以通过代码而不是在XML布局中完成相同的工作。
不,通过代码是不可能的。当您定义
这两个代码片段是相等的,只是以两种不同的方式实现。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 } |
上面是
XML实现
1 2 3 4 5 6 7 8 | <?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将只在当前活动中查找
我注意到的另一件重要事情。你说过你不喜欢匿名的方法。你想说你不喜欢匿名课程。
当我看到最上面的答案时,我意识到我的问题不是把参数(视图V)放在花式方法上:
1 | public void myFancyMethod(View v) {} |
当试图从XML访问它时,应该使用
1 | android:onClick="myFancyMethod"/> |
希望能帮助别人。
检查是否忘记将方法公开!
指定
为了有清晰的理解,让我们看看这个框架是如何处理XML
展开布局文件时,将实例化其中指定的所有视图。在这种特定情况下,使用
attrs.xml中定义的onclick属性在view.java中称为
这里是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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; |
如您所见,调用
以下是其他答案中提到的问题的原因:
- 回调方法应该是公共的:因为使用了
Java Class getMethod ,所以只搜索具有公共访问说明符的函数。否则准备好处理IllegalAccessException 异常。 - 当使用带有onclick-in片段的按钮时,回调应该在activity中定义:
getContext().getClass().getMethod() 调用将方法搜索限制在当前上下文中,即fragment中的activity。因此,方法是在活动类而不是片段类中搜索的。 - 回调方法应该接受视图参数:因为
Java Class getMethod 搜索接受View.class 作为参数的方法。
注意,如果要使用onclick XML特性,对应的方法应该有一个参数,其类型应该与XML对象匹配。
例如,按钮将通过其名称字符串:
如果您试图将此功能添加到菜单项中,它在XML文件中将具有完全相同的语法,但您的方法将声明为:
这里有很好的答案,但我想增加一行:
在EDCOX1×0的XML中,Android使用场景背后的Java反射来处理这个问题。
正如这里所解释的,反射总是减慢性能。(尤其是在达尔维克虚拟机上)。注册
另一种设置点击监听器的方法是使用XML。只需将android:onclick属性添加到标签中。
如果可能的话,在匿名Java类上使用XML属性"OnCalk"是一个很好的实践。
首先,让我们看一下代码的区别:
xml属性/onclick属性
XML部分
1 2 3 4 5 | <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button1" android:onClick="showToast"/> |
爪哇部分
1 2 3 | public void showToast(View v) { //Add some logic } |
匿名Java类/ StutoCnLink监听器
XML部分
1 2 3 | <Button android:layout_width="wrap_content" android:layout_height="wrap_content"/> |
爪哇部分
1 2 3 4 5 6 7 | findViewById(R.id.button1).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { //Add some logic } }); |
下面是在匿名Java类上使用XML属性的好处:
- 对于匿名Java类,我们必须为我们指定一个ID。元素,但可以省略WITHXML属性ID。
- 使用匿名Java类,我们必须主动搜索元素在视图内部(findViewByID部分),但具有XML属性安卓为我们做的。
- 匿名Java类需要至少5行代码,因为我们可以请参见,但对于XML属性,3行代码就足够了。
- 使用匿名Java类,我们必须命名我们的方法"OnCalk",但是使用XML属性,我们可以添加任何需要的名称,这将极大地提高了代码的可读性。
- XML"onclick"属性已由Google在API级别添加4发行版,这意味着它有点现代语法和现代语法几乎总是更好的。
当然,不可能总是使用XML属性,以下是我们不选择它的原因:
- 如果我们在处理碎片。只能添加onclick属性对于一个活动,所以如果我们有一个片段,我们必须使用匿名类。
- 如果我们想将onclick侦听器移动到单独的类中(如果非常复杂和/或我们希望在应用程序的不同部分),那么我们不想使用XML属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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; }} |
By using the XML attribute you just need to define a method instead of
a class so I was wondering if the same can be done via code and not in
the XML layout.
是的,你可以让你的
在代码中初始化新的视图对象时,只需执行
这会自动将代码中的所有视图对象设置为使用您的
为了区分哪个视图调用了
这个答案不同于"不,通过代码是不可能的"的答案。
使用Java 8,您可能可以使用方法引用来实现您想要的。
假设这是一个按钮的
1 2 3 4 5 | private void onMyButtonClicked(View v) { if (v.getId() == R.id.myButton) { // Do something when myButton was clicked } } |
然后,在这样的
1 2 | Button myButton = (Button) findViewById(R.id.myButton); myButton.setOnClickListener(this::onMyButtonClicked); |
这将允许您避免自己显式定义匿名类。然而,我必须强调Java 8的方法引用实际上只是一个语法糖。它实际上为您创建了一个匿名类的实例(就像lambda表达式一样),因此在注销事件处理程序时应用了类似lambda表达式样式的事件处理程序。这篇文章解释得很好。
对于那些好奇我如何在Android中真正使用Java 8语言特征的人来说,这是RealLAMDA库的礼貌。
支持ruivo的答案,是的,您必须声明方法为"public"才能在Android的XML onclick中使用-我正在开发一个应用程序,目标是从API级别8(minsdk…)到16(targetsdk…)。
我声明我的方法是私有的,它导致了错误,只是声明它是公共工程。
只需在android:onclick属性的值中使用方法的名称。确保在方法名称之前使用公共关键字。
android onclick XML示例
注意,虽然
虽然在大多数Android实现中这可能不是问题,但根据Phone构造函数,Button始终默认为clickable=true,但某些手机模型上的其他构造函数在非按钮视图中可能有默认的clickable=false。
所以设置XML是不够的,你必须一直考虑在非按钮上添加
此外,我们永远无法确定proguard将如何混淆和重命名XML属性和类方法,因此不100%安全,他们将永远不会有一天出现错误。
所以,如果你不想遇到麻烦,也不想去想它,最好使用
假设您想添加这样的click事件
1 2 3 4 5 6 7 8 9 | <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文件中,您必须编写类似此方法的方法。
1 2 | public void register(View view) { } |
我用XML文件编写这段代码…
1 2 3 4 5 6 7 8 9 | <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"/> |
把这段代码写成片段…
1 2 | public void register(View view) { } |
最好的方法是使用以下代码:
1 2 3 4 5 6 7 | Button button = (Button)findViewById(R.id.btn_register); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //do your fancy method } }); |