ActionMenuView.java revision cf0357639e952a87f0d535c82691919af81f058b
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.internal.view.menu; 17 18import android.content.Context; 19import android.content.res.Configuration; 20import android.content.res.Resources; 21import android.content.res.TypedArray; 22import android.graphics.drawable.Drawable; 23import android.util.AttributeSet; 24import android.view.Gravity; 25import android.view.View; 26import android.view.ViewGroup; 27import android.view.ViewParent; 28import android.widget.ImageButton; 29import android.widget.ImageView; 30import android.widget.LinearLayout; 31 32import java.util.ArrayList; 33 34/** 35 * @hide 36 */ 37public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView { 38 private static final String TAG = "ActionMenuView"; 39 40 // TODO Theme/style this. 41 private static final int DIVIDER_PADDING = 12; // dips 42 43 private MenuBuilder mMenu; 44 45 private int mMaxItems; 46 private boolean mReserveOverflow; 47 private OverflowMenuButton mOverflowButton; 48 private MenuPopupHelper mOverflowPopup; 49 50 private float mButtonPaddingLeft; 51 private float mButtonPaddingRight; 52 private float mDividerPadding; 53 54 private Drawable mDivider; 55 56 private Runnable mShowOverflow = new Runnable() { 57 public void run() { 58 showOverflowMenu(); 59 } 60 }; 61 62 private class OpenOverflowRunnable implements Runnable { 63 private MenuPopupHelper mPopup; 64 65 public OpenOverflowRunnable(MenuPopupHelper popup) { 66 mPopup = popup; 67 } 68 69 public void run() { 70 mOverflowPopup = mPopup; 71 mPopup.show(); 72 mPostedOpenRunnable = null; 73 } 74 } 75 76 private OpenOverflowRunnable mPostedOpenRunnable; 77 78 public ActionMenuView(Context context) { 79 this(context, null); 80 } 81 82 public ActionMenuView(Context context, AttributeSet attrs) { 83 super(context, attrs); 84 85 final Resources res = getResources(); 86 87 // Measure for initial configuration 88 mMaxItems = getMaxActionButtons(); 89 90 // TODO There has to be a better way to indicate that we don't have a hard menu key. 91 final int screen = res.getConfiguration().screenLayout; 92 mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) == 93 Configuration.SCREENLAYOUT_SIZE_XLARGE; 94 95 TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme); 96 final int buttonStyle = a.getResourceId( 97 com.android.internal.R.styleable.Theme_actionButtonStyle, 0); 98 mDivider = a.getDrawable(com.android.internal.R.styleable.Theme_dividerVertical); 99 a.recycle(); 100 101 a = context.obtainStyledAttributes(buttonStyle, com.android.internal.R.styleable.View); 102 mButtonPaddingLeft = a.getDimension(com.android.internal.R.styleable.View_paddingLeft, 0); 103 mButtonPaddingRight = a.getDimension(com.android.internal.R.styleable.View_paddingRight, 0); 104 a.recycle(); 105 106 mDividerPadding = DIVIDER_PADDING * res.getDisplayMetrics().density; 107 108 setBaselineAligned(false); 109 } 110 111 @Override 112 public void onConfigurationChanged(Configuration newConfig) { 113 super.onConfigurationChanged(newConfig); 114 final int screen = newConfig.screenLayout; 115 mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) == 116 Configuration.SCREENLAYOUT_SIZE_XLARGE; 117 mMaxItems = getMaxActionButtons(); 118 if (mMenu != null) { 119 mMenu.setMaxActionItems(mMaxItems); 120 updateChildren(false); 121 } 122 123 if (mOverflowPopup != null && mOverflowPopup.isShowing()) { 124 mOverflowPopup.dismiss(); 125 post(mShowOverflow); 126 } 127 } 128 129 @Override 130 public void onDetachedFromWindow() { 131 super.onDetachedFromWindow(); 132 if (mOverflowPopup != null && mOverflowPopup.isShowing()) { 133 mOverflowPopup.dismiss(); 134 } 135 } 136 137 private int getMaxActionButtons() { 138 return getResources().getInteger(com.android.internal.R.integer.max_action_buttons); 139 } 140 141 public boolean isOverflowReserved() { 142 return mReserveOverflow; 143 } 144 145 public void setOverflowReserved(boolean reserveOverflow) { 146 mReserveOverflow = reserveOverflow; 147 } 148 149 @Override 150 protected LayoutParams generateDefaultLayoutParams() { 151 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 152 LayoutParams.WRAP_CONTENT); 153 params.gravity = Gravity.CENTER_VERTICAL; 154 return params; 155 } 156 157 @Override 158 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 159 return generateDefaultLayoutParams(); 160 } 161 162 public boolean invokeItem(MenuItemImpl item) { 163 return mMenu.performItemAction(item, 0); 164 } 165 166 public int getWindowAnimations() { 167 return 0; 168 } 169 170 public void initialize(MenuBuilder menu, int menuType) { 171 menu.setMaxActionItems(mMaxItems); 172 mMenu = menu; 173 updateChildren(true); 174 } 175 176 public void updateChildren(boolean cleared) { 177 final boolean reserveOverflow = mReserveOverflow; 178 removeAllViews(); 179 180 final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(reserveOverflow); 181 final int itemCount = itemsToShow.size(); 182 183 boolean needsDivider = false; 184 for (int i = 0; i < itemCount; i++) { 185 if (needsDivider) { 186 addView(makeDividerView(), makeDividerLayoutParams()); 187 } 188 final MenuItemImpl itemData = itemsToShow.get(i); 189 final View actionView = itemData.getActionView(); 190 if (actionView != null) { 191 final ViewParent parent = actionView.getParent(); 192 if (parent instanceof ViewGroup) { 193 ((ViewGroup) parent).removeView(actionView); 194 } 195 addView(actionView, makeActionViewLayoutParams()); 196 } else { 197 needsDivider = addItemView(i == 0 || !needsDivider, 198 (ActionMenuItemView) itemData.getItemView( 199 MenuBuilder.TYPE_ACTION_BUTTON, this)); 200 } 201 } 202 203 if (reserveOverflow) { 204 if (mMenu.getNonActionItems(true).size() > 0) { 205 OverflowMenuButton button = new OverflowMenuButton(mContext); 206 addView(button); 207 mOverflowButton = button; 208 } else { 209 mOverflowButton = null; 210 } 211 } 212 } 213 214 public boolean showOverflowMenu() { 215 if (mOverflowButton != null && !isOverflowMenuShowing()) { 216 mMenu.getCallback().onMenuModeChange(mMenu); 217 return true; 218 } 219 return false; 220 } 221 222 public void openOverflowMenu() { 223 OverflowPopup popup = new OverflowPopup(getContext(), mMenu, mOverflowButton, true); 224 mPostedOpenRunnable = new OpenOverflowRunnable(popup); 225 // Post this for later; we might still need a layout for the anchor to be right. 226 post(mPostedOpenRunnable); 227 } 228 229 public boolean isOverflowMenuShowing() { 230 return mOverflowPopup != null && mOverflowPopup.isShowing(); 231 } 232 233 public boolean isOverflowMenuOpen() { 234 return mOverflowPopup != null; 235 } 236 237 public boolean hideOverflowMenu() { 238 if (mPostedOpenRunnable != null) { 239 removeCallbacks(mPostedOpenRunnable); 240 return true; 241 } 242 243 MenuPopupHelper popup = mOverflowPopup; 244 if (popup != null) { 245 popup.dismiss(); 246 return true; 247 } 248 return false; 249 } 250 251 private boolean addItemView(boolean needsDivider, ActionMenuItemView view) { 252 view.setItemInvoker(this); 253 boolean hasText = view.hasText(); 254 255 if (hasText && needsDivider) { 256 addView(makeDividerView(), makeDividerLayoutParams()); 257 } 258 addView(view); 259 return hasText; 260 } 261 262 private ImageView makeDividerView() { 263 ImageView result = new ImageView(mContext); 264 result.setImageDrawable(mDivider); 265 result.setScaleType(ImageView.ScaleType.FIT_XY); 266 return result; 267 } 268 269 private LayoutParams makeDividerLayoutParams() { 270 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 271 LayoutParams.MATCH_PARENT); 272 params.topMargin = (int) mDividerPadding; 273 params.bottomMargin = (int) mDividerPadding; 274 return params; 275 } 276 277 private LayoutParams makeActionViewLayoutParams() { 278 LayoutParams params = generateDefaultLayoutParams(); 279 params.leftMargin = (int) mButtonPaddingLeft; 280 params.rightMargin = (int) mButtonPaddingRight; 281 return params; 282 } 283 284 private class OverflowMenuButton extends ImageButton { 285 public OverflowMenuButton(Context context) { 286 super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle); 287 288 setClickable(true); 289 setFocusable(true); 290 setVisibility(VISIBLE); 291 setEnabled(true); 292 } 293 294 @Override 295 public boolean performClick() { 296 if (super.performClick()) { 297 return true; 298 } 299 300 showOverflowMenu(); 301 return true; 302 } 303 } 304 305 private class OverflowPopup extends MenuPopupHelper { 306 public OverflowPopup(Context context, MenuBuilder menu, View anchorView, 307 boolean overflowOnly) { 308 super(context, menu, anchorView, overflowOnly); 309 } 310 311 @Override 312 public void onDismiss() { 313 super.onDismiss(); 314 mMenu.getCallback().onCloseMenu(mMenu, true); 315 mOverflowPopup = null; 316 } 317 } 318} 319