static int measureChildForCells( View child, int cellSize, int cellsRemaining, int parentHeightMeasureSpec, int parentHeightPadding) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); int childHeightSpec = MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(parentHeightMeasureSpec) - parentHeightPadding, MeasureSpec.getMode(parentHeightMeasureSpec)); ActionMenuItemView itemView = child instanceof ActionMenuItemView ? (ActionMenuItemView) child : null; boolean hasText = itemView != null && itemView.hasText(); int cellsUsed = 0; if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) { child.measure( MeasureSpec.makeMeasureSpec(cellSize * cellsRemaining, ExploreByTouchHelper.INVALID_ID), childHeightSpec); int measuredWidth = child.getMeasuredWidth(); cellsUsed = measuredWidth / cellSize; if (measuredWidth % cellSize != 0) { cellsUsed++; } if (hasText && cellsUsed < 2) { cellsUsed = 2; } } boolean expandable = !lp.isOverflowButton && hasText; lp.expandable = expandable; lp.cellsUsed = cellsUsed; child.measure(MeasureSpec.makeMeasureSpec(cellsUsed * cellSize, 1073741824), childHeightSpec); return cellsUsed; }
/** * Measure a child view to fit within cell-based formatting. The child's width will be measured to * a whole multiple of cellSize. * * <p> * * <p>Sets the expandable and cellsUsed fields of LayoutParams. * * @param child Child to measure * @param cellSize Size of one cell * @param cellsRemaining Number of cells remaining that this view can expand to fill * @param parentHeightMeasureSpec MeasureSpec used by the parent view * @param parentHeightPadding Padding present in the parent view * @return Number of cells this child was measured to occupy */ static int measureChildForCells( View child, int cellSize, int cellsRemaining, int parentHeightMeasureSpec, int parentHeightPadding) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) - parentHeightPadding; final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec); final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode); int cellsUsed = 0; if (cellsRemaining > 0) { final int childWidthSpec = MeasureSpec.makeMeasureSpec(cellSize * cellsRemaining, MeasureSpec.AT_MOST); child.measure(childWidthSpec, childHeightSpec); final int measuredWidth = child.getMeasuredWidth(); cellsUsed = measuredWidth / cellSize; if (measuredWidth % cellSize != 0) cellsUsed++; } final ActionMenuItemView itemView = child instanceof ActionMenuItemView ? (ActionMenuItemView) child : null; final boolean expandable = !lp.isOverflowButton && itemView != null && itemView.hasText(); lp.expandable = expandable; lp.cellsUsed = cellsUsed; final int targetWidth = cellsUsed * cellSize; child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), childHeightSpec); return cellsUsed; }
private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) { // We already know the width mode is EXACTLY if we're here. final int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); final int widthPadding = getPaddingLeft() + getPaddingRight(); final int heightPadding = getPaddingTop() + getPaddingBottom(); final int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding, ViewGroup.LayoutParams.WRAP_CONTENT); widthSize -= widthPadding; // Divide the view into cells. final int cellCount = widthSize / mMinCellSize; final int cellSizeRemaining = widthSize % mMinCellSize; if (cellCount == 0) { // Give up, nothing fits. setMeasuredDimension(widthSize, 0); return; } final int cellSize = mMinCellSize + cellSizeRemaining / cellCount; int cellsRemaining = cellCount; int maxChildHeight = 0; int maxCellsUsed = 0; int expandableItemCount = 0; int visibleItemCount = 0; boolean hasOverflow = false; // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64. long smallestItemsAt = 0; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); if (child.getVisibility() == GONE) continue; final boolean isGeneratedItem = child instanceof ActionMenuItemView; visibleItemCount++; if (isGeneratedItem) { // Reset padding for generated menu item views; it may change below // and views are recycled. child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0); } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.expanded = false; lp.extraPixels = 0; lp.cellsUsed = 0; lp.expandable = false; lp.leftMargin = 0; lp.rightMargin = 0; lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText(); // Overflow always gets 1 cell. No more, no less. final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining; final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable, itemHeightSpec, heightPadding); maxCellsUsed = Math.max(maxCellsUsed, cellsUsed); if (lp.expandable) expandableItemCount++; if (lp.isOverflowButton) hasOverflow = true; cellsRemaining -= cellsUsed; maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); if (cellsUsed == 1) smallestItemsAt |= (1 << i); } // When we have overflow and a single expanded (text) item, we want to try centering it // visually in the available space even though overflow consumes some of it. final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2; // Divide space for remaining cells if we have items that can expand. // Try distributing whole leftover cells to smaller items first. boolean needsExpansion = false; while (expandableItemCount > 0 && cellsRemaining > 0) { int minCells = Integer.MAX_VALUE; long minCellsAt = 0; // Bit locations are indices of relevant child views int minCellsItemCount = 0; for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); // Don't try to expand items that shouldn't. if (!lp.expandable) continue; // Mark indices of children that can receive an extra cell. if (lp.cellsUsed < minCells) { minCells = lp.cellsUsed; minCellsAt = 1 << i; minCellsItemCount = 1; } else if (lp.cellsUsed == minCells) { minCellsAt |= 1 << i; minCellsItemCount++; } } // Items that get expanded will always be in the set of smallest items when we're done. smallestItemsAt |= minCellsAt; if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop. // We have enough cells, all minimum size items will be incremented. minCells++; for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if ((minCellsAt & (1 << i)) == 0) { // If this item is already at our small item count, mark it for later. if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i; continue; } if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) { // Add padding to this item such that it centers. child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0); } lp.cellsUsed++; lp.expanded = true; cellsRemaining--; } needsExpansion = true; } // Divide any space left that wouldn't divide along cell boundaries // evenly among the smallest items final boolean singleItem = !hasOverflow && visibleItemCount == 1; if (cellsRemaining > 0 && smallestItemsAt != 0 && (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) { float expandCount = Long.bitCount(smallestItemsAt); if (!singleItem) { // The items at the far edges may only expand by half in order to pin to either side. if ((smallestItemsAt & 1) != 0) { LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams(); if (!lp.preventEdgeOffset) expandCount -= 0.5f; } if ((smallestItemsAt & (1 << (childCount - 1))) != 0) { LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams()); if (!lp.preventEdgeOffset) expandCount -= 0.5f; } } final int extraPixels = expandCount > 0 ? (int) (cellsRemaining * cellSize / expandCount) : 0; for (int i = 0; i < childCount; i++) { if ((smallestItemsAt & (1 << i)) == 0) continue; final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (child instanceof ActionMenuItemView) { // If this is one of our views, expand and measure at the larger size. lp.extraPixels = extraPixels; lp.expanded = true; if (i == 0 && !lp.preventEdgeOffset) { // First item gets part of its new padding pushed out of sight. // The last item will get this implicitly from layout. lp.leftMargin = -extraPixels / 2; } needsExpansion = true; } else if (lp.isOverflowButton) { lp.extraPixels = extraPixels; lp.expanded = true; lp.rightMargin = -extraPixels / 2; needsExpansion = true; } else { // If we don't know what it is, give it some margins instead // and let it center within its space. We still want to pin // against the edges. if (i != 0) { lp.leftMargin = extraPixels / 2; } if (i != childCount - 1) { lp.rightMargin = extraPixels / 2; } } } cellsRemaining = 0; } // Remeasure any items that have had extra space allocated to them. if (needsExpansion) { for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.expanded) continue; final int width = lp.cellsUsed * cellSize + lp.extraPixels; child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), itemHeightSpec); } } if (heightMode != MeasureSpec.EXACTLY) { heightSize = maxChildHeight; } setMeasuredDimension(widthSize, heightSize); }
private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) { int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int widthPadding = getPaddingLeft() + getPaddingRight(); int heightPadding = getPaddingTop() + getPaddingBottom(); int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding, -2); widthSize -= widthPadding; int cellCount = widthSize / this.mMinCellSize; int cellSizeRemaining = widthSize % this.mMinCellSize; if (cellCount == 0) { setMeasuredDimension(widthSize, 0); return; } int i; LayoutParams lp; int cellSize = this.mMinCellSize + (cellSizeRemaining / cellCount); int cellsRemaining = cellCount; int maxChildHeight = 0; int maxCellsUsed = 0; int expandableItemCount = 0; int visibleItemCount = 0; boolean hasOverflow = false; long smallestItemsAt = 0; int childCount = getChildCount(); for (i = 0; i < childCount; i++) { View child = getChildAt(i); if (child.getVisibility() != 8) { int cellsAvailable; boolean isGeneratedItem = child instanceof ActionMenuItemView; visibleItemCount++; if (isGeneratedItem) { child.setPadding(this.mGeneratedItemPadding, 0, this.mGeneratedItemPadding, 0); } lp = (LayoutParams) child.getLayoutParams(); lp.expanded = false; lp.extraPixels = 0; lp.cellsUsed = 0; lp.expandable = false; lp.leftMargin = 0; lp.rightMargin = 0; boolean z = isGeneratedItem && ((ActionMenuItemView) child).hasText(); lp.preventEdgeOffset = z; if (lp.isOverflowButton) { cellsAvailable = 1; } else { cellsAvailable = cellsRemaining; } int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable, itemHeightSpec, heightPadding); maxCellsUsed = Math.max(maxCellsUsed, cellsUsed); if (lp.expandable) { expandableItemCount++; } if (lp.isOverflowButton) { hasOverflow = true; } cellsRemaining -= cellsUsed; maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); if (cellsUsed == 1) { smallestItemsAt |= (long) (1 << i); } } } boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2; boolean needsExpansion = false; while (expandableItemCount > 0 && cellsRemaining > 0) { int minCells = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED; long minCellsAt = 0; int minCellsItemCount = 0; for (i = 0; i < childCount; i++) { int i2; lp = (LayoutParams) getChildAt(i).getLayoutParams(); if (lp.expandable) { i2 = lp.cellsUsed; if (r0 < minCells) { minCells = lp.cellsUsed; minCellsAt = (long) (1 << i); minCellsItemCount = 1; } else { i2 = lp.cellsUsed; if (r0 == minCells) { minCellsAt |= (long) (1 << i); minCellsItemCount++; } } } } smallestItemsAt |= minCellsAt; if (minCellsItemCount > cellsRemaining) { break; } minCells++; for (i = 0; i < childCount; i++) { child = getChildAt(i); lp = (LayoutParams) child.getLayoutParams(); if ((((long) (1 << i)) & minCellsAt) == 0) { i2 = lp.cellsUsed; if (r0 == minCells) { smallestItemsAt |= (long) (1 << i); } } else { if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) { child.setPadding( this.mGeneratedItemPadding + cellSize, 0, this.mGeneratedItemPadding, 0); } lp.cellsUsed++; lp.expanded = true; cellsRemaining--; } } needsExpansion = true; } boolean singleItem = !hasOverflow && visibleItemCount == 1; if (cellsRemaining > 0 && smallestItemsAt != 0 && (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) { float expandCount = (float) Long.bitCount(smallestItemsAt); if (!singleItem) { if ((1 & smallestItemsAt) != 0) { if (!((LayoutParams) getChildAt(0).getLayoutParams()).preventEdgeOffset) { expandCount -= 0.5f; } } if ((((long) (1 << (childCount - 1))) & smallestItemsAt) != 0) { if (!((LayoutParams) getChildAt(childCount - 1).getLayoutParams()).preventEdgeOffset) { expandCount -= 0.5f; } } } int extraPixels = expandCount > 0.0f ? (int) (((float) (cellsRemaining * cellSize)) / expandCount) : 0; for (i = 0; i < childCount; i++) { if ((((long) (1 << i)) & smallestItemsAt) != 0) { child = getChildAt(i); lp = (LayoutParams) child.getLayoutParams(); if (child instanceof ActionMenuItemView) { lp.extraPixels = extraPixels; lp.expanded = true; if (i == 0 && !lp.preventEdgeOffset) { lp.leftMargin = (-extraPixels) / 2; } needsExpansion = true; } else if (lp.isOverflowButton) { lp.extraPixels = extraPixels; lp.expanded = true; lp.rightMargin = (-extraPixels) / 2; needsExpansion = true; } else { if (i != 0) { lp.leftMargin = extraPixels / 2; } if (i != childCount - 1) { lp.rightMargin = extraPixels / 2; } } } } } if (needsExpansion) { for (i = 0; i < childCount; i++) { child = getChildAt(i); lp = (LayoutParams) child.getLayoutParams(); if (lp.expanded) { child.measure( MeasureSpec.makeMeasureSpec((lp.cellsUsed * cellSize) + lp.extraPixels, 1073741824), itemHeightSpec); } } } if (heightMode != 1073741824) { heightSize = maxChildHeight; } setMeasuredDimension(widthSize, heightSize); }