先放上效果圖, 因簡(jiǎn)書不支持webp格式的圖片,如上傳gif圖片太大而無(wú)法顯示,可移步掘金或GitHub查看動(dòng)態(tài)效果圖。
仿知乎日?qǐng)?bào)無(wú)限輪播圖+指示符切換動(dòng)畫
動(dòng)畫分析
- 未選中頁(yè)面對(duì)應(yīng)的指示符寬度小,形狀為圓形,選中頁(yè)面對(duì)應(yīng)的指示符寬度大,形狀為橢圓。
- 當(dāng)頁(yè)面A->B,A頁(yè)面對(duì)應(yīng)的指示符寬度由大->小,B頁(yè)面對(duì)應(yīng)的指示符寬度由小->大,指示符寬度和顏色隨滑動(dòng)而不斷變化。
- 當(dāng)從最后一個(gè)頁(yè)面手指向右滑動(dòng),頁(yè)面向左時(shí),最后一個(gè)頁(yè)面對(duì)應(yīng)的指示符寬度由大->小,第一個(gè)頁(yè)面對(duì)應(yīng)的指示符寬度由小->大,其余指示符向右平移,有聯(lián)動(dòng)效果。
- 當(dāng)從第一個(gè)頁(yè)面手指向左滑動(dòng),頁(yè)面向右時(shí),第一個(gè)頁(yè)面對(duì)應(yīng)的指示符寬度由大->小,最后一個(gè)對(duì)應(yīng)的指示符寬度由小->大,其余指示符向左平移,有聯(lián)動(dòng)效果。
代碼實(shí)現(xiàn)
TKBanner
實(shí)現(xiàn)無(wú)限輪播圖功能,默認(rèn)支持圓點(diǎn)和數(shù)字指示符。
仿知乎日?qǐng)?bào)APP輪播圖 | 仿品玩APP輪播圖 | 仿虎嗅APP輪播圖 |
---|---|---|
CuteIndicator
自定義View
,實(shí)現(xiàn)ViewPager
頁(yè)面滑動(dòng)過程中指示符切換動(dòng)畫效果。
相關(guān)屬性
屬性 | 說明 | 默認(rèn)值 |
---|---|---|
IndicatorColor | 未選中頁(yè)面對(duì)應(yīng)的指示符顏色 | Color.GRAY |
IndicatorSelectedColor | 選中頁(yè)面對(duì)應(yīng)的指示符顏色 | Color.WHITE |
IndicatorWidth | 未選中頁(yè)面對(duì)應(yīng)的指示符寬度 | 5dp |
IndicatorSelectedWidth | 選中頁(yè)面對(duì)應(yīng)的指示符寬度 | 20dp |
IndicatorHeight | 指示符高度 | 5dp |
IndicatorMargin | 指示符之間的間隔 | 5dp |
IndicatorShowAnimation | 是否顯示指示符切換動(dòng)畫 | true |
關(guān)鍵代碼
- 覆蓋
onMeasure
方法,計(jì)算設(shè)置指示符寬度和高度。目前沒有根據(jù)測(cè)量模式去判斷計(jì)算,簡(jiǎn)單的計(jì)算指示符總寬度=(指示符數(shù)量-1)*(未選中指示符寬度+指示符之間的間隔)+選中指示符的寬度,此處還有很多優(yōu)化空間。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if(mIndicatorCount>0) {
val width = ((mIndicatorCount - 1) * (mIndicatorMargin + mIndicatorWidth) + mIndicatorSelectedWidth).toInt()
setMeasuredDimension(width, mIndicatorHeight.toInt())
}
}
- 覆蓋
onDraw
方法,繪制指示符,針對(duì)第一個(gè)頁(yè)面和最后一個(gè)頁(yè)面滑動(dòng)時(shí)特別處理。
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (mIndicatorCount <= 0) {
return
}
var left=0f
var right=0f
if (position == (mIndicatorCount - 1) && positionOffset > 0f) {
for (i in 0 until mIndicatorCount) {
if(i==0){
left=0f
right=left+mIndicatorWidth+(mIndicatorSelectedWidth - mIndicatorWidth) * positionOffset
mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, positionOffset)
}
else if(i<position){
right=left+mIndicatorWidth
mIndicatorPaint.color = mIndicatorColor
}
else if(i==position){
right=i*(mIndicatorWidth+mIndicatorMargin)+mIndicatorSelectedWidth
mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, 1-positionOffset)
}
canvas.drawRoundRect(RectF(left, 0f, right, mIndicatorHeight), mIndicatorWidth / 2, mIndicatorWidth / 2, mIndicatorPaint)
left=right+mIndicatorMargin
}
} else {
for (i in 0 until mIndicatorCount) {
if (i < position) {
left = i * (mIndicatorWidth + mIndicatorMargin)
right = left + mIndicatorWidth
mIndicatorPaint.color = mIndicatorColor
} else if (i == position) {
left = i * (mIndicatorWidth + mIndicatorMargin)
right = left + mIndicatorWidth + (mIndicatorSelectedWidth - mIndicatorWidth) * (1 - positionOffset)
mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, 1 - positionOffset)
} else if (i == (position + 1)) {
left = (i - 1) * (mIndicatorMargin + mIndicatorWidth) + mIndicatorWidth + (mIndicatorSelectedWidth - mIndicatorWidth) * (1 - positionOffset) + mIndicatorMargin
right = i * (mIndicatorMargin + mIndicatorWidth) + mIndicatorSelectedWidth
mIndicatorPaint.color = ColorUtils.blendARGB(mIndicatorColor, mIndicatorSelectedColor, positionOffset)
} else {
left = (i - 1) * (mIndicatorWidth + mIndicatorMargin) + (mIndicatorSelectedWidth + mIndicatorMargin)
right = left + mIndicatorWidth
mIndicatorPaint.color = mIndicatorColor
}
canvas.drawRoundRect(RectF(left, 0f, right, mIndicatorHeight), mIndicatorWidth / 2, mIndicatorWidth / 2, mIndicatorPaint)
}
}
}
-
setUp
方法實(shí)現(xiàn)ViewPager
和Indicator
綁定
fun setUp(count:Int) {
mIndicatorCount = count
requestLayout()
}
GitHub
完整的代碼可以在GitHub上獲取,喜歡的可以考慮給我點(diǎn)個(gè)贊^
https://github.com/kongpf8848/ViewWorld