I asked this question 6 years ago. In the meantime Android development best practices have changed, and I have become a better developer.
Since then, I have realized that using the onClick
XML attribute is a bad practice, and have removed it from any code base I work on.
All of my click handlers are now defined in the code of the app, not the XML layouts!
My reasons for never using onClick
are
- it is easy to make a mistake in the value of the
onClick
XML attribute, which will then result in a run-time error
- a developer might refactor the name of the click handler method, without realizing it is called from a layout (see reason 1)
- finding out which method is actually being called is not always obvious. Especially if the layout is being used by a Fragment
- separating the concerns of layout vs behavior is good. Using
onClick
mixes them up, which is bad!
I hope I have convinced you to never use onClick
in a layout :) !
Below is my original question, which is a pretty good illustration of why using onClick
is a bad idea.
===
I'm defining menu items in XML, and trying to use the onClick attribute that was added in API 11. When the Activity is launched in an emulator running 4.0.3, the following Exceptions occur:
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)
I don't understand what is causing the Exception, since the following method is defined in my Activity
import com.actionbarsherlock.view.MenuItem;
...
public void onFeedbackMenu( MenuItem menuItem ) {
Toast.makeText( this, "onFeedBack", Toast.LENGTH_LONG ).show();
}
My XML menu definition file contains:
<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>
For backwards compatibility I am using ActionBarSherlock, and also getting a very similar Exception when I run the App on 2.3.x.
This is a more Complete version of the Stack trace
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
I found a solution that worked for me.
Usually the onClick
attribute in a layout has the following method
public void methodname(View view) {
// actions
}
On a menu item (in this case Sherlock menu) it should follow the following signature:
public boolean methodname(MenuItem item) {
// actions
}
So, your problem was that your method returned void
and not boolean
.
In my case, the AndroidManifest.xml
of my application (kick-started by the default Eclipse assistant) contained android:theme="@style/AppTheme"
in the <application>
block.
When debugging the cause of the problem, it turned out that the line
mMethod = c.getMethod(methodName, PARAM_TYPES);
in android.view.MenuInflater/InflatedOnMenuItemClickListener
was called with c
not being my Activity
class but a dubious android.view.ContextThemeWrapper
(which of course doesn't contain the onClick handler).
So, I removed the android:theme
and everything worked.
Although this is a bit out of date, here is the reason for the exception. When you look into the sources of android API 15 (4.0.3-4.0.4) in the class MenuInflater you will see this method:
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;
}
This is were the exception happens, as Junique already pointed out. However the removing of the app theme is just a workaround and no real option. As we see the method tries to find the Callback method on the class of the context item passed. So instead of calling getMenuInflater()
in onCreateOptionsMenu
you should call new MenuInflater(this)
, so that this
is passed as a context and then the code will work.
You can still use getMenuInflater()
for other api versions if you just use an if statement like this:
if (Build.VERSION.SDK_INT > 15)
inflater = getMenuInflater();
else
inflater = new MenuInflater(this);
I don't actually know if the bug happens in api versions under 15 too, so i just generally used the save version.
In my case the problem was that I had both onClick
in my menu XML and an onCreateOptionsMenu
in my Activity. My onClick
was actually faulty (because it pointed to non-existent methods) but I didn't notice this at first because I was testing under Android 2.x, where onClick
is not supported and ignored. Once I tested on 4.x though, I started getting this error.
So basically, don't use onClick
if you plan on deploying under Android 2.x. It will silently ignore your onClick
values until you try running on 3.0+.
I found that I had the same problem with the ActionBar menu items, and their onClick events. What i discovered is that the workstation I'm developing in had run out of memory and needed to be rebooted. The Android VM is now able to resolve the method name referenced.
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.activity_main, menu);
MenuItem item = menu.findItem(R.id.menu_open);
if (item == null)
return true;
item.setOnMenuItemClickListener
(
new MenuItem.OnMenuItemClickListener ()
{
public boolean onMenuItemClick(MenuItem item)
{ return (showDirectory(item)); }
}
);
return true;
}
public boolean showDirectory (MenuItem item)
{
CheckBox checkBox = (CheckBox) findViewById (R.id.checkBox1);
checkBox.setChecked(true);
}
Your method must accept a MenuItem as its only parameter per here.
public void onMenuItemClickMethod(MenuItem menuItem){
// Do stuff here
}