Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2006 The Android Open Source Project
   *               2011 Jake Wharton
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package com.actionbarsherlock.view;
 
 
This class is used to instantiate menu XML files into Menu objects.

For performance reasons, menu inflation relies heavily on pre-processing of XML files that is done at build time. Therefore, it is not currently possible to use MenuInflater with an XmlPullParser over a plain XML file at runtime; it only works with an XmlPullParser returned from a compiled resource (R. something file.)

 
 public class MenuInflater {
     private static final String LOG_TAG = "MenuInflater";

    
Menu tag name in XML.
 
     private static final String XML_MENU = "menu";

    
Group tag name in XML.
 
     private static final String XML_GROUP = "group";

    
Item tag name in XML.
 
     private static final String XML_ITEM = "item";
 
     private static final int NO_ID = 0;
 
     private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
 
 
     private final Object[] mActionViewConstructorArguments;
 
     private final Object[] mActionProviderConstructorArguments;
 
     private Context mContext;
     private Object mRealOwner;

    
Constructs a menu inflater.

See also:
Activity#getMenuInflater()
 
     public MenuInflater(Context context) {
          = context;
          = context;
          = new Object[] {context};
     }

    
Constructs a menu inflater.

See also:
Activity#getMenuInflater()
Hide:
 
     public MenuInflater(Context contextObject realOwner) {
          = context;
          = realOwner;
          = new Object[] {context};
     }

    
Inflate a menu hierarchy from the specified XML resource. Throws android.view.InflateException if there is an error.

Parameters:
menuRes Resource ID for an XML layout resource to load (e.g., R.menu.main_activity)
menu The Menu to inflate into. The items and submenus will be added to this Menu.
    public void inflate(int menuResMenu menu) {
        XmlResourceParser parser = null;
        try {
            parser = .getResources().getLayout(menuRes);
            AttributeSet attrs = Xml.asAttributeSet(parser);
            parseMenu(parserattrsmenu);
        } catch (XmlPullParserException e) {
            throw new InflateException("Error inflating menu XML"e);
        } catch (IOException e) {
            throw new InflateException("Error inflating menu XML"e);
        } finally {
            if (parser != nullparser.close();
        }
    }

    
Called internally to fill the given menu. If a sub menu is seen, it will call this recursively.
    private void parseMenu(XmlPullParser parserAttributeSet attrsMenu menu)
            throws XmlPullParserExceptionIOException {
        MenuState menuState = new MenuState(menu);
        int eventType = parser.getEventType();
        String tagName;
        boolean lookingForEndOfUnknownTag = false;
        String unknownTagName = null;
        // This loop will skip to the menu start tag
        do {
            if (eventType == .) {
                tagName = parser.getName();
                if (tagName.equals()) {
                    // Go to next tag
                    eventType = parser.next();
                    break;
                }
                throw new RuntimeException("Expecting menu, got " + tagName);
            }
            eventType = parser.next();
        } while (eventType != .);
        boolean reachedEndOfMenu = false;
        while (!reachedEndOfMenu) {
            switch (eventType) {
                case .:
                    if (lookingForEndOfUnknownTag) {
                        break;
                    }
                    tagName = parser.getName();
                    if (tagName.equals()) {
                        menuState.readGroup(attrs);
                    } else if (tagName.equals()) {
                        menuState.readItem(attrs);
                    } else if (tagName.equals()) {
                        // A menu start tag denotes a submenu for an item
                        SubMenu subMenu = menuState.addSubMenuItem();
                        // Parse the submenu into returned SubMenu
                        parseMenu(parserattrssubMenu);
                    } else {
                        lookingForEndOfUnknownTag = true;
                        unknownTagName = tagName;
                    }
                    break;
                case .:
                    tagName = parser.getName();
                    if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
                        lookingForEndOfUnknownTag = false;
                        unknownTagName = null;
                    } else if (tagName.equals()) {
                        menuState.resetGroup();
                    } else if (tagName.equals()) {
                        // Add the item if it hasn't been added (if the item was
                        // a submenu, it would have been added already)
                        if (!menuState.hasAddedItem()) {
                            if (menuState.itemActionProvider != null &&
                                    menuState.itemActionProvider.hasSubMenu()) {
                                menuState.addSubMenuItem();
                            } else {
                                menuState.addItem();
                            }
                        }
                    } else if (tagName.equals()) {
                        reachedEndOfMenu = true;
                    }
                    break;
                case .:
                    throw new RuntimeException("Unexpected end of document");
            }
            eventType = parser.next();
        }
    }
    private static class InflatedOnMenuItemClickListener
            implements MenuItem.OnMenuItemClickListener {
        private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
        private Object mRealOwner;
        private Method mMethod;
        public InflatedOnMenuItemClickListener(Object realOwnerString methodName) {
             = realOwner;
            Class<?> c = realOwner.getClass();
            try {
                 = c.getMethod(methodName);
            } catch (Exception e) {
                InflateException ex = new InflateException(
                        "Couldn't resolve menu item onClick handler " + methodName +
                        " in class " + c.getName());
                ex.initCause(e);
                throw ex;
            }
        }
        public boolean onMenuItemClick(MenuItem item) {
            try {
                if (.getReturnType() == .) {
                    return (Boolean.invoke(item);
                } else {
                    .invoke(item);
                    return true;
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    
State for the current menu.

Groups can not be nested unless there is another menu (which will have its state class).

    private class MenuState {
        private Menu menu;
        /*
         * Group state is set on items as they are added, allowing an item to
         * override its group state. (As opposed to set on items at the group end tag.)
         */
        private int groupId;
        private int groupCategory;
        private int groupOrder;
        private int groupCheckable;
        private boolean groupVisible;
        private boolean groupEnabled;
        private boolean itemAdded;
        private int itemId;
        private int itemCategoryOrder;
        private CharSequence itemTitle;
        private CharSequence itemTitleCondensed;
        private int itemIconResId;
        private char itemAlphabeticShortcut;
        private char itemNumericShortcut;
        
Sync to attrs.xml enum: - 0: none - 1: all - 2: exclusive
        private int itemCheckable;
        private boolean itemChecked;
        private boolean itemVisible;
        private boolean itemEnabled;

        
Sync to attrs.xml enum, values in MenuItem: - 0: never - 1: ifRoom - 2: always - -1: Safe sentinel for "no value".
        private int itemShowAsAction;
        private int itemActionViewLayout;
        private String itemActionViewClassName;
        private String itemActionProviderClassName;
        private String itemListenerMethodName;
        private ActionProvider itemActionProvider;
        private static final int defaultGroupId = ;
        private static final int defaultItemId = ;
        private static final int defaultItemCategory = 0;
        private static final int defaultItemOrder = 0;
        private static final int defaultItemCheckable = 0;
        private static final boolean defaultItemChecked = false;
        private static final boolean defaultItemVisible = true;
        private static final boolean defaultItemEnabled = true;
        public MenuState(final Menu menu) {
            this. = menu;
            resetGroup();
        }
        public void resetGroup() {
             = ;
             = ;
             = ;
             = ;
             = ;
             = ;
        }

        
Called when the parser is pointing to a group tag.
        public void readGroup(AttributeSet attrs) {
            TypedArray a = .obtainStyledAttributes(attrs,
                    ..);
            a.recycle();
        }

        
Called when the parser is pointing to an item tag.
        public void readItem(AttributeSet attrs) {
            TypedArray a = .obtainStyledAttributes(attrs,
                    ..);
            // Inherit attributes from the group as default value
            final int category = a.getInt(..);
            final int order = a.getInt(..);
             = (category & .) | (order & .);
             =
             =
                    getShortcut(a.getString(..));
            if (a.hasValue(..)) {
                // Item has attribute checkable, use it
                 = a.getBoolean(..false) ? 1 : 0;
            } else {
                // Item does not have attribute, use the group's (group can have one more state
                // for checkable that represents the exclusive checkable)
                 = ;
            }
            TypedValue value = new TypedValue();
            a.getValue(..value);
             = value.type == . ? value.data : -1;
            // itemActionViewClassName = a.getString(R.styleable.SherlockMenuItem_android_actionViewClass);
            value = new TypedValue();
             = value.type == . ? value.string.toString() : null;
            // itemActionProviderClassName = a.getString(R.styleable.SherlockMenuItem_android_actionProviderClass);
            value = new TypedValue();
             = value.type == . ? value.string.toString() : null;
            final boolean hasActionProvider =  != null;
            if (hasActionProvider &&  == 0 &&  == null) {
                            ,
                            );
            } else {
                if (hasActionProvider) {
                    Log.w("Ignoring attribute 'actionProviderClass'."
                            + " Action view already specified.");
                }
                 = null;
            }
            a.recycle();
             = false;
        }
        private char getShortcut(String shortcutString) {
            if (shortcutString == null) {
                return 0;
            } else {
                return shortcutString.charAt(0);
            }
        }
        private void setItem(MenuItem item) {
            item.setChecked()
                .setVisible()
                .setEnabled()
                .setCheckable( >= 1)
                .setTitleCondensed()
                .setIcon()
                .setAlphabeticShortcut()
                .setNumericShortcut();
            if ( >= 0) {
                item.setShowAsAction();
            }
            if ( != null) {
                if (.isRestricted()) {
                    throw new IllegalStateException("The android:onClick attribute cannot "
                            + "be used within a restricted context");
                }
                item.setOnMenuItemClickListener(
                        new InflatedOnMenuItemClickListener());
            }
            if ( >= 2) {
                if (item instanceof MenuItemImpl) {
                    MenuItemImpl impl = (MenuItemImplitem;
                    impl.setExclusiveCheckable(true);
                } else {
                    .setGroupCheckable(truetrue);
                }
            }
            boolean actionViewSpecified = false;
            if ( != null) {
                View actionView = (ViewnewInstance(,
                        );
                item.setActionView(actionView);
                actionViewSpecified = true;
            }
            if ( > 0) {
                if (!actionViewSpecified) {
                    item.setActionView();
                    actionViewSpecified = true;
                } else {
                    Log.w("Ignoring attribute 'itemActionViewLayout'."
                            + " Action view already specified.");
                }
            }
            if ( != null) {
                item.setActionProvider();
            }
        }
        public void addItem() {
             = true;
            setItem(.add());
        }
        public SubMenu addSubMenuItem() {
             = true;
            SubMenu subMenu = .addSubMenu();
            setItem(subMenu.getItem());
            return subMenu;
        }
        public boolean hasAddedItem() {
            return ;
        }
        @SuppressWarnings("unchecked")
        private <T> T newInstance(String classNameClass<?>[] constructorSignature,
                Object[] arguments) {
            try {
                Class<?> clazz = .getClassLoader().loadClass(className);
                Constructor<?> constructor = clazz.getConstructor(constructorSignature);
                return (T) constructor.newInstance(arguments);
            } catch (Exception e) {
                Log.w("Cannot instantiate class: " + classNamee);
            }
            return null;
        }
    }
New to GrepCode? Check out our FAQ X