blob: 16a2031a5643b405c7d818f5776f48e182f8eb68 [file] [log] [blame]
Adam Powell96675b12010-06-10 18:58:59 -07001/*
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;
Adam Powell8028dd32010-07-15 10:16:33 -070019import android.content.res.Configuration;
Adam Powell367ee322012-05-06 18:32:33 -070020import android.content.res.TypedArray;
Adam Powell96675b12010-06-10 18:58:59 -070021import android.util.AttributeSet;
Adam Powell6b336f82010-08-10 20:13:01 -070022import android.view.Gravity;
Adam Powellcf78b3e2010-09-12 18:25:23 -070023import android.view.View;
Adam Powell640a66e2011-04-29 10:18:53 -070024import android.view.ViewDebug;
Adam Powell7ade1be2010-06-17 12:51:21 -070025import android.view.ViewGroup;
Adam Powell7bc3ca02011-08-26 18:29:58 -070026import android.view.accessibility.AccessibilityEvent;
Adam Powell96675b12010-06-10 18:58:59 -070027import android.widget.LinearLayout;
Adam Powellda971082013-10-03 18:21:58 -070028import com.android.internal.R;
Adam Powell96675b12010-06-10 18:58:59 -070029
Adam Powell96675b12010-06-10 18:58:59 -070030/**
31 * @hide
32 */
33public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView {
34 private static final String TAG = "ActionMenuView";
35
Adam Powell35aecd52011-07-01 13:43:49 -070036 static final int MIN_CELL_SIZE = 56; // dips
Adam Powellbe3c3292011-08-24 12:52:28 -070037 static final int GENERATED_ITEM_PADDING = 4; // dips
Adam Powell35aecd52011-07-01 13:43:49 -070038
Adam Powell96675b12010-06-10 18:58:59 -070039 private MenuBuilder mMenu;
Adam Powell7ade1be2010-06-17 12:51:21 -070040
Adam Powell8028dd32010-07-15 10:16:33 -070041 private boolean mReserveOverflow;
Adam Powell696cba52011-03-29 10:38:16 -070042 private ActionMenuPresenter mPresenter;
Adam Powell640a66e2011-04-29 10:18:53 -070043 private boolean mFormatItems;
Adam Powell89b09da2011-07-27 11:55:29 -070044 private int mFormatItemsWidth;
Adam Powell35aecd52011-07-01 13:43:49 -070045 private int mMinCellSize;
Adam Powellbe3c3292011-08-24 12:52:28 -070046 private int mGeneratedItemPadding;
Adam Powell35aecd52011-07-01 13:43:49 -070047 private int mMeasuredExtraWidth;
Adam Powell367ee322012-05-06 18:32:33 -070048 private int mMaxItemHeight;
Adam Powell8515ee82010-11-30 14:09:55 -080049
Adam Powell96675b12010-06-10 18:58:59 -070050 public ActionMenuView(Context context) {
51 this(context, null);
52 }
53
54 public ActionMenuView(Context context, AttributeSet attrs) {
55 super(context, attrs);
Adam Powellf16888f2010-10-11 17:05:29 -070056 setBaselineAligned(false);
Adam Powellbe3c3292011-08-24 12:52:28 -070057 final float density = context.getResources().getDisplayMetrics().density;
58 mMinCellSize = (int) (MIN_CELL_SIZE * density);
59 mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
Adam Powell367ee322012-05-06 18:32:33 -070060
61 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar,
62 R.attr.actionBarStyle, 0);
63 mMaxItemHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, 0);
64 a.recycle();
Adam Powell773b1b92010-08-20 15:45:24 -070065 }
66
Adam Powell696cba52011-03-29 10:38:16 -070067 public void setPresenter(ActionMenuPresenter presenter) {
68 mPresenter = presenter;
69 }
70
Adam Powell35aecd52011-07-01 13:43:49 -070071 public boolean isExpandedFormat() {
72 return mFormatItems;
73 }
74
Adam Powelld5c81db2012-08-02 14:35:26 -070075 public void setMaxItemHeight(int maxItemHeight) {
76 mMaxItemHeight = maxItemHeight;
77 requestLayout();
78 }
79
Adam Powell6c6f5752010-08-20 18:34:46 -070080 @Override
Adam Powell773b1b92010-08-20 15:45:24 -070081 public void onConfigurationChanged(Configuration newConfig) {
Adam Powell8515ee82010-11-30 14:09:55 -080082 super.onConfigurationChanged(newConfig);
Adam Powell696cba52011-03-29 10:38:16 -070083 mPresenter.updateMenuView(false);
Adam Powell6c6f5752010-08-20 18:34:46 -070084
Adam Powell696cba52011-03-29 10:38:16 -070085 if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
86 mPresenter.hideOverflowMenu();
87 mPresenter.showOverflowMenu();
Adam Powell6c6f5752010-08-20 18:34:46 -070088 }
Adam Powell773b1b92010-08-20 15:45:24 -070089 }
90
Adam Powell8515ee82010-11-30 14:09:55 -080091 @Override
Adam Powell640a66e2011-04-29 10:18:53 -070092 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Adam Powell35aecd52011-07-01 13:43:49 -070093 // If we've been given an exact size to match, apply special formatting during layout.
Adam Powell89b09da2011-07-27 11:55:29 -070094 final boolean wasFormatted = mFormatItems;
Adam Powell35aecd52011-07-01 13:43:49 -070095 mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
Adam Powell89b09da2011-07-27 11:55:29 -070096
97 if (wasFormatted != mFormatItems) {
98 mFormatItemsWidth = 0; // Reset this when switching modes
99 }
100
101 // Special formatting can change whether items can fit as action buttons.
102 // Kick the menu and update presenters when this changes.
Adam Powellda971082013-10-03 18:21:58 -0700103 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Adam Powell89b09da2011-07-27 11:55:29 -0700104 if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
105 mFormatItemsWidth = widthSize;
Adam Powell640a66e2011-04-29 10:18:53 -0700106 mMenu.onItemsChanged(true);
Adam Powell640a66e2011-04-29 10:18:53 -0700107 }
Adam Powell35aecd52011-07-01 13:43:49 -0700108
109 if (mFormatItems) {
110 onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
111 } else {
Adam Powell75d022a2012-03-06 12:04:07 -0800112 // Previous measurement at exact format may have set margins - reset them.
113 final int childCount = getChildCount();
114 for (int i = 0; i < childCount; i++) {
115 final View child = getChildAt(i);
116 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
117 lp.leftMargin = lp.rightMargin = 0;
118 }
Adam Powell35aecd52011-07-01 13:43:49 -0700119 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
120 }
121 }
122
123 private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
124 // We already know the width mode is EXACTLY if we're here.
125 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
126 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
127 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
128
129 final int widthPadding = getPaddingLeft() + getPaddingRight();
130 final int heightPadding = getPaddingTop() + getPaddingBottom();
131
Adam Powell367ee322012-05-06 18:32:33 -0700132 final int itemHeightSpec = heightMode == MeasureSpec.EXACTLY
133 ? MeasureSpec.makeMeasureSpec(heightSize - heightPadding, MeasureSpec.EXACTLY)
134 : MeasureSpec.makeMeasureSpec(
135 Math.min(mMaxItemHeight, heightSize - heightPadding), MeasureSpec.AT_MOST);
136
Adam Powell35aecd52011-07-01 13:43:49 -0700137 widthSize -= widthPadding;
138
139 // Divide the view into cells.
140 final int cellCount = widthSize / mMinCellSize;
141 final int cellSizeRemaining = widthSize % mMinCellSize;
Adam Powell3bb421d2011-08-16 15:04:53 -0700142
143 if (cellCount == 0) {
144 // Give up, nothing fits.
145 setMeasuredDimension(widthSize, 0);
146 return;
147 }
148
Adam Powell35aecd52011-07-01 13:43:49 -0700149 final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
150
151 int cellsRemaining = cellCount;
152 int maxChildHeight = 0;
153 int maxCellsUsed = 0;
Adam Powell160bb7f2011-07-07 10:22:27 -0700154 int expandableItemCount = 0;
Adam Powell14b7e2c2011-08-12 11:11:50 -0700155 int visibleItemCount = 0;
156 boolean hasOverflow = false;
Adam Powell35aecd52011-07-01 13:43:49 -0700157
Adam Powell14b7e2c2011-08-12 11:11:50 -0700158 // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
159 long smallestItemsAt = 0;
Adam Powell35aecd52011-07-01 13:43:49 -0700160
161 final int childCount = getChildCount();
162 for (int i = 0; i < childCount; i++) {
163 final View child = getChildAt(i);
Adam Powell14b7e2c2011-08-12 11:11:50 -0700164 if (child.getVisibility() == GONE) continue;
165
Adam Powellbe3c3292011-08-24 12:52:28 -0700166 final boolean isGeneratedItem = child instanceof ActionMenuItemView;
Adam Powell14b7e2c2011-08-12 11:11:50 -0700167 visibleItemCount++;
168
Adam Powellbe3c3292011-08-24 12:52:28 -0700169 if (isGeneratedItem) {
170 // Reset padding for generated menu item views; it may change below
171 // and views are recycled.
172 child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
173 }
174
Adam Powell35aecd52011-07-01 13:43:49 -0700175 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
176 lp.expanded = false;
177 lp.extraPixels = 0;
178 lp.cellsUsed = 0;
Adam Powell160bb7f2011-07-07 10:22:27 -0700179 lp.expandable = false;
Adam Powell14b7e2c2011-08-12 11:11:50 -0700180 lp.leftMargin = 0;
181 lp.rightMargin = 0;
Adam Powellbe3c3292011-08-24 12:52:28 -0700182 lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
Adam Powell35aecd52011-07-01 13:43:49 -0700183
184 // Overflow always gets 1 cell. No more, no less.
185 final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
186
187 final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
Adam Powell367ee322012-05-06 18:32:33 -0700188 itemHeightSpec, heightPadding);
Adam Powell35aecd52011-07-01 13:43:49 -0700189
190 maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
Adam Powell160bb7f2011-07-07 10:22:27 -0700191 if (lp.expandable) expandableItemCount++;
Adam Powell14b7e2c2011-08-12 11:11:50 -0700192 if (lp.isOverflowButton) hasOverflow = true;
Adam Powell35aecd52011-07-01 13:43:49 -0700193
194 cellsRemaining -= cellsUsed;
195 maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
Adam Powell14b7e2c2011-08-12 11:11:50 -0700196 if (cellsUsed == 1) smallestItemsAt |= (1 << i);
Adam Powell35aecd52011-07-01 13:43:49 -0700197 }
198
Adam Powellbe3c3292011-08-24 12:52:28 -0700199 // When we have overflow and a single expanded (text) item, we want to try centering it
200 // visually in the available space even though overflow consumes some of it.
201 final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
202
Adam Powell35aecd52011-07-01 13:43:49 -0700203 // Divide space for remaining cells if we have items that can expand.
204 // Try distributing whole leftover cells to smaller items first.
205
206 boolean needsExpansion = false;
Adam Powell160bb7f2011-07-07 10:22:27 -0700207 while (expandableItemCount > 0 && cellsRemaining > 0) {
Adam Powell35aecd52011-07-01 13:43:49 -0700208 int minCells = Integer.MAX_VALUE;
209 long minCellsAt = 0; // Bit locations are indices of relevant child views
210 int minCellsItemCount = 0;
211 for (int i = 0; i < childCount; i++) {
212 final View child = getChildAt(i);
213 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
214
215 // Don't try to expand items that shouldn't.
Adam Powell160bb7f2011-07-07 10:22:27 -0700216 if (!lp.expandable) continue;
Adam Powell35aecd52011-07-01 13:43:49 -0700217
218 // Mark indices of children that can receive an extra cell.
219 if (lp.cellsUsed < minCells) {
220 minCells = lp.cellsUsed;
221 minCellsAt = 1 << i;
222 minCellsItemCount = 1;
223 } else if (lp.cellsUsed == minCells) {
224 minCellsAt |= 1 << i;
225 minCellsItemCount++;
226 }
227 }
228
Adam Powell35aecd52011-07-01 13:43:49 -0700229 // Items that get expanded will always be in the set of smallest items when we're done.
Adam Powell14b7e2c2011-08-12 11:11:50 -0700230 smallestItemsAt |= minCellsAt;
Adam Powell35aecd52011-07-01 13:43:49 -0700231
Adam Powellbe3c3292011-08-24 12:52:28 -0700232 if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
Adam Powell35aecd52011-07-01 13:43:49 -0700233
Adam Powellbe3c3292011-08-24 12:52:28 -0700234 // We have enough cells, all minimum size items will be incremented.
235 minCells++;
236
237 for (int i = 0; i < childCount; i++) {
Adam Powell35aecd52011-07-01 13:43:49 -0700238 final View child = getChildAt(i);
239 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powellbe3c3292011-08-24 12:52:28 -0700240 if ((minCellsAt & (1 << i)) == 0) {
241 // If this item is already at our small item count, mark it for later.
242 if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
243 continue;
244 }
245
246 if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
247 // Add padding to this item such that it centers.
248 child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
249 }
Adam Powell35aecd52011-07-01 13:43:49 -0700250 lp.cellsUsed++;
251 lp.expanded = true;
252 cellsRemaining--;
253 }
254
255 needsExpansion = true;
256 }
257
258 // Divide any space left that wouldn't divide along cell boundaries
Adam Powell14b7e2c2011-08-12 11:11:50 -0700259 // evenly among the smallest items
Adam Powell35aecd52011-07-01 13:43:49 -0700260
Adam Powell14b7e2c2011-08-12 11:11:50 -0700261 final boolean singleItem = !hasOverflow && visibleItemCount == 1;
262 if (cellsRemaining > 0 && smallestItemsAt != 0 &&
Adam Powellbe3c3292011-08-24 12:52:28 -0700263 (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
Adam Powell14b7e2c2011-08-12 11:11:50 -0700264 float expandCount = Long.bitCount(smallestItemsAt);
265
266 if (!singleItem) {
267 // The items at the far edges may only expand by half in order to pin to either side.
268 if ((smallestItemsAt & 1) != 0) {
Adam Powellbe3c3292011-08-24 12:52:28 -0700269 LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
270 if (!lp.preventEdgeOffset) expandCount -= 0.5f;
Adam Powell14b7e2c2011-08-12 11:11:50 -0700271 }
272 if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
Adam Powellbe3c3292011-08-24 12:52:28 -0700273 LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
274 if (!lp.preventEdgeOffset) expandCount -= 0.5f;
Adam Powell14b7e2c2011-08-12 11:11:50 -0700275 }
276 }
277
Adam Powell3bb421d2011-08-16 15:04:53 -0700278 final int extraPixels = expandCount > 0 ?
279 (int) (cellsRemaining * cellSize / expandCount) : 0;
Adam Powell35aecd52011-07-01 13:43:49 -0700280
281 for (int i = 0; i < childCount; i++) {
Adam Powell14b7e2c2011-08-12 11:11:50 -0700282 if ((smallestItemsAt & (1 << i)) == 0) continue;
Adam Powell35aecd52011-07-01 13:43:49 -0700283
284 final View child = getChildAt(i);
285 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powell14b7e2c2011-08-12 11:11:50 -0700286 if (child instanceof ActionMenuItemView) {
287 // If this is one of our views, expand and measure at the larger size.
288 lp.extraPixels = extraPixels;
289 lp.expanded = true;
Adam Powellbe3c3292011-08-24 12:52:28 -0700290 if (i == 0 && !lp.preventEdgeOffset) {
Adam Powell14b7e2c2011-08-12 11:11:50 -0700291 // First item gets part of its new padding pushed out of sight.
292 // The last item will get this implicitly from layout.
293 lp.leftMargin = -extraPixels / 2;
294 }
295 needsExpansion = true;
296 } else if (lp.isOverflowButton) {
297 lp.extraPixels = extraPixels;
298 lp.expanded = true;
299 lp.rightMargin = -extraPixels / 2;
300 needsExpansion = true;
301 } else {
302 // If we don't know what it is, give it some margins instead
303 // and let it center within its space. We still want to pin
304 // against the edges.
305 if (i != 0) {
306 lp.leftMargin = extraPixels / 2;
307 }
308 if (i != childCount - 1) {
309 lp.rightMargin = extraPixels / 2;
310 }
311 }
Adam Powell35aecd52011-07-01 13:43:49 -0700312 }
313
Adam Powell35aecd52011-07-01 13:43:49 -0700314 cellsRemaining = 0;
315 }
316
317 // Remeasure any items that have had extra space allocated to them.
318 if (needsExpansion) {
Adam Powell35aecd52011-07-01 13:43:49 -0700319 for (int i = 0; i < childCount; i++) {
320 final View child = getChildAt(i);
321 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
322
323 if (!lp.expanded) continue;
324
325 final int width = lp.cellsUsed * cellSize + lp.extraPixels;
Adam Powell367ee322012-05-06 18:32:33 -0700326 child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
327 itemHeightSpec);
Adam Powell35aecd52011-07-01 13:43:49 -0700328 }
329 }
330
331 if (heightMode != MeasureSpec.EXACTLY) {
332 heightSize = maxChildHeight;
333 }
334
335 setMeasuredDimension(widthSize, heightSize);
336 mMeasuredExtraWidth = cellsRemaining * cellSize;
337 }
338
339 /**
340 * Measure a child view to fit within cell-based formatting. The child's width
341 * will be measured to a whole multiple of cellSize.
342 *
Adam Powell160bb7f2011-07-07 10:22:27 -0700343 * <p>Sets the expandable and cellsUsed fields of LayoutParams.
Adam Powell35aecd52011-07-01 13:43:49 -0700344 *
345 * @param child Child to measure
346 * @param cellSize Size of one cell
347 * @param cellsRemaining Number of cells remaining that this view can expand to fill
348 * @param parentHeightMeasureSpec MeasureSpec used by the parent view
349 * @param parentHeightPadding Padding present in the parent view
350 * @return Number of cells this child was measured to occupy
351 */
352 static int measureChildForCells(View child, int cellSize, int cellsRemaining,
353 int parentHeightMeasureSpec, int parentHeightPadding) {
354 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powell35aecd52011-07-01 13:43:49 -0700355
356 final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
357 parentHeightPadding;
358 final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
359 final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
360
Adam Powella7dec6d2012-04-09 15:54:01 -0700361 final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
362 (ActionMenuItemView) child : null;
363 final boolean hasText = itemView != null && itemView.hasText();
364
Adam Powell160bb7f2011-07-07 10:22:27 -0700365 int cellsUsed = 0;
Adam Powella7dec6d2012-04-09 15:54:01 -0700366 if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700367 final int childWidthSpec = MeasureSpec.makeMeasureSpec(
368 cellSize * cellsRemaining, MeasureSpec.AT_MOST);
369 child.measure(childWidthSpec, childHeightSpec);
370
371 final int measuredWidth = child.getMeasuredWidth();
372 cellsUsed = measuredWidth / cellSize;
373 if (measuredWidth % cellSize != 0) cellsUsed++;
Adam Powella7dec6d2012-04-09 15:54:01 -0700374 if (hasText && cellsUsed < 2) cellsUsed = 2;
Adam Powell35aecd52011-07-01 13:43:49 -0700375 }
Adam Powell160bb7f2011-07-07 10:22:27 -0700376
Adam Powella7dec6d2012-04-09 15:54:01 -0700377 final boolean expandable = !lp.isOverflowButton && hasText;
Adam Powell160bb7f2011-07-07 10:22:27 -0700378 lp.expandable = expandable;
379
Adam Powell35aecd52011-07-01 13:43:49 -0700380 lp.cellsUsed = cellsUsed;
381 final int targetWidth = cellsUsed * cellSize;
382 child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
383 childHeightSpec);
384 return cellsUsed;
Adam Powell640a66e2011-04-29 10:18:53 -0700385 }
386
387 @Override
388 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
389 if (!mFormatItems) {
390 super.onLayout(changed, left, top, right, bottom);
391 return;
392 }
393
394 final int childCount = getChildCount();
395 final int midVertical = (top + bottom) / 2;
396 final int dividerWidth = getDividerWidth();
Adam Powell640a66e2011-04-29 10:18:53 -0700397 int overflowWidth = 0;
398 int nonOverflowWidth = 0;
399 int nonOverflowCount = 0;
400 int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
Adam Powell35aecd52011-07-01 13:43:49 -0700401 boolean hasOverflow = false;
Fabrice Di Meglio0762cec2012-09-05 19:17:20 -0700402 final boolean isLayoutRtl = isLayoutRtl();
Adam Powell640a66e2011-04-29 10:18:53 -0700403 for (int i = 0; i < childCount; i++) {
404 final View v = getChildAt(i);
405 if (v.getVisibility() == GONE) {
406 continue;
407 }
408
409 LayoutParams p = (LayoutParams) v.getLayoutParams();
410 if (p.isOverflowButton) {
Adam Powell640a66e2011-04-29 10:18:53 -0700411 overflowWidth = v.getMeasuredWidth();
412 if (hasDividerBeforeChildAt(i)) {
413 overflowWidth += dividerWidth;
414 }
415
416 int height = v.getMeasuredHeight();
Fabrice Di Meglio0762cec2012-09-05 19:17:20 -0700417 int r;
418 int l;
419 if (isLayoutRtl) {
420 l = getPaddingLeft() + p.leftMargin;
421 r = l + overflowWidth;
422 } else {
423 r = getWidth() - getPaddingRight() - p.rightMargin;
424 l = r - overflowWidth;
425 }
Adam Powell640a66e2011-04-29 10:18:53 -0700426 int t = midVertical - (height / 2);
427 int b = t + height;
428 v.layout(l, t, r, b);
429
430 widthRemaining -= overflowWidth;
Adam Powell35aecd52011-07-01 13:43:49 -0700431 hasOverflow = true;
Adam Powell640a66e2011-04-29 10:18:53 -0700432 } else {
Adam Powell35aecd52011-07-01 13:43:49 -0700433 final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
434 nonOverflowWidth += size;
435 widthRemaining -= size;
Adam Powell640a66e2011-04-29 10:18:53 -0700436 if (hasDividerBeforeChildAt(i)) {
437 nonOverflowWidth += dividerWidth;
438 }
439 nonOverflowCount++;
440 }
441 }
442
Adam Powell14b7e2c2011-08-12 11:11:50 -0700443 if (childCount == 1 && !hasOverflow) {
444 // Center a single child
445 final View v = getChildAt(0);
446 final int width = v.getMeasuredWidth();
447 final int height = v.getMeasuredHeight();
448 final int midHorizontal = (right - left) / 2;
449 final int l = midHorizontal - width / 2;
450 final int t = midVertical - height / 2;
451 v.layout(l, t, l + width, t + height);
452 return;
453 }
454
Adam Powell35aecd52011-07-01 13:43:49 -0700455 final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
Adam Powell14b7e2c2011-08-12 11:11:50 -0700456 final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
Adam Powell640a66e2011-04-29 10:18:53 -0700457
Fabrice Di Meglio0762cec2012-09-05 19:17:20 -0700458 if (isLayoutRtl) {
459 int startRight = getWidth() - getPaddingRight();
460 for (int i = 0; i < childCount; i++) {
461 final View v = getChildAt(i);
462 final LayoutParams lp = (LayoutParams) v.getLayoutParams();
463 if (v.getVisibility() == GONE || lp.isOverflowButton) {
464 continue;
465 }
Adam Powell640a66e2011-04-29 10:18:53 -0700466
Fabrice Di Meglio0762cec2012-09-05 19:17:20 -0700467 startRight -= lp.rightMargin;
468 int width = v.getMeasuredWidth();
469 int height = v.getMeasuredHeight();
470 int t = midVertical - height / 2;
471 v.layout(startRight - width, t, startRight, t + height);
472 startRight -= width + lp.leftMargin + spacerSize;
473 }
474 } else {
475 int startLeft = getPaddingLeft();
476 for (int i = 0; i < childCount; i++) {
477 final View v = getChildAt(i);
478 final LayoutParams lp = (LayoutParams) v.getLayoutParams();
479 if (v.getVisibility() == GONE || lp.isOverflowButton) {
480 continue;
481 }
482
483 startLeft += lp.leftMargin;
484 int width = v.getMeasuredWidth();
485 int height = v.getMeasuredHeight();
486 int t = midVertical - height / 2;
487 v.layout(startLeft, t, startLeft + width, t + height);
488 startLeft += width + lp.rightMargin + spacerSize;
489 }
Adam Powell640a66e2011-04-29 10:18:53 -0700490 }
491 }
492
493 @Override
Adam Powell8515ee82010-11-30 14:09:55 -0800494 public void onDetachedFromWindow() {
495 super.onDetachedFromWindow();
Adam Powell696cba52011-03-29 10:38:16 -0700496 mPresenter.dismissPopupMenus();
Adam Powell8028dd32010-07-15 10:16:33 -0700497 }
498
499 public boolean isOverflowReserved() {
500 return mReserveOverflow;
Adam Powell7ade1be2010-06-17 12:51:21 -0700501 }
502
Adam Powellb366bba2010-07-20 14:26:38 -0700503 public void setOverflowReserved(boolean reserveOverflow) {
504 mReserveOverflow = reserveOverflow;
505 }
Adam Powellf0ad6e62011-01-10 17:14:06 -0800506
Adam Powell7ade1be2010-06-17 12:51:21 -0700507 @Override
508 protected LayoutParams generateDefaultLayoutParams() {
509 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
510 LayoutParams.WRAP_CONTENT);
Adam Powell6b336f82010-08-10 20:13:01 -0700511 params.gravity = Gravity.CENTER_VERTICAL;
Adam Powell7ade1be2010-06-17 12:51:21 -0700512 return params;
513 }
514
515 @Override
Adam Powell35aecd52011-07-01 13:43:49 -0700516 public LayoutParams generateLayoutParams(AttributeSet attrs) {
517 return new LayoutParams(getContext(), attrs);
518 }
519
520 @Override
Adam Powell7ade1be2010-06-17 12:51:21 -0700521 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
Adam Powell8c1b02e2012-07-16 17:58:18 -0700522 if (p != null) {
523 final LayoutParams result = p instanceof LayoutParams
524 ? new LayoutParams((LayoutParams) p)
525 : new LayoutParams(p);
Adam Powell3f476b32011-01-03 19:25:36 -0800526 if (result.gravity <= Gravity.NO_GRAVITY) {
527 result.gravity = Gravity.CENTER_VERTICAL;
528 }
529 return result;
530 }
Adam Powell7ade1be2010-06-17 12:51:21 -0700531 return generateDefaultLayoutParams();
532 }
Adam Powell96675b12010-06-10 18:58:59 -0700533
Adam Powell696cba52011-03-29 10:38:16 -0700534 @Override
535 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
Adam Powell35aecd52011-07-01 13:43:49 -0700536 return p != null && p instanceof LayoutParams;
Adam Powell696cba52011-03-29 10:38:16 -0700537 }
538
Adam Powell640a66e2011-04-29 10:18:53 -0700539 public LayoutParams generateOverflowButtonLayoutParams() {
540 LayoutParams result = generateDefaultLayoutParams();
541 result.isOverflowButton = true;
542 return result;
543 }
544
Adam Powell96675b12010-06-10 18:58:59 -0700545 public boolean invokeItem(MenuItemImpl item) {
546 return mMenu.performItemAction(item, 0);
547 }
548
549 public int getWindowAnimations() {
550 return 0;
551 }
552
Adam Powell696cba52011-03-29 10:38:16 -0700553 public void initialize(MenuBuilder menu) {
Adam Powell96675b12010-06-10 18:58:59 -0700554 mMenu = menu;
Adam Powell96675b12010-06-10 18:58:59 -0700555 }
556
Adam Powell696cba52011-03-29 10:38:16 -0700557 @Override
558 protected boolean hasDividerBeforeChildAt(int childIndex) {
Jake Wharton825992f2012-07-28 21:31:51 -0700559 if (childIndex == 0) {
560 return false;
561 }
Adam Powell696cba52011-03-29 10:38:16 -0700562 final View childBefore = getChildAt(childIndex - 1);
563 final View child = getChildAt(childIndex);
564 boolean result = false;
565 if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
566 result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
Adam Powell96675b12010-06-10 18:58:59 -0700567 }
Adam Powell696cba52011-03-29 10:38:16 -0700568 if (childIndex > 0 && child instanceof ActionMenuChildView) {
569 result |= ((ActionMenuChildView) child).needsDividerBefore();
Adam Powell8028dd32010-07-15 10:16:33 -0700570 }
Adam Powellbe4d68e2010-10-08 18:16:34 -0700571 return result;
572 }
573
Adam Powell7bc3ca02011-08-26 18:29:58 -0700574 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
575 return false;
576 }
577
Adam Powell696cba52011-03-29 10:38:16 -0700578 public interface ActionMenuChildView {
579 public boolean needsDividerBefore();
580 public boolean needsDividerAfter();
Adam Powell8515ee82010-11-30 14:09:55 -0800581 }
Adam Powell640a66e2011-04-29 10:18:53 -0700582
583 public static class LayoutParams extends LinearLayout.LayoutParams {
584 @ViewDebug.ExportedProperty(category = "layout")
585 public boolean isOverflowButton;
Adam Powell35aecd52011-07-01 13:43:49 -0700586 @ViewDebug.ExportedProperty(category = "layout")
587 public int cellsUsed;
588 @ViewDebug.ExportedProperty(category = "layout")
Adam Powell35aecd52011-07-01 13:43:49 -0700589 public int extraPixels;
Adam Powell160bb7f2011-07-07 10:22:27 -0700590 @ViewDebug.ExportedProperty(category = "layout")
591 public boolean expandable;
Adam Powellbe3c3292011-08-24 12:52:28 -0700592 @ViewDebug.ExportedProperty(category = "layout")
593 public boolean preventEdgeOffset;
Adam Powell160bb7f2011-07-07 10:22:27 -0700594
Adam Powell35aecd52011-07-01 13:43:49 -0700595 public boolean expanded;
Adam Powell640a66e2011-04-29 10:18:53 -0700596
597 public LayoutParams(Context c, AttributeSet attrs) {
598 super(c, attrs);
599 }
600
Adam Powell8c1b02e2012-07-16 17:58:18 -0700601 public LayoutParams(ViewGroup.LayoutParams other) {
602 super(other);
603 }
604
Adam Powell640a66e2011-04-29 10:18:53 -0700605 public LayoutParams(LayoutParams other) {
606 super((LinearLayout.LayoutParams) other);
607 isOverflowButton = other.isOverflowButton;
608 }
609
610 public LayoutParams(int width, int height) {
611 super(width, height);
612 isOverflowButton = false;
613 }
614
615 public LayoutParams(int width, int height, boolean isOverflowButton) {
616 super(width, height);
617 this.isOverflowButton = isOverflowButton;
618 }
619 }
Adam Powell96675b12010-06-10 18:58:59 -0700620}