Java tutorial
/* * Copyright (C) 2011 The Android Open Source Project * * 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.internal.view.menu; import java.util.ArrayList; import java.util.List; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.MenuItem; import android.util.SparseBooleanArray; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import com.actionbarsherlock.R; /** * MenuPresenter for building action menus as seen in the action bar and action modes. */ public class ActionMenuPresenter extends BaseMenuPresenter { //UNUSED private static final String TAG = "ActionMenuPresenter"; private int mWidthLimit; private int mActionItemWidthLimit; private int mMaxItems; private boolean mMaxItemsSet; private boolean mStrictWidthLimit; private boolean mWidthLimitSet; private int mMinCellSize; private AlertDialog mDialog; // Group IDs that have been added as actions - used temporarily, allocated here for reuse. private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray(); private View mScrapActionButtonView; int mOpenSubMenuId; public ActionMenuPresenter(Context context) { super(context, R.layout.abs__action_menu_layout, R.layout.abs__action_menu_item_layout); } @Override public void initForMenu(Context context, MenuBuilder menu) { super.initForMenu(context, menu); final Resources res = context.getResources(); if (!mWidthLimitSet) { mWidthLimit = res.getDisplayMetrics().widthPixels / 2; } // Measure for initial configuration if (!mMaxItemsSet) { mMaxItems = res.getInteger(R.integer.abs__max_action_buttons); } mActionItemWidthLimit = mWidthLimit; mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density); // Drop a scrap view as it may no longer reflect the proper context/config. mScrapActionButtonView = null; } public void onConfigurationChanged(Configuration newConfig) { if (!mMaxItemsSet) { mMaxItems = mContext.getResources().getInteger(R.integer.abs__max_action_buttons); if (mMenu != null) { mMenu.onItemsChanged(true); } } } public void setWidthLimit(int width, boolean strict) { mWidthLimit = width; mStrictWidthLimit = strict; mWidthLimitSet = true; } public void setItemLimit(int itemCount) { mMaxItems = itemCount; mMaxItemsSet = true; } @Override public MenuView getMenuView(ViewGroup root) { MenuView result = super.getMenuView(root); ((ActionMenuView) result).setPresenter(this); return result; } @Override public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) { View actionView = item.getActionView(); if (actionView == null) { if (!(convertView instanceof ActionMenuItemView)) { convertView = null; } actionView = super.getItemView(item, convertView, parent); } actionView.setVisibility(View.VISIBLE); final ActionMenuView menuParent = (ActionMenuView) parent; final ViewGroup.LayoutParams lp = actionView.getLayoutParams(); if (!menuParent.checkLayoutParams(lp)) { actionView.setLayoutParams(menuParent.generateLayoutParams(lp)); } return actionView; } @Override public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) { itemView.initialize(item, 0); final ActionMenuView menuView = (ActionMenuView) mMenuView; ActionMenuItemView actionItemView = (ActionMenuItemView) itemView; actionItemView.setItemInvoker(menuView); } @Override public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) { return item.isActionButton(); } @Override public boolean filterLeftoverView(ViewGroup parent, int childIndex) { return super.filterLeftoverView(parent, childIndex); } public boolean onSubMenuSelected(SubMenuBuilder subMenu) { if (!subMenu.hasVisibleItems()) return false; SubMenuBuilder topSubMenu = subMenu; while (topSubMenu.getParentMenu() != mMenu) { topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu(); } View anchor = findViewForItem(topSubMenu.getItem()); if (anchor == null) { return false; } mOpenSubMenuId = subMenu.getItem().getItemId(); final List<MenuItemImpl> items = subMenu.getVisibleItems(); final int itemsSize = items.size(); final CharSequence[] itemText = new CharSequence[itemsSize]; for (int i = 0; i < itemsSize; i++) { itemText[i] = items.get(i).getTitle(); } AlertDialog.Builder builder = new AlertDialog.Builder(mContext).setTitle(subMenu.getItem().getTitle()) .setIcon(subMenu.getItem().getIcon()).setCancelable(true) .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { mDialog = null; } }); final boolean isExclusive = ((MenuItemImpl) subMenu.getItem(0)).isExclusiveCheckable(); final boolean isCheckable = ((MenuItemImpl) subMenu.getItem(0)).isCheckable(); if (isExclusive) { int selected = -1; for (int i = 0; i < itemsSize; i++) { if (items.get(i).isChecked()) { selected = i; break; } } builder.setSingleChoiceItems(itemText, selected, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { items.get(which).invoke(); //dialog.dismiss(); mDialog = null; } }); } else if (isCheckable) { boolean[] selected = new boolean[itemsSize]; for (int i = 0; i < itemsSize; i++) { selected[i] = items.get(i).isChecked(); } builder.setMultiChoiceItems(itemText, selected, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { items.get(which).setChecked(isChecked); //dialog.dismiss(); mDialog = null; } }); } else { builder.setItems(itemText, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { items.get(which).invoke(); //dialog.dismiss(); mDialog = null; } }); } mDialog = builder.show(); super.onSubMenuSelected(subMenu); return true; } private View findViewForItem(MenuItem item) { final ViewGroup parent = (ViewGroup) mMenuView; if (parent == null) return null; final int count = parent.getChildCount(); for (int i = 0; i < count; i++) { final View child = parent.getChildAt(i); if (child instanceof MenuView.ItemView && ((MenuView.ItemView) child).getItemData() == item) { return child; } } return null; } /** * Dismiss all popup menus - overflow and submenus. * @return true if popups were dismissed, false otherwise. (This can be because none were open.) */ public boolean dismissPopupMenus() { return hideSubMenus(); } /** * Dismiss all submenu popups. * * @return true if popups were dismissed, false otherwise. (This can be because none were open.) */ public boolean hideSubMenus() { if (mDialog != null) { try { mDialog.dismiss(); } catch (Exception e) { //Must have been dismissed or cancelled already } mDialog = null; return true; } return false; } public boolean flagActionItems() { final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems(); final int itemsSize = visibleItems.size(); int maxActions = mMaxItems; int widthLimit = mActionItemWidthLimit; final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final ViewGroup parent = (ViewGroup) mMenuView; int requiredItems = 0; //int requestedItems = 0; int firstActionWidth = 0; for (int i = 0; i < itemsSize; i++) { MenuItemImpl item = visibleItems.get(i); if (item.requiresActionButton()) { requiredItems++; } /* else if (item.requestsActionButton()) { requestedItems++; }*/ } maxActions -= requiredItems; final SparseBooleanArray seenGroups = mActionButtonGroups; seenGroups.clear(); int cellSize = 0; int cellsRemaining = 0; if (mStrictWidthLimit) { cellsRemaining = widthLimit / mMinCellSize; final int cellSizeRemaining = widthLimit % mMinCellSize; cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining; } // Flag as many more requested items as will fit. for (int i = 0; i < itemsSize; i++) { MenuItemImpl item = visibleItems.get(i); if (item.requiresActionButton()) { View v = getItemView(item, mScrapActionButtonView, parent); if (mScrapActionButtonView == null) { mScrapActionButtonView = v; } if (mStrictWidthLimit) { cellsRemaining -= ActionMenuView.measureChildForCells(v, cellSize, cellsRemaining, querySpec, 0); } else { v.measure(querySpec, querySpec); } final int measuredWidth = v.getMeasuredWidth(); widthLimit -= measuredWidth; if (firstActionWidth == 0) { firstActionWidth = measuredWidth; } final int groupId = item.getGroupId(); if (groupId != 0) { seenGroups.put(groupId, true); } item.setIsActionButton(true); } else if (item.requestsActionButton()) { // Items in a group with other items that already have an action slot // can break the max actions rule, but not the width limit. final int groupId = item.getGroupId(); final boolean inGroup = seenGroups.get(groupId); boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 && (!mStrictWidthLimit || cellsRemaining > 0); if (isAction) { View v = getItemView(item, mScrapActionButtonView, parent); if (mScrapActionButtonView == null) { mScrapActionButtonView = v; } if (mStrictWidthLimit) { final int cells = ActionMenuView.measureChildForCells(v, cellSize, cellsRemaining, querySpec, 0); cellsRemaining -= cells; if (cells == 0) { isAction = false; } } else { v.measure(querySpec, querySpec); } final int measuredWidth = v.getMeasuredWidth(); widthLimit -= measuredWidth; if (firstActionWidth == 0) { firstActionWidth = measuredWidth; } if (mStrictWidthLimit) { isAction &= widthLimit >= 0; } else { // Did this push the entire first item past the limit? isAction &= widthLimit + firstActionWidth > 0; } } if (isAction && groupId != 0) { seenGroups.put(groupId, true); } else if (inGroup) { // We broke the width limit. Demote the whole group, they all overflow now. seenGroups.put(groupId, false); for (int j = 0; j < i; j++) { MenuItemImpl areYouMyGroupie = visibleItems.get(j); if (areYouMyGroupie.getGroupId() == groupId) { // Give back the action slot if (areYouMyGroupie.isActionButton()) maxActions++; areYouMyGroupie.setIsActionButton(false); } } } if (isAction) maxActions--; item.setIsActionButton(isAction); } } return true; } @Override public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { dismissPopupMenus(); super.onCloseMenu(menu, allMenusAreClosing); } @Override public Parcelable onSaveInstanceState() { SavedState state = new SavedState(); state.openSubMenuId = mOpenSubMenuId; return state; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState saved = (SavedState) state; if (saved.openSubMenuId > 0) { MenuItem item = mMenu.findItem(saved.openSubMenuId); if (item != null) { SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu(); onSubMenuSelected(subMenu); } } } private static class SavedState implements Parcelable { public int openSubMenuId; SavedState() { } SavedState(Parcel in) { openSubMenuId = in.readInt(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(openSubMenuId); } @SuppressWarnings("unused") public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }