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 lewa.support.v7.internal.view.menu; import android.content.Context; import android.content.DialogInterface; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import lewa.support.v7.appcompat.R; import lewa.support.v7.internal.view.ActionBarPolicy; ///LEWA BEGIN import lewa.support.v7.lewa.v5.LewaActionMenuPresenter; ///LEWA END import android.support.v4.view.ActionProvider; import android.util.Log; import android.util.SparseBooleanArray; import android.view.MenuItem; import android.view.SoundEffectConstants; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; import android.widget.ImageButton; import java.util.ArrayList; /** * MenuPresenter for building action menus as seen in the action bar and action modes. * * @hide */ public class ActionMenuPresenter extends BaseMenuPresenter implements ActionProvider.SubUiVisibilityListener { private static final String TAG = "ActionMenuPresenter"; private View mOverflowButton; private boolean mReserveOverflow; private boolean mReserveOverflowSet; private int mWidthLimit; private int mActionItemWidthLimit; private int mMaxItems; private boolean mMaxItemsSet; private boolean mStrictWidthLimit; private boolean mWidthLimitSet; private boolean mExpandedActionViewsExclusive; private int mMinCellSize; // Group IDs that have been added as actions - used temporarily, allocated here for reuse. private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray(); private View mScrapActionButtonView; private OverflowPopup mOverflowPopup; private ActionButtonSubmenu mActionButtonPopup; private OpenOverflowRunnable mPostedOpenRunnable; final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback(); int mOpenSubMenuId; // LEWA ADD BEGIN private boolean mIsActionModeStyle; // LEWA ADD END public ActionMenuPresenter(Context context) { super(context, R.layout.abc_action_menu_layout, R.layout.abc_action_menu_item_layout); // LEWA ADD BEGIN Injector.initLewaStyle(this, context); // LEWA ADD END } @Override public void initForMenu(Context context, MenuBuilder menu) { super.initForMenu(context, menu); final Resources res = context.getResources(); final ActionBarPolicy abp = ActionBarPolicy.get(context); if (!mReserveOverflowSet) { mReserveOverflow = abp.showsOverflowMenuButton(); } if (!mWidthLimitSet) { mWidthLimit = abp.getEmbeddedMenuWidthLimit(); } // Measure for initial configuration if (!mMaxItemsSet) { mMaxItems = abp.getMaxActionButtons(); } int width = mWidthLimit; if (mReserveOverflow) { if (mOverflowButton == null) { mOverflowButton = new OverflowMenuButton(mSystemContext); final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); mOverflowButton.measure(spec, spec); } width -= mOverflowButton.getMeasuredWidth(); } else { mOverflowButton = null; } mActionItemWidthLimit = width; 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.abc_max_action_buttons); } if (mMenu != null) { mMenu.onItemsChanged(true); } } public void setWidthLimit(int width, boolean strict) { mWidthLimit = width; mStrictWidthLimit = strict; mWidthLimitSet = true; } public void setReserveOverflow(boolean reserveOverflow) { mReserveOverflow = reserveOverflow; mReserveOverflowSet = true; } public void setItemLimit(int itemCount) { mMaxItems = itemCount; mMaxItemsSet = true; } public void setExpandedActionViewsExclusive(boolean isExclusive) { mExpandedActionViewsExclusive = isExclusive; } @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 || item.hasCollapsibleActionView()) { if (!(convertView instanceof ActionMenuItemView)) { convertView = null; } actionView = super.getItemView(item, convertView, parent); } actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : 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) { // LEWA MODIFY BEGIN Injector.initActionMenuItem(this, item, itemView); //itemView.initialize(item, 0); // LEWA MODIFY END final ActionMenuView menuView = (ActionMenuView) mMenuView; ActionMenuItemView actionItemView = (ActionMenuItemView) itemView; actionItemView.setItemInvoker(menuView); actionItemView.requestLayout(); } @Override public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) { return item.isActionButton(); } @Override public void updateMenuView(boolean cleared) { super.updateMenuView(cleared); if (mMenuView == null) { return; } if (mMenu != null) { final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems(); final int count = actionItems.size(); for (int i = 0; i < count; i++) { final ActionProvider provider = actionItems.get(i).getSupportActionProvider(); if (provider != null) { provider.setSubUiVisibilityListener(this); } } } final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ? mMenu.getNonActionItems() : null; boolean hasOverflow = false; if (mReserveOverflow && nonActionItems != null) { final int count = nonActionItems.size(); if (count == 1) { hasOverflow = !nonActionItems.get(0).isActionViewExpanded(); } else { hasOverflow = count > 0; } } if (hasOverflow) { if (mOverflowButton == null) { mOverflowButton = new OverflowMenuButton(mSystemContext); } ViewGroup parent = (ViewGroup) mOverflowButton.getParent(); if (parent != mMenuView) { if (parent != null) { parent.removeView(mOverflowButton); } ActionMenuView menuView = (ActionMenuView) mMenuView; menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams()); } } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) { ((ViewGroup) mMenuView).removeView(mOverflowButton); } ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow); } @Override public boolean filterLeftoverView(ViewGroup parent, int childIndex) { if (parent.getChildAt(childIndex) == mOverflowButton) { return false; } 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) { if (mOverflowButton == null) { return false; } anchor = mOverflowButton; } mOpenSubMenuId = subMenu.getItem().getItemId(); mActionButtonPopup = new ActionButtonSubmenu(subMenu); mActionButtonPopup.show(null); 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; } /** * Display the overflow menu if one is present. * * @return true if the overflow menu was shown, false otherwise. */ public boolean showOverflowMenu() { if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null && mPostedOpenRunnable == null) { OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true); mPostedOpenRunnable = new OpenOverflowRunnable(popup); // Post this for later; we might still need a layout for the anchor to be right. ((View) mMenuView).post(mPostedOpenRunnable); // ActionMenuPresenter uses null as a callback argument here // to indicate overflow is opening. super.onSubMenuSelected(null); return true; } return false; } /** * Hide the overflow menu if it is currently showing. * * @return true if the overflow menu was hidden, false otherwise. */ public boolean hideOverflowMenu() { if (mPostedOpenRunnable != null && mMenuView != null) { ((View) mMenuView).removeCallbacks(mPostedOpenRunnable); mPostedOpenRunnable = null; return true; } MenuPopupHelper popup = mOverflowPopup; if (popup != null) { popup.dismiss(); return true; } return false; } /** * 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() { boolean result = hideOverflowMenu(); result |= hideSubMenus(); return result; } /** * Dismiss all submenu popups. * * @return true if popups were dismissed, false otherwise. (This can be because none were open.) */ public boolean hideSubMenus() { if (mActionButtonPopup != null) { mActionButtonPopup.dismiss(); return true; } return false; } /** * @return true if the overflow menu is currently showing */ public boolean isOverflowMenuShowing() { return mOverflowPopup != null && mOverflowPopup.isShowing(); } /** * @return true if space has been reserved in the action menu for an overflow item. */ public boolean isOverflowReserved() { return mReserveOverflow; } 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; boolean hasOverflow = false; for (int i = 0; i < itemsSize; i++) { MenuItemImpl item = visibleItems.get(i); if (item.requiresActionButton()) { requiredItems++; } else if (item.requestsActionButton()) { requestedItems++; } else { hasOverflow = true; } if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) { // Overflow everything if we have an expanded action view and we're // space constrained. maxActions = 0; } } // Reserve a spot for the overflow item if needed. if (mReserveOverflow && (hasOverflow || requiredItems + requestedItems > maxActions)) { maxActions--; } 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); } else { // Neither requires nor requests an action button. item.setIsActionButton(false); } } 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); } } } @Override public void onSubUiVisibilityChanged(boolean isVisible) { if (isVisible) { // Not a submenu, but treat it like one. super.onSubMenuSelected(null); } else { mMenu.close(false); } } 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); } 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]; } }; } private class OverflowMenuButton extends ImageButton implements ActionMenuView.ActionMenuChildView, View.OnClickListener { public OverflowMenuButton(Context context) { super(context, null, R.attr.actionOverflowButtonStyle); // <item name="android:src">@drawable/abc_ic_menu_moreoverflow_mtrl_alpha</item> // <item name="android:background">?attr/actionBarItemBackground</item> // <item name="android:contentDescription">@string/abc_action_menu_overflow_description</item> // <item name="android:minWidth">@dimen/abc_action_button_min_width_overflow_material</item> // <item name="android:minHeight">@dimen/abc_action_button_min_height_material</item> // this.setBackgroundResource(R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha); Drawable colorfulIcon = null; // colorfulIcon = icon;//ColorUtils.resetDrawableColor(itemView.mContext, icon, color); // colorfulIcon = ColorUtils.resetDrawableColor(mContext, this.getResources().getDrawable(R.drawable.ic_menu_moreoverflow_normal_holo_light), 0xffc9c9c9); colorfulIcon = this.getResources().getDrawable(R.drawable.ic_menu_moreoverflow_normal_holo_light); this.setImageDrawable(colorfulIcon); // this.setBackgroundResource(resid); this.setContentDescription(context.getString(R.string.abc_action_menu_overflow_description)); // this.setMinimumWidth(context.getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material)); // this.setMinimumHeight(context.getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_height_material)); // this.setPadding(0, 100, 12, 100); this.setScaleType(ScaleType.CENTER); // LEWA MODIFY BEGIN Injector.initOverflowMenuButton(context, this); this.setOnClickListener(this); /* setClickable(true); setFocusable(true); setVisibility(VISIBLE); setEnabled(true); setOnTouchListener(new ListPopupWindow.ForwardingListener(this) { @Override public ListPopupWindow getPopup() { if (mOverflowPopup == null) { return null; } return mOverflowPopup.getPopup(); } @Override public boolean onForwardingStarted() { showOverflowMenu(); return true; } @Override public boolean onForwardingStopped() { // Displaying the popup occurs asynchronously, so wait for // the runnable to finish before deciding whether to stop // forwarding. if (mPostedOpenRunnable != null) { return false; } hideOverflowMenu(); return true; } }); */ // LEWA MODIFY END } // @Override // public boolean performClick() { // Log.d("zhoulikai", "===performClick==="); // if (super.performClick()) { // return true; // } // // playSoundEffect(SoundEffectConstants.CLICK); // //showOverflowMenu(); // // LEWA MODIFY BEGIN // Injector.invokePerformClick(ActionMenuPresenter.this); // // LEWA MODIFY END // return true; // } public boolean needsDividerBefore() { return false; } public boolean needsDividerAfter() { return false; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { // Fill available height heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public void onClick(View v) { // TODO Auto-generated method stub // if (super.performClick()) { // return true; // } //playSoundEffect(SoundEffectConstants.CLICK); //showOverflowMenu(); // LEWA MODIFY BEGIN Injector.invokePerformClick(ActionMenuPresenter.this); // LEWA MODIFY END } } private class OverflowPopup extends MenuPopupHelper { public OverflowPopup(Context context, MenuBuilder menu, View anchorView, boolean overflowOnly) { super(context, menu, anchorView, overflowOnly); setCallback(mPopupPresenterCallback); } @Override public void onDismiss() { super.onDismiss(); mMenu.close(); mOverflowPopup = null; } } private class ActionButtonSubmenu extends MenuDialogHelper { public ActionButtonSubmenu(SubMenuBuilder subMenu) { super(subMenu); setCallback(mPopupPresenterCallback); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); mActionButtonPopup = null; mOpenSubMenuId = 0; } } private class PopupPresenterCallback implements MenuPresenter.Callback { @Override public boolean onOpenSubMenu(MenuBuilder subMenu) { if (subMenu == null) { return false; } mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId(); return false; } @Override public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { if (menu instanceof SubMenuBuilder) { ((SubMenuBuilder) menu).getRootMenu().close(false); } } } private class OpenOverflowRunnable implements Runnable { private OverflowPopup mPopup; public OpenOverflowRunnable(OverflowPopup popup) { mPopup = popup; } public void run() { mMenu.changeMenuMode(); final View menuView = (View) mMenuView; if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) { mOverflowPopup = mPopup; } mPostedOpenRunnable = null; } } ///LEWA ADD BEGIN public ActionMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) { super(context, menuLayoutRes, itemLayoutRes); } /** @hide */ public interface OnPerformClickListener { void onPerformClick(); } private OnPerformClickListener mPerformClickListener; public void setOnPerformClickListener(OnPerformClickListener listener) { mPerformClickListener = listener; } //@LewaHook(LewaHook.LewaHookType.NEW_CLASS) static class Injector { static void invokePerformClick(ActionMenuPresenter presenter) { // Handle event of action bar click if (true) { if (presenter.mPerformClickListener != null) { presenter.mPerformClickListener.onPerformClick(); } else { presenter.showOverflowMenu(); } } else { presenter.showOverflowMenu(); } } static void initOverflowMenuButton(Context context, OverflowMenuButton overflowMenuButton) { // Disabled event of over flow button // if (LewaUiUtil.isV5Ui(context)) { // overflowMenuButton.setVisibility(OverflowMenuButton.VISIBLE); // } else { overflowMenuButton.setClickable(true); overflowMenuButton.setFocusable(true); overflowMenuButton.setVisibility(OverflowMenuButton.VISIBLE); overflowMenuButton.setEnabled(true); // } } static void initActionMenuItem(ActionMenuPresenter oThis, MenuItemImpl item, MenuView.ItemView itemView) { if (true) { if (oThis instanceof LewaActionMenuPresenter) { LewaActionMenuPresenter menuPresenter = (LewaActionMenuPresenter) oThis; if (menuPresenter.isActionMode() || oThis.mIsActionModeStyle) { if ((itemView instanceof ActionMenuItemView)) { ((ActionMenuItemView) itemView).initialize(item, 0, ActionMenuItemView.ACTION_MODE_ITEM_COLOR); } } else { itemView.initialize(item, 0); } } else { itemView.initialize(item, 0); } } else { itemView.initialize(item, 0); } } static void initLewaStyle(ActionMenuPresenter oThis, Context context) { if (true) { android.util.TypedValue typedValue = new android.util.TypedValue(); if (context.getTheme().resolveAttribute(R.attr.lewa_action_mode_style, typedValue, true)) { if (typedValue.type == android.util.TypedValue.TYPE_INT_BOOLEAN) { oThis.mIsActionModeStyle = typedValue.data != 0; } } } else { } } } ///LEWA ADD END }