仿小红书轮播图指示器
·
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.content.res.Resources
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import kotlin.math.roundToInt
class DotIndicatorView @JvmOverloads constructor(
context: Context, attr: AttributeSet?, def: Int = 0
) : LinearLayout(context, attr, def) {
companion object {
private const val DOT_MAX_SIZE = 5
}
private val dotSizeSelected = 10.dp
private val dotSizeNormal = 5.dp
private val dotSizeMiddle = 5.dp
private val dotSizeSmall = 3.dp
private val dotMargin = 5.dp
private var curSelected = 0
private val dotList = arrayListOf<ImageView>()
private val dotSize
get() = dotList.size
init {
this.gravity = Gravity.CENTER_VERTICAL
}
fun setCount(count: Int) {
if (count <= 1) {
visibility = View.GONE
return
}
visibility = View.VISIBLE
removeAllViews()
dotList.clear()
val width = if (count >= DOT_MAX_SIZE) {
dotSizeSelected + (DOT_MAX_SIZE - 1) * dotSizeNormal + (DOT_MAX_SIZE - 1) * dotMargin
} else {
dotSizeSelected + (count - 1) * dotSizeNormal + (count - 1) * dotMargin
}
layoutParams.width = width
Log.d("DotIndicatorView", "setCount: $count, width: $width")
for (i in 0 until count) {
val dot = createDotView(i)
addView(dot)
dotList.add(dot)
}
setDotSelected(0)
if (count <= DOT_MAX_SIZE) return
setDotSmall(DOT_MAX_SIZE - 1)
setDotMiddle(DOT_MAX_SIZE - 2)
}
fun setSelectedIndex(index: Int) {
Log.d("DotIndicatorView", "setSelectedIndex: $index")
if (index == curSelected) return
if (index !in 0 until dotSize) {
return
}
if (index == 0) {
setCount(dotSize)
return
}
dotList.forEachIndexed { i, _ ->
setDotNormal(i)
}
when (index) {
1, 2 -> { // 前三个.
setDotSmall(DOT_MAX_SIZE - 1)
setDotMiddle(DOT_MAX_SIZE - 2)
}
dotSize - 1, dotSize - 2, dotSize - 3 -> { // 倒数三个.
setDotSmall(dotSize - DOT_MAX_SIZE)
setDotMiddle(dotSize - DOT_MAX_SIZE + 1)
}
else -> { // 中间部分.
setDotSmall(index - 2)
if (index + 2 < dotSize) {
setDotSmall(index + 2)
}
}
}
setDotSelected(index)
this.post {
translationDot(getTranslationX(index))
}
}
private fun getTranslationX(selectedIndex: Int): Int {
val ivSelected = dotList[selectedIndex]
val selectedDotCenterX = ivSelected.x + ivSelected.width / 2f
val targetX: Float = when (selectedIndex) {
0 -> selectedDotCenterX
1 -> dotSizeNormal + dotMargin + ivSelected.width / 2f
2 -> dotSizeNormal * 2 + dotMargin * 2 + ivSelected.width / 2f
dotSize - 2 -> this.width - dotSizeNormal - dotMargin - ivSelected.width / 2f
dotSize - 1 -> this.width - ivSelected.width / 2f
else -> this.width / 2f
}
return (targetX - selectedDotCenterX).toInt()
}
private fun setDotSelected(index: Int) {
curSelected = index
dotList.getOrNull(index)?.apply {
setImageResource(R.drawable.shape_selected)
layoutParams = layoutParams.apply { width = dotSizeSelected; height = dotSizeNormal }
}
}
private fun setDotSmall(index: Int) {
if (dotSize <= DOT_MAX_SIZE) {
return
}
dotList.getOrNull(index)?.apply {
setImageResource(R.drawable.shape_small)
layoutParams = layoutParams.apply { width = dotSizeSmall;height = dotSizeSmall }
}
}
private fun setDotMiddle(index: Int) {
if (dotSize <= DOT_MAX_SIZE) {
return
}
dotList.getOrNull(index)?.apply {
setImageResource(R.drawable.shape_middle)
layoutParams = layoutParams.apply { width = dotSizeMiddle;height = dotSizeMiddle }
}
}
private fun setDotNormal(index: Int) {
dotList.getOrNull(index)?.apply {
setImageResource(R.drawable.shape_normal)
layoutParams = layoutParams.apply { width = dotSizeNormal;height = dotSizeNormal }
}
}
private fun createDotView(index: Int): ImageView {
val lp = LayoutParams(dotSizeNormal, dotSizeNormal)
if (index > 0) {
lp.setMargins(dotMargin, 0, 0, 0)
}
val imageView = ImageView(context)
imageView.setImageResource(R.drawable.shape_normal)
imageView.layoutParams = lp
return imageView
}
private fun translationDot(dx: Int) {
val animatorSet = AnimatorSet()
dotList.forEach { vDot ->
animatorSet.playTogether(
ObjectAnimator.ofFloat(
vDot,
"translationX",
vDot.translationX,
vDot.translationX + dx
)
)
}
animatorSet.duration = 200L
animatorSet.start()
}
}
inline val Number.dpFloat
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
toFloat(),
Resources.getSystem().displayMetrics
)
inline val Number.dp
get() = dpFloat.roundToInt()
更多推荐



所有评论(0)