InflateException:无法解析onClick处理程序的菜单项

Fra*_*per 35 android actionbarsherlock android-actionbar

我在6年前问过这个问题.与此同时,Android开发最佳实践已发生变化,我已成为更好的开发人员.

从那以后,我意识到使用onClickXML属性是一种不好的做法,并将其从我工作的任何代码库中删除.

我的所有点击处理程序现在都在应用程序的代码中定义,而不是XML布局!

我从不使用的理由onClick

  1. 很容易在onClickXML属性的值中出错,这将导致运行时错误
  2. 开发人员可能会重构单击处理程序方法的名称,而不会意识到它是从布局中调用的(请参阅原因1)
  3. 找出实际被调用的方法并不总是显而易见的.特别是如果片段正在使用布局
  4. 将布局与行为的关注分开是好的.使用onClick它们混合起来,这很糟糕!

我希望我已经说服你永远不要onClick在布局中使用:)!

下面是我原来的问题,这是一个很好的说明为什么使用onClick是一个坏主意.

===

我在XML中定义菜单项,并尝试使用API​​ 11中添加的onClick属性.当在运行4.0.3的模拟器中启动Activity时,会发生以下异常:

FATAL EXCEPTION: main
android.view.InflateException: Couldn't resolve menu item onClick handler 
    onFeedbackMenu in class android.view.ContextThemeWrapper

...
Caused by: java.lang.NoSuchMethodException: onFeedbackMenu 
    [interface com.actionbarsherlock.view.MenuItem]
at java.lang.Class.getConstructorOrMethod(Class.java:460)
Run Code Online (Sandbox Code Playgroud)

我不明白导致异常的原因,因为我的Activity中定义了以下方法

import com.actionbarsherlock.view.MenuItem;
...
public void onFeedbackMenu( MenuItem menuItem ) { 
    Toast.makeText( this, "onFeedBack", Toast.LENGTH_LONG ).show();
}
Run Code Online (Sandbox Code Playgroud)

我的XML菜单定义文件包含:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
...
    <item
        android:id="@+id/menu_feedback"
        android:icon="@drawable/ic_action_share"
        android:showAsAction="ifRoom"
        android:title="@string/menu_feedback"
        android:onClick="onFeedbackMenu" />
</menu>
Run Code Online (Sandbox Code Playgroud)

为了向后兼容,我使用的是ActionBarSherlock,当我在2.3.x上运行App时,它也会得到一个非常类似的Exception.

这是堆栈跟踪的更完整版本

FATAL EXCEPTION: main
android.view.InflateException: Couldn't resolve menu item onClick handler 
    onFeedbackMenu in class android.view.ContextThemeWrapper
    at com.actionbarsherlock.view.MenuInflater$InflatedOnMenuItemClickListener.<init>(MenuInflater.java:204)
    at com.actionbarsherlock.view.MenuInflater$MenuState.setItem(MenuInflater.java:410)
    at com.actionbarsherlock.view.MenuInflater$MenuState.addItem(MenuInflater.java:445)
    at com.actionbarsherlock.view.MenuInflater.parseMenu(MenuInflater.java:175)
    at com.actionbarsherlock.view.MenuInflater.inflate(MenuInflater.java:97)
    ...
Caused by: java.lang.NoSuchMethodException: onFeedbackMenu 
    [interface com.actionbarsherlock.view.MenuItem]
    at java.lang.Class.getConstructorOrMethod(Class.java:460)
    at java.lang.Class.getMethod(Class.java:915)
    at com.actionbarsherlock.view.MenuInflater$InflatedOnMenuItemClickListener.<init>(MenuInflater.java:202)
    ... 23 more
Run Code Online (Sandbox Code Playgroud)

小智 73

我找到了一个适合我的解决方案.通常,onClick布局中的属性具有以下方法

public void methodname(View view) { 
    // actions
}
Run Code Online (Sandbox Code Playgroud)

在菜单项(在本例中为Sherlock菜单)上,它应遵循以下签名:

public boolean methodname(MenuItem item) { 
    // actions
}
Run Code Online (Sandbox Code Playgroud)

所以,你的问题是你的方法返回void而不是boolean.


jun*_*que 16

就我而言,AndroidManifest.xml我的应用程序(由默认的Eclipse助手启动)包含android:theme="@style/AppTheme"<application>块中.

在调试问题的原因时,事实证明该行

mMethod = c.getMethod(methodName, PARAM_TYPES);
Run Code Online (Sandbox Code Playgroud)

in android.view.MenuInflater/InflatedOnMenuItemClickListener被称为c不是我的Activity班级,而是一个可疑的android.view.ContextThemeWrapper(当然不包含onClick处理程序).

所以,我删除了android:theme所有工作.

  • 这个很有趣,因为看起来这是我的问题的原因.但是,任何需要(或想要)主题集的人都会遇到这个问题(在API 14/15上,而不是我能说的API 16+).因此,我的解决方案是删除onClick赋值并只添加对onOptionsItemSelected()方法的调用. (2认同)

fin*_*usl 9

虽然这有点过时,但这是异常的原因.当您在MenuInflater类中查看android API 15(4.0.3-4.0.4)的源代码时,您将看到以下方法:

public InflatedOnMenuItemClickListener(Context context, String methodName) {
mContext = context;
Class<?> c = context.getClass();
try {
    mMethod = c.getMethod(methodName, PARAM_TYPES);
} catch (Exception e) {
    InflateException ex = new InflateException(
            "Couldn't resolve menu item onClick handler " + methodName +
            " in class " + c.getName());
    ex.initCause(e);
    throw ex;
}
Run Code Online (Sandbox Code Playgroud)

正如Junique已经指出的那样,这是例外情况.然而,删除应用程序主题只是一种解决方法,没有真正的选择.正如我们所看到的,该方法试图在传递的上下文项的类上找到Callback方法.因此,不再拨打电话getMenuInflater()onCreateOptionsMenu,你应该调用new MenuInflater(this),以便this作为一个环境传送,然后代码将工作.

getMenuInflater()如果你只使用这样的if语句,你仍然可以用于其他api版本:

if (Build.VERSION.SDK_INT > 15)
        inflater = getMenuInflater();
    else
        inflater = new MenuInflater(this);
Run Code Online (Sandbox Code Playgroud)

我实际上并不知道这个错误是否也发生在15岁以下的api版本中,所以我通常只使用保存版本.