简体   繁体   中英

ActionBar menu item different behaviour

I'm working in my Android project developing with a HTC Desire (Gingerbread 2.3.7) and a Google Nexus 7 (Jelly Bean 4.3). I need to send some data from MainActivity to InfoActivity, so I use an intent. In this InfoActivity, I also have a menu item in the action bar to refresh the info.

在此处输入图片说明

In InfoActivity I show the data to the user. But this is not the problem, the problem is with the menu. Look at the following code:

public class ShowInfoActivity extends ActionBarActivity {

    private MenuItem menuItem   = null;
    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        new OneTask().execute(...);
        // ...
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        case R.id.refresh:
            menuItem = item;
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private class OneTask extends AsyncTask<Object, Void, String> {
        // ...
        @Override
        protected void onPreExecute() {
            MenuItemCompat.setActionView(menuItem,
                        R.layout.actionbar_indeterminate_progress);
            MenuItemCompat.expandActionView(menuItem);
        }
        // ...
        @Override
        protected void onPostExecute(String result) {
            MenuItemCompat.collapseActionView(menuItem);
            MenuItemCompat.setActionView(menuItem, null);
        }
    }

Obviously, the first time it's executed, menuItem=null, so it must crash. Incredibly, in HTC it works fine but in Nexus it obviously crashes. Why is this different between devices?

PS: I already solved it, but I want to know why this behaviour...

When in doubt, always check the source code. If you look at MenuItemCompat.java you'll find that it switches based on the API level like so:

    static final MenuVersionImpl IMPL;
    static {
        final int version = android.os.Build.VERSION.SDK_INT;
        if (version >= 14) {
            IMPL = new IcsMenuVersionImpl();
        } else if (version >= 11) {
            IMPL = new HoneycombMenuVersionImpl();
        } else {
            IMPL = new BaseMenuVersionImpl();
        }
    }

The base setActionView method for the base implementation (which is used for 2.3 devices) just returns the MenuItem, so it wouldn't ever throw the exception:

@Override
public MenuItem setActionView(MenuItem item, View view) {
    return item;
}

The HoneycombMenuVersionImpl, on the other hand, delegates to another class:

        @Override
        public boolean setShowAsAction(MenuItem item, int actionEnum) {
            MenuItemCompatHoneycomb.setShowAsAction(item, actionEnum);
            return true;
        }

And the delegate class attempts to call the actual method on the MenuItem, which will throw an exception:

public static void setShowAsAction(MenuItem item, int actionEnum) {
    item.setShowAsAction(actionEnum);
}

In this particular example, checking the source code answers your question and shows you a solid strategy for dealing with compatibility across different versions of Android.

setActionView()/collapseActionView() are native functions in Android 4 ... passing null MenuItem will surely be frowned upon.

On your Android 2.3 device, the compatibility library (android.support.v4) has it's own internal implementations of setActionView()/collapseActionView() that are likely more resilient to bad input.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM