Пример #1
0
  public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
    final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title);
    final SubMenuBuilder subMenu = new SubMenuBuilder(mContext, this, item);
    item.setSubMenu(subMenu);

    return subMenu;
  }
Пример #2
0
  public boolean performItemAction(MenuItem item, int flags) {
    MenuItemImpl itemImpl = (MenuItemImpl) item;

    if (itemImpl == null || !itemImpl.isEnabled()) {
      return false;
    }

    boolean invoked = itemImpl.invoke();

    if (itemImpl.hasCollapsibleActionView()) {
      invoked |= itemImpl.expandActionView();
      if (invoked) close(true);
    } else if (item.hasSubMenu()) {
      close(false);

      final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
      final ActionProvider provider = item.getActionProvider();
      if (provider != null && provider.hasSubMenu()) {
        provider.onPrepareSubMenu(subMenu);
      }
      invoked |= dispatchSubMenuSelected(subMenu);
      if (!invoked) close(true);
    } else {
      if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
        close(true);
      }
    }

    return invoked;
  }
Пример #3
0
  /**
   * We want to return the menu item associated with the key, but if there is no ambiguity (i.e.
   * there is only one menu item corresponding to the key) we want to return it even if it's not an
   * exact match; this allow the user to _not_ use the ALT key for example, making the use of
   * shortcuts slightly more user-friendly. An example is on the G1, '!' and '1' are on the same
   * key, and in Gmail, Menu+1 will trigger Menu+! (the actual shortcut).
   *
   * <p>On the other hand, if two (or more) shortcuts corresponds to the same key, we have to only
   * return the exact match.
   */
  MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) {
    // Get all items that can be associated directly or indirectly with the keyCode
    ArrayList<MenuItemImpl> items = mTempShortcutItemList;
    items.clear();
    findItemsWithShortcutForKey(items, keyCode, event);

    if (items.isEmpty()) {
      return null;
    }

    final int metaState = event.getMetaState();
    final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
    // Get the chars associated with the keyCode (i.e using any chording combo)
    event.getKeyData(possibleChars);

    // If we have only one element, we can safely returns it
    final int size = items.size();
    if (size == 1) {
      return items.get(0);
    }

    final boolean qwerty = isQwertyMode();
    // If we found more than one item associated with the key,
    // we have to return the exact match
    for (int i = 0; i < size; i++) {
      final MenuItemImpl item = items.get(i);
      final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
      if ((shortcutChar == possibleChars.meta[0] && (metaState & KeyEvent.META_ALT_ON) == 0)
          || (shortcutChar == possibleChars.meta[2] && (metaState & KeyEvent.META_ALT_ON) != 0)
          || (qwerty && shortcutChar == '\b' && keyCode == KeyEvent.KEYCODE_DEL)) {
        return item;
      }
    }
    return null;
  }
Пример #4
0
  private static int findInsertIndex(ArrayList<MenuItemImpl> items, int ordering) {
    for (int i = items.size() - 1; i >= 0; i--) {
      MenuItemImpl item = items.get(i);
      if (item.getOrdering() <= ordering) {
        return i + 1;
      }
    }

    return 0;
  }
Пример #5
0
  public void setGroupEnabled(int group, boolean enabled) {
    final int N = mItems.size();

    for (int i = 0; i < N; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.getGroupId() == group) {
        item.setEnabled(enabled);
      }
    }
  }
Пример #6
0
  public void setShortcut(boolean showShortcut, char shortcutKey) {
    final int newVisibility = (showShortcut && mItemData.shouldShowShortcut()) ? VISIBLE : GONE;

    if (newVisibility == VISIBLE) {
      mShortcutView.setText(mItemData.getShortcutLabel());
    }

    if (mShortcutView.getVisibility() != newVisibility) {
      mShortcutView.setVisibility(newVisibility);
    }
  }
Пример #7
0
  public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
    final int N = mItems.size();

    for (int i = 0; i < N; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.getGroupId() == group) {
        item.setExclusiveCheckable(exclusive);
        item.setCheckable(checkable);
      }
    }
  }
Пример #8
0
  public int findItemIndex(int id) {
    final int size = size();

    for (int i = 0; i < size; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.getItemId() == id) {
        return i;
      }
    }

    return -1;
  }
Пример #9
0
  public boolean hasVisibleItems() {
    final int size = size();

    for (int i = 0; i < size; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.isVisible()) {
        return true;
      }
    }

    return false;
  }
