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);
  }
Exemplo n.º 2
0
 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);
 }