BottomNavigationView
官方并没有提供给我们修改动画效果的方法,在Stackoverflow上找到的方案也是利用反射机制关闭。
1.创建一个Helper
类,在里面添加如下方法
禁用Item>=3时的平移动画
@SuppressLint("RestrictedApi")
public static void disableShiftingMode(BottomNavigationView view) {
try {
BottomNavigationMenuView mMenuView = (BottomNavigationMenuView) view.getChildAt(0);
Field mShiftingModeField = BottomNavigationMenuView.class.getDeclaredField("mShiftingMode");
mShiftingModeField.setAccessible(true);
mShiftingModeField.set(mMenuView, false);
for (int i = 0; i < mMenuView.getChildCount(); i++) {
BottomNavigationItemView itemView = (BottomNavigationItemView) mMenuView.getChildAt(i);
itemView.setShiftingMode(false);
itemView.setChecked(itemView.getItemData().isChecked());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
}
}
禁用选中放大图标/文字的动画
@SuppressLint("RestrictedApi")
public static void disableItemScale(BottomNavigationView view) {
try {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
Field mLargeLabelField = BottomNavigationItemView.class.getDeclaredField("mLargeLabel");
Field mSmallLabelField = BottomNavigationItemView.class.getDeclaredField("mSmallLabel");
Field mShiftAmountField = BottomNavigationItemView.class.getDeclaredField("mShiftAmount");
Field mScaleUpFactorField = BottomNavigationItemView.class.getDeclaredField("mScaleUpFactor");
Field mScaleDownFactorField = BottomNavigationItemView.class.getDeclaredField("mScaleDownFactor");
mSmallLabelField.setAccessible(true);
mLargeLabelField.setAccessible(true);
mShiftAmountField.setAccessible(true);
mScaleUpFactorField.setAccessible(true);
mScaleDownFactorField.setAccessible(true);
final float fontScale = view.getResources().getDisplayMetrics().scaledDensity;
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
TextView lagerObj = (TextView) mLargeLabelField.get(itemView);
TextView smallObj = (TextView) mSmallLabelField.get(itemView);
lagerObj.setTextSize(smallObj.getTextSize() / fontScale + 0.5f);
mShiftAmountField.set(itemView, 0);
mScaleUpFactorField.set(itemView, 1f);
mScaleDownFactorField.set(itemView, 1f);
itemView.setChecked(itemView.getItemData().isChecked());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
}
}
2.使用
在Activity
或者Fragment
中调用对应方法使用即可
BottomNavigationView mNavigationView= (BottomNavigationView) findViewById(R.id.bottom_navigation);
BottomNavigationViewHelper.disableShiftMode(mNavigationView);
BottomNavigationViewHelper.disableItemScale(mNavigationView);
3.混淆
如果按照上述方法修改了之后,会发现release
版本中如果开启了混淆,上述修改就失效了。
这是因为编译时编译器把字段混淆了,所以方法就找不到反射需要的字段了。
所以如果开启了混淆,需要把如下规则添加到proguard-rules.pro
文件中:
#因为动画效果由BottomNavigationMenuView类控制,所以只需要添加这一条即可
#原理见下文
-keep class android.support.design.internal.BottomNavigationMenuView {*;}
4.原理
BottomNavigationView
的显示效果由BottomNavigationMenuView
控制
public class BottomNavigationView extends FrameLayout {
private final BottomNavigationMenuView mMenuView;
}
public class BottomNavigationMenuView extends ViewGroup implements MenuView {
//这里控制平移动画效果
private boolean mShiftingMode = true;
public void buildMenuView() {
mButtons = new BottomNavigationItemView[mMenu.size()];
//这里控制item大于3的时候缩放动画
mShiftingMode = mMenu.size() > 3;
for (int i = 0; i < mMenu.size(); i++) {
BottomNavigationItemView child = getNewItem();
child.setShiftingMode(mShiftingMode);
//...
}
}
}
public class BottomNavigationItemView extends FrameLayout implements MenuView.ItemView {
private final int mShiftAmount;
private final float mScaleUpFactor;
private final float mScaleDownFactor;
private boolean mShiftingMode;
public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final Resources res = getResources();
int inactiveLabelSize =
res.getDimensionPixelSize(R.dimen.design_bottom_navigation_text_size);
int activeLabelSize = res.getDimensionPixelSize(
R.dimen.design_bottom_navigation_active_text_size);
mDefaultMargin = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_margin);
mShiftAmount = inactiveLabelSize - activeLabelSize;
mScaleUpFactor = 1f * activeLabelSize / inactiveLabelSize;
mScaleDownFactor = 1f * inactiveLabelSize / activeLabelSize;
LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item, this, true);
setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
mIcon = findViewById(R.id.icon);
mSmallLabel = findViewById(R.id.smallLabel);
mLargeLabel = findViewById(R.id.largeLabel);
}
@Override
public void setChecked(boolean checked) {
if (mShiftingMode) {
if (checked) {
LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
iconParams.topMargin = mDefaultMargin;
mIcon.setLayoutParams(iconParams);
mLargeLabel.setVisibility(VISIBLE);
mLargeLabel.setScaleX(1f);
mLargeLabel.setScaleY(1f);
} else {
LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
iconParams.gravity = Gravity.CENTER;
iconParams.topMargin = mDefaultMargin;
mIcon.setLayoutParams(iconParams);
//ShiftingMode模式下,不选中文字隐藏
mLargeLabel.setVisibility(INVISIBLE);
mLargeLabel.setScaleX(0.5f);
mLargeLabel.setScaleY(0.5f);
}
mSmallLabel.setVisibility(INVISIBLE);
} else {
if (checked) {
LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
//通过设置mShiftAmount 的大小 控制图片margin,进而控制图片大小
iconParams.topMargin = mDefaultMargin + mShiftAmount;
mIcon.setLayoutParams(iconParams);
//通过设置mLargeLabel和mSmallLabel的显示与隐藏控制文本大小
//如果把他俩TextSize设置为一样。则就不会变化
mLargeLabel.setVisibility(VISIBLE);
mSmallLabel.setVisibility(INVISIBLE);
mLargeLabel.setScaleX(1f);
mLargeLabel.setScaleY(1f);
mSmallLabel.setScaleX(mScaleUpFactor);
mSmallLabel.setScaleY(mScaleUpFactor);
} else {
LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
iconParams.topMargin = mDefaultMargin;
mIcon.setLayoutParams(iconParams);
mLargeLabel.setVisibility(INVISIBLE);
mSmallLabel.setVisibility(VISIBLE);
mLargeLabel.setScaleX(mScaleDownFactor);
mLargeLabel.setScaleY(mScaleDownFactor);
mSmallLabel.setScaleX(1f);
mSmallLabel.setScaleY(1f);
}
}
refreshDrawableState();
}
}
<!--大文本和小文本大小-->
<dimen name="design_bottom_navigation_text_size">12sp</dimen>
<dimen name="design_bottom_navigation_active_text_size">14sp</dimen>
结论
所以要去掉item数量大于3时的缩放和平移:
1.通过反射把BottomNavigationMenuView#mShiftingMode修改为false。
2.通过反射把BottomNavigationMenuView中的BottomNavigationItemView#mShiftingMode修改为false.
如果去掉item中的图片缩放和文本大小改变:
1.mShiftAmount 设置为 0。
2.mScaleUpFactor和mScaleDownFactor设置为1.0f。
3.mLargeLabel和mSmallLabel的文本大小设置为一样