Пример #10
0
  void setExclusiveItemChecked(MenuItem item) {
    final int group = item.getGroupId();

    final int N = mItems.size();
    for (int i = 0; i < N; i++) {
      MenuItemImpl curItem = mItems.get(i);
      if (curItem.getGroupId() == group) {
        if (!curItem.isExclusiveCheckable()) continue;
        if (!curItem.isCheckable()) continue;

        // Check the item meant to be checked, uncheck the others (that are in the group)
        curItem.setCheckedInt(curItem == item);
      }
    }
  }
Пример #11
0
  /** * Adds an item to the menu. The other add methods funnel to this. */
  private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
    final int ordering = getOrdering(categoryOrder);

    final MenuItemImpl item =
        new MenuItemImpl(this, group, id, categoryOrder, ordering, title, mDefaultShowAsAction);

    if (mCurrentMenuInfo != null) {
      // Pass along the current menu info
      item.setMenuInfo(mCurrentMenuInfo);
    }

    mItems.add(findInsertIndex(mItems, ordering), item);
    onItemsChanged(true);

    return item;
  }
Пример #12
0
  public void setGroupVisible(int group, boolean visible) {
    final int N = mItems.size();

    // We handle the notification of items being changed ourselves, so we use setVisibleInt rather
    // than setVisible and at the end notify of items being changed

    boolean changedAtLeastOneItem = false;
    for (int i = 0; i < N; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.getGroupId() == group) {
        if (item.setVisibleInt(visible)) changedAtLeastOneItem = true;
      }
    }

    if (changedAtLeastOneItem) onItemsChanged(true);
  }
Пример #13
0
  // 根据id查找菜单项,中间会有子菜单的递归
  public MenuItem findItem(int id) {
    final int size = size();
    for (int i = 0; i < size; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.getItemId() == id) {
        return item;
      } else if (item.hasSubMenu()) {
        MenuItem possibleItem = item.getSubMenu().findItem(id);

        if (possibleItem != null) {
          return possibleItem;
        }
      }
    }

    return null;
  }
Пример #14
0
  public int findGroupIndex(int group, int start) {
    final int size = size();

    if (start < 0) {
      start = 0;
    }

    for (int i = start; i < size; i++) {
      final MenuItemImpl item = mItems.get(i);

      if (item.getGroupId() == group) {
        return i;
      }
    }

    return -1;
  }
Пример #15
0
  ArrayList<MenuItemImpl> getVisibleItems() {
    if (!mIsVisibleItemsStale) return mVisibleItems;

    // Refresh the visible items
    mVisibleItems.clear();

    final int itemsSize = mItems.size();
    MenuItemImpl item;
    for (int i = 0; i < itemsSize; i++) {
      item = mItems.get(i);
      if (item.isVisible()) mVisibleItems.add(item);
    }

    mIsVisibleItemsStale = false;
    mIsActionItemsStale = true;

    return mVisibleItems;
  }
Пример #16
0
  public void setCheckable(boolean checkable) {

    if (!checkable && mRadioButton == null && mCheckBox == null) {
      return;
    }

    if (mRadioButton == null) {
      insertRadioButton();
    }
    if (mCheckBox == null) {
      insertCheckBox();
    }

    // Depending on whether its exclusive check or not, the checkbox or
    // radio button will be the one in use (and the other will be otherCompoundButton)
    final CompoundButton compoundButton;
    final CompoundButton otherCompoundButton;

    if (mItemData.isExclusiveCheckable()) {
      compoundButton = mRadioButton;
      otherCompoundButton = mCheckBox;
    } else {
      compoundButton = mCheckBox;
      otherCompoundButton = mRadioButton;
    }

    if (checkable) {
      compoundButton.setChecked(mItemData.isChecked());

      final int newVisibility = checkable ? VISIBLE : GONE;
      if (compoundButton.getVisibility() != newVisibility) {
        compoundButton.setVisibility(newVisibility);
      }

      // Make sure the other compound button isn't visible
      if (otherCompoundButton.getVisibility() != GONE) {
        otherCompoundButton.setVisibility(GONE);
      }
    } else {
      mCheckBox.setVisibility(GONE);
      mRadioButton.setVisibility(GONE);
    }
  }
