Material Design Team a487c89185 Internal change
PiperOrigin-RevId: 412135443
2021-11-29 17:35:39 +00:00

199 lines
7.7 KiB
Java

/*
* Copyright (C) 2016 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 com.google.android.material.bottomnavigation;
import com.google.android.material.R;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.Context;
import android.content.res.Resources;
import androidx.appcompat.view.menu.MenuBuilder;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.core.view.ViewCompat;
import com.google.android.material.navigation.NavigationBarItemView;
import com.google.android.material.navigation.NavigationBarMenuView;
/** @hide For internal use only. */
@RestrictTo(LIBRARY_GROUP)
public class BottomNavigationMenuView extends NavigationBarMenuView {
private final int inactiveItemMaxWidth;
private final int inactiveItemMinWidth;
private final int activeItemMaxWidth;
private final int activeItemMinWidth;
private boolean itemHorizontalTranslationEnabled;
private int[] tempChildWidths;
public BottomNavigationMenuView(@NonNull Context context) {
super(context);
FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
setLayoutParams(params);
final Resources res = getResources();
inactiveItemMaxWidth =
res.getDimensionPixelSize(R.dimen.design_bottom_navigation_item_max_width);
inactiveItemMinWidth =
res.getDimensionPixelSize(R.dimen.design_bottom_navigation_item_min_width);
activeItemMaxWidth =
res.getDimensionPixelSize(R.dimen.design_bottom_navigation_active_item_max_width);
activeItemMinWidth =
res.getDimensionPixelSize(R.dimen.design_bottom_navigation_active_item_min_width);
tempChildWidths = new int[BottomNavigationView.MAX_ITEM_COUNT];
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final MenuBuilder menu = getMenu();
final int width = MeasureSpec.getSize(widthMeasureSpec);
// Use visible item count to calculate widths
final int visibleCount = menu.getVisibleItems().size();
// Use total item counts to measure children
final int totalCount = getChildCount();
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
final int heightSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY);
if (isShifting(getLabelVisibilityMode(), visibleCount)
&& isItemHorizontalTranslationEnabled()) {
final View activeChild = getChildAt(getSelectedItemPosition());
int activeItemWidth = activeItemMinWidth;
if (activeChild.getVisibility() != View.GONE) {
// Do an AT_MOST measure pass on the active child to get its desired width, and resize the
// active child view based on that width
activeChild.measure(
MeasureSpec.makeMeasureSpec(activeItemMaxWidth, MeasureSpec.AT_MOST), heightSpec);
activeItemWidth = Math.max(activeItemWidth, activeChild.getMeasuredWidth());
}
final int inactiveCount = visibleCount - (activeChild.getVisibility() != View.GONE ? 1 : 0);
final int activeMaxAvailable = width - inactiveCount * inactiveItemMinWidth;
final int activeWidth =
Math.min(activeMaxAvailable, Math.min(activeItemWidth, activeItemMaxWidth));
final int inactiveMaxAvailable =
(width - activeWidth) / (inactiveCount == 0 ? 1 : inactiveCount);
final int inactiveWidth = Math.min(inactiveMaxAvailable, inactiveItemMaxWidth);
int extra = width - activeWidth - inactiveWidth * inactiveCount;
for (int i = 0; i < totalCount; i++) {
if (getChildAt(i).getVisibility() != View.GONE) {
tempChildWidths[i] = (i == getSelectedItemPosition()) ? activeWidth : inactiveWidth;
// Account for integer division which sometimes leaves some extra pixel spaces.
// e.g. If the nav was 10px wide, and 3 children were measured to be 3px-3px-3px, there
// would be a 1px gap somewhere, which this fills in.
if (extra > 0) {
tempChildWidths[i]++;
extra--;
}
} else {
tempChildWidths[i] = 0;
}
}
} else {
final int maxAvailable = width / (visibleCount == 0 ? 1 : visibleCount);
final int childWidth = Math.min(maxAvailable, activeItemMaxWidth);
int extra = width - childWidth * visibleCount;
for (int i = 0; i < totalCount; i++) {
if (getChildAt(i).getVisibility() != View.GONE) {
tempChildWidths[i] = childWidth;
if (extra > 0) {
tempChildWidths[i]++;
extra--;
}
} else {
tempChildWidths[i] = 0;
}
}
}
int totalWidth = 0;
for (int i = 0; i < totalCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(
MeasureSpec.makeMeasureSpec(tempChildWidths[i], MeasureSpec.EXACTLY), heightSpec);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
totalWidth += child.getMeasuredWidth();
}
setMeasuredDimension(
View.resolveSizeAndState(
totalWidth, MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), 0),
View.resolveSizeAndState(parentHeight, heightMeasureSpec, 0));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int width = right - left;
final int height = bottom - top;
int used = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
child.layout(width - used - child.getMeasuredWidth(), 0, width - used, height);
} else {
child.layout(used, 0, child.getMeasuredWidth() + used, height);
}
used += child.getMeasuredWidth();
}
}
/**
* Sets whether the menu items horizontally translate on selection when the combined item widths
* fill the screen.
*
* @param itemHorizontalTranslationEnabled whether the menu items horizontally translate on
* selection
* @see #isItemHorizontalTranslationEnabled()
*/
public void setItemHorizontalTranslationEnabled(boolean itemHorizontalTranslationEnabled) {
this.itemHorizontalTranslationEnabled = itemHorizontalTranslationEnabled;
}
/**
* Returns whether the menu items horizontally translate on selection when the combined item
* widths fill the screen.
*
* @return whether the menu items horizontally translate on selection
* @see #setItemHorizontalTranslationEnabled(boolean)
*/
public boolean isItemHorizontalTranslationEnabled() {
return itemHorizontalTranslationEnabled;
}
@Override
@NonNull
protected NavigationBarItemView createNavigationBarItemView(@NonNull Context context) {
return new BottomNavigationItemView(context);
}
}