最近开展新的项目,首当其中的问题就是首页导航。
虽然之前就已经知道BottomNavigationView
的存在,但是一直没有使用。原因也很明显,BottomNavigationView
存在两个非常严重的问题:
- 使用png格式的图标时无法显示原本的图案;
-
默认有位移动画,且无法通过配置进行取消。
禁用位移动画.gif
统一所有条目中图标边距和文本的大小
在禁用了位移动画后,我们发现在选中一个条目的时候,条目的图标和文本框还是有轻微的位移。我们继续向下分析:
@Override public void setChecked(boolean checked) { // ... 省略无关代码 if (mShiftingMode) { // 当mShiftingMode=true时,该分支生效 if (checked) { // ... 省略无关代码 mLargeLabel.setVisibility(VISIBLE); mLargeLabel.setScaleX(1f); mLargeLabel.setScaleY(1f); } else { // ... 省略无关代码 mLargeLabel.setVisibility(INVISIBLE); mLargeLabel.setScaleX(0.5f); mLargeLabel.setScaleY(0.5f); } mSmallLabel.setVisibility(INVISIBLE); } else { // 当mShiftingMode=false时,该分支生效 if (checked) { LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams(); iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; // 选中时,图标的顶部外边距增加了mDefaultMargin+mShiftAmount iconParams.topMargin = mDefaultMargin + mShiftAmount; mIcon.setLayoutParams(iconParams); // 选中时,mLargeLabel显示、mSmallLabel隐藏 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隐藏、mSmallLabel显示 mLargeLabel.setVisibility(INVISIBLE); mSmallLabel.setVisibility(VISIBLE); mLargeLabel.setScaleX(mScaleDownFactor); mLargeLabel.setScaleY(mScaleDownFactor); mSmallLabel.setScaleX(1f); mSmallLabel.setScaleY(1f); } } refreshDrawableState(); }
从上面的代码中可以看出,问题原因在于选中的条目的图标增加了顶部外边距
mShiftAmount
,以及大小两个文本框的显隐变化。为了解决这两个问题,做出如下扩展:
/** * 统一每个条目的文本大小和图标的外边距 */ fun BottomNavigationView.unifyItems(forceUpdate: Boolean = true) { try { val bottomNavigationMenuView = getChildAt(0) as BottomNavigationMenuView val childCount = bottomNavigationMenuView.childCount for (i in 0 until childCount) { val child = bottomNavigationMenuView.getChildAt(i) as BottomNavigationItemView val clazz = child.javaClass // 使选中/未选中条目的顶部边距不发生变化 val shiftAmountField = clazz.getDeclaredField("mShiftAmount") shiftAmountField.setIntValue(child, 0) // 使选中/未选中条目的文本保持同样的大小 child.unifyTextSize() } if (forceUpdate) { bottomNavigationMenuView.updateMenuView() } } catch (e: Exception) { e.printStackTrace() } } private fun BottomNavigationItemView.unifyTextSize() { val baselineLayout = getChildAt(1) as BaselineLayout val smallLabel = baselineLayout.getChildAt(0) as TextView val largeLabel = baselineLayout.getChildAt(1) as TextView largeLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, smallLabel.textSize) } private fun Field.setIntValue(obj: Any, value: Int) { isAccessible = true setInt(obj, value) isAccessible = false }
效果如下:
最终效果.gif
网友评论