实现效果安卓上一直没有上图片下文字的组件,即使是谷歌的BottomNavigationBar,也只能是一整个控件,而且横竖屏都是一个样式,不能定制。其实这也怪国内设计师没想法,人苹果怎么来UI就怎么画。
需求
- 图片
- 文字
- 红点(消息推送)
定义及获取
背景不需要指定,只需要在TabBar指定就行,TabBar将TabItem作为子控件。
1. 图片
- 定义
private var tabIcon: Drawable? = null
- 格式
<attr name="tab_icon" format="reference"/>
- 获取
tabIcon = ta.getDrawable(R.styleable.TabItem_tab_icon)
传入的是selector类型的xml?没问题,它是StateListDrawable,是Drawable的子类。仍然正常接收,状态切换的话。
翻了下ImageView
的源码,easy,就是在回调中设置下view的状态就行了。
override fun drawableStateChanged() {
super.drawableStateChanged()
tabIcon?.let { icon ->
icon.setState(drawableState).takeIf { icon.isStateful }
}
// 为保证文字颜色也会改变,统一重新绘制
invalidate()
}
2. 文字
- 定义
private var tabTitle: String = ""
- 格式
<attr name="tab_title" format="string"/>
- 获取
tabTitle = ta.getString(R.styleable.TabItem_tab_title) ?: "Tab"
太简单了,不说了。
3. 红点
- 定义
var showBadge: Boolean = false
private var badgeSize: Int = if (isInEditMode) 8 else 6.dp.toInt()
badgeSize可以从xml读取,showBadge直接从外部设置就行。
实现
重写onDraw方法即可
override fun onDraw(canvas: Canvas?) {
// 设置字体大小
textPaint.textSize = tabTitleSize
// 获取文字宽度
val titleWidth = textPaint.measureText(tabTitle)
// 获取文字高度
val titleHeight = textPaint.fontMetrics.bottom - textPaint.fontMetrics.top
canvas?.let {
// 确定文字的left、top
// left、right左右居中
titleL = (measuredWidth - titleWidth) / 2f
// 无icon上下居中,有icon,加上icon高度和间距再居中
titleT = (measuredHeight - titleHeight + (0.takeIf { intrinsicHeight == 0 } ?: (intrinsicHeight + iconPadding))) / 2f
tabIcon?.let { icon ->
// 确定图片的left、top、right、bottom
iconL = (measuredWidth - intrinsicWidth) / 2
iconR = iconL + intrinsicWidth
// padding+图片高度+文字高度之后 图的位置
iconT = (measuredHeight - titleHeight.toInt() - (0.takeIf { intrinsicHeight == 0 } ?: (iconPadding + intrinsicHeight))) / 2
iconB = iconT + intrinsicHeight
drawDrawable(icon, it)
if (showBadge) drawBadge(it)
}
drawTitle(it, titleHeight.toInt())
}
}
具体代码及实现可以参考我的Github页面
网友评论