Пример #17
0
  /**
   * This function will return all the menu and sub-menu items that can be directly (the shortcut
   * directly corresponds) and indirectly (the ALT-enabled char corresponds to the shortcut)
   * associated with the keyCode.
   */
  void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) {
    final boolean qwerty = isQwertyMode();
    final int metaState = event.getMetaState();
    final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
    // Get the chars associated with the keyCode (i.e using any chording combo)
    final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
    // The delete key is not mapped to '\b' so we treat it specially
    if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) {
      return;
    }

    // Look for an item whose shortcut is this key.
    final int N = mItems.size();
    for (int i = 0; i < N; i++) {
      MenuItemImpl item = mItems.get(i);
      if (item.hasSubMenu()) {
        ((MenuBuilder) item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
      }
      final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
      if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0)
          && (shortcutChar != 0)
          && (shortcutChar == possibleChars.meta[0]
              || shortcutChar == possibleChars.meta[2]
              || (qwerty && shortcutChar == '\b' && keyCode == KeyEvent.KEYCODE_DEL))
          && item.isEnabled()) {
        items.add(item);
      }
    }
  }
Пример #18
0
  public void initialize(MenuItemImpl itemData, int menuType) {
    mItemData = itemData;
    mMenuType = menuType;

    setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);

    setTitle(itemData.getTitleForItemView(this));
    setCheckable(itemData.isCheckable());
    setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
    setIcon(itemData.getIcon());
    setEnabled(itemData.isEnabled());
  }
Пример #19
0
  /**
   * * This method determines which menu items get to be 'action items' that will appear in an
   * action bar and which items should be 'overflow items' in a secondary menu. The rules are as
   * follows:
   *
   * <p>Items are considered for inclusion in the order specified within the menu. There is a limit
   * of mMaxActionItems as a total count, optionally including the overflow menu button itself. This
   * is a soft limit; if an item shares a group ID with an item previously included as an action
   * item, the new item will stay with its group and become an action item itself even if it breaks
   * the max item count limit. This is done to limit the conceptual complexity of the items
   * presented within an action bar. Only a few unrelated concepts should be presented to the user
   * in this space, and groups are treated as a single concept.
   *
   * <p>There is also a hard limit of consumed measurable space: mActionWidthLimit. This limit may
   * be broken by a single item that exceeds the remaining space, but no further items may be added.
   * If an item that is part of a group cannot fit within the remaining measured width, the entire
   * group will be demoted to overflow. This is done to ensure room for navigation and other
   * affordances in the action bar as well as reduce general UI clutter.
   *
   * <p>The space freed by demoting a full group cannot be consumed by future menu items. Once items
   * begin to overflow, all future items become overflow items as well. This is to avoid inadvertent
   * reordering that may break the app's intended design.
   */
  public void flagActionItems() {
    if (!mIsActionItemsStale) {
      return;
    }

    // Presenters flag action items as needed.
    boolean flagged = false;
    for (WeakReference<MenuPresenter> ref : mPresenters) {
      final MenuPresenter presenter = ref.get();
      if (presenter == null) {
        mPresenters.remove(ref);
      } else {
        flagged |= presenter.flagActionItems();
      }
    }

    if (flagged) {
      mActionItems.clear();
      mNonActionItems.clear();
      ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
      final int itemsSize = visibleItems.size();
      for (int i = 0; i < itemsSize; i++) {
        MenuItemImpl item = visibleItems.get(i);
        if (item.isActionButton()) {
          mActionItems.add(item);
        } else {
          mNonActionItems.add(item);
        }
      }
    } else {
      // Nobody flagged anything, everything is a non-action item.
      // (This happens during a first pass with no action-item presenters.)
      mActionItems.clear();
      mNonActionItems.clear();
      mNonActionItems.addAll(getVisibleItems());
    }
    mIsActionItemsStale = false;
  }
Пример #20
0
  public void setChecked(boolean checked) {
    CompoundButton compoundButton;

    if (mItemData.isExclusiveCheckable()) {
      if (mRadioButton == null) {
        insertRadioButton();
      }
      compoundButton = mRadioButton;
    } else {
      if (mCheckBox == null) {
        insertCheckBox();
      }
      compoundButton = mCheckBox;
    }

    compoundButton.setChecked(checked);
  }
Пример #21
0
  public void setIcon(Drawable icon) {
    final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
    if (!showIcon && !mPreserveIconSpacing) {
      return;
    }

    if (mIconView == null && icon == null && !mPreserveIconSpacing) {
      return;
    }

    if (mIconView == null) {
      insertIconView();
    }

    if (icon != null || mPreserveIconSpacing) {
      mIconView.setImageDrawable(showIcon ? icon : null);

      if (mIconView.getVisibility() != VISIBLE) {
        mIconView.setVisibility(VISIBLE);
      }
    } else {
      mIconView.setVisibility(GONE);
    }
  }