Я добавил индикатор в нижнюю панель навигации, чтобы получить ожидаемый результат, подобный этому изображение 1 , но он ведет себя странно, когда я нажимаю на вкладку в первый раз изображение 2 , но показываю ожидаемый результат при ретуши на вкладке изображение 3
Это код для BottomNavigationViewIndicator:
class BottomNavigationViewIndicator @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
private val targetId: Int
private var target: BottomNavigationMenuView? = null
private var rect = Rect()
private val backgroundDrawable: Drawable
private var index = 0
private var animator: AnimatorSet? = null
init {
if (attrs == null) {
targetId = NO_ID
backgroundDrawable = ColorDrawable(Color.TRANSPARENT)
} else {
with(context.obtainStyledAttributes(attrs, R.styleable.BottomNavigationViewIndicator)) {
targetId = getResourceId(R.styleable.BottomNavigationViewIndicator_targetBottomNavigation, NO_ID)
val clipableId = getResourceId(R.styleable.BottomNavigationViewIndicator_clipableBackground, NO_ID)
backgroundDrawable = if (clipableId != NO_ID) {
getDrawable(context, clipableId) ?: ColorDrawable(Color.TRANSPARENT)
} else {
ColorDrawable(getColor(R.styleable.BottomNavigationViewIndicator_clipableBackground, Color.TRANSPARENT))
}
recycle()
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (targetId == NO_ID) return attachedError("invalid target id $targetId, did you set the app:targetBottomNavigation attribute?")
val parentView = parent as? View ?: return attachedError("Impossible to find the view using $parent")
val child = parentView.findViewById<View?>(targetId)
if (child !is ListenableBottomNavigationView) return attachedError("Invalid view $child, the app:targetBottomNavigation has to be n ListenableBottomNavigationView")
for (i in 0 until child.childCount) {
val subView = child.getChildAt(i)
if (subView is BottomNavigationMenuView) target = subView
}
if (SDK_INT >= LOLLIPOP) elevation = child.elevation
child.addOnNavigationItemSelectedListener { updateRectByIndex(it, true) }
post { updateRectByIndex(index, false) }
}
private fun attachedError(message: String) {
Timber.e(message)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
target = null
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.clipRect(rect)
backgroundDrawable.draw(canvas)
}
private fun updateRectByIndex(index: Int, animated: Boolean) {
this.index = index
target?.apply {
if (childCount < 1 || index >= childCount) return
val reference = getChildAt(index)
val start = reference.left + left
val end = reference.right + left
backgroundDrawable.setBounds(left, top, right, bottom)
val newRect = Rect(start, 0, end, height)
if (animated) startUpdateRectAnimation(newRect) else updateRect(newRect)
}
}
private fun startUpdateRectAnimation(rect: Rect) {
animator?.cancel()
animator = AnimatorSet().also {
it.playTogether(
ofInt(this, "rectLeft", this.rect.left, rect.left),
ofInt(this, "rectRight", this.rect.right, rect.right),
ofInt(this, "rectTop", this.rect.top, rect.top),
ofInt(this, "rectBottom", this.rect.bottom, rect.bottom)
)
it.interpolator = FastOutSlowInInterpolator()
it.duration = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
it.start()
}
}
private fun updateRect(rect: Rect) {
this.rect = rect
postInvalidate()
}
@Keep fun setRectLeft(left: Int) = updateRect(rect.apply { this.left = left })
@Keep fun setRectRight(right: Int) = updateRect(rect.apply { this.right = right })
@Keep fun setRectTop(top: Int) = updateRect(rect.apply { this.top = top })
@Keep fun setRectBottom(bottom: Int) = updateRect(rect.apply { this.bottom = bottom })
}
Я взял его из этого article https://medium.com/@ademar111190 / writing-a-custom-view-to-use-as-bottom-navigation-indicator-8cf63a737dab