Android自定義超級炫酷的ViewPage指示器
思路
其實主要的內容就是自定義一個帶顏色漸變的一個TextView,需要定義兩個畫筆,一個負責繪制未選中顏色,一個負責繪制選中時的顏色,各自繪制各自的區(qū)域就能繪制出一個帶顏色漸變的TextView
然后外部再包裝一層LinearLayout,水平排放每個tab,再監(jiān)聽Viewpage的滑動,根據滑動百分比來改變TextView的選中以及未選中區(qū)域的大小三,代碼實現(xiàn)
難點
繪制漸變色的TextView其實最重要的還是計算基線的位置,基線位置計算出來,那么就能調用drawText()方法去繪制我們的文本了,基線計算方式參考文末
代碼實現(xiàn)漸變TextView
@RequiresApi(Build.VERSION_CODES.Q)
class CustomTextView : AppCompatTextView {
/**
* 未選中畫筆
*/
private var mUnSelectPaint: Paint? = null
/**
* 選中畫筆
*/
private var mSelectPaint: Paint? = null
/**
* 滑動進度百分比
*/
var mScrollProcess = 0f
/**
* 是否左邊滑動
*/
var mScrollToLeft = true
/**
* 文字縮放比例
*/
var mTextScale = 0.7f
/**
* 選中顏色以及未選中顏色
*/
private var mSelectColor = Color.parseColor("#8966FF")
private var mUnSelectColor = Color.parseColor("#DBC0FF")
constructor(context: Context) : this(context, null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(
context,
attributeSet,
defStyleAttr
) {
//初始化畫筆
mUnSelectPaint = Paint()
mUnSelectPaint?.apply {
color = mUnSelectColor
textSize = this@CustomTextView.textSize
isAntiAlias = true
isDither = true
}
mSelectPaint = Paint()
mSelectPaint?.apply {
color = mSelectColor
textSize = this@CustomTextView.textSize
isAntiAlias = true
isDither = true
}
}
/**
* 繪制內容
*/
override fun onDraw(canvas: Canvas?) {
val x = width * mScrollProcess
if (mScrollToLeft) {
drawText(canvas!!, mUnSelectPaint!!, x, width.toFloat())
drawText(canvas!!, mSelectPaint!!, 0f, x)
} else {
drawText(canvas!!, mUnSelectPaint!!, 0f, width - x)
drawText(canvas!!, mSelectPaint!!, width - x, width.toFloat())
}
}
/**
* 繪制文字
*/
private fun drawText(canvas: Canvas, paint: Paint, startX: Float, endX: Float) {
paint.textSize = textSize * mTextScale
//保存畫布,剪切畫布(設置畫布顯示區(qū)域)
canvas.save()
canvas.clipRect(startX, 0f, endX, height.toFloat())
//獲取文字矩形區(qū)域
var bounds = Rect()
paint.getTextBounds(text, 0, text.length, bounds)
//獲取文字的度量指標
val fontMetrics = paint.fontMetrics
//計算基線到中心點位置
val dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
//計算基線
val baseLine = height / 2 + dy
//繪制文字
canvas.drawText(
text.toString(),
(width / 2 - bounds.width() / 2).toFloat(),
baseLine,
paint
)
canvas.restore()
}
/**
* 設置選中 未選中顏色
*/
fun setColor(selectColor: Int, unSelectColor: Int) {
mSelectColor = selectColor
mUnSelectColor = unSelectColor
mSelectPaint?.color = selectColor
mUnSelectPaint?.color = unSelectColor
invalidate()
}
}1,在構造方法中初始化兩支畫筆,分別用于繪制未選中區(qū)域和選中區(qū)域
2,在onDraw()方法中根據滑動百分計算出繪制區(qū)域的寬度
3,判斷滑動方向
4,開始繪制文本
- 設置畫筆的文字大?。ㄎ淖执笮?* 縮放比例)
- 保存畫布,并根據計算出來的繪制區(qū)域的開始坐標和結束坐標剪切畫布,這是整個繪制核心,一旦剪切等下調用darwText()方法就只會繪制剪切的這部分
- 獲取文字矩形區(qū)域,通過調用paint.getTextBounds()這個方法就能拿到我們繪制文本的區(qū)域
- 獲取文字的度量指標,計算出基線的位置
- 調用畫布的drawText()去繪制我們的文本
5,效果如下,對算法不理解的需要自行去搜索getTextBounds(),getFontMetrics()這兩個方法的作用及其屬性變量的作用,到這里,一個漸變色的TextView就出來了,最后容我埋下一個bug

代碼實現(xiàn)指示器
@RequiresApi(Build.VERSION_CODES.Q)
class MyIndicator : LinearLayout, ViewPager.OnPageChangeListener {
var mTextScale = 0.7f
constructor(context: Context) : this(context, null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(
context,
attributeSet,
defStyleAttr
)
/**
* 添加多個tab
*/
fun addTabs(texts: List<String>) {
texts.forEach {
addTab(it)
}
//選中第一個
val customTextView = getChildAt(0) as CustomTextView
customTextView.mScrollToLeft = false
customTextView.mScrollProcess = 1f
customTextView.mTextScale = 1f
customTextView.invalidate()
}
/**
* 添加tab
*/
fun addTab(text: String) {
val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)
layoutParams.weight = 1f
val customTextView = CustomTextView(context)
customTextView.textSize = 21f
customTextView.gravity = Gravity.CENTER
customTextView.text = text
customTextView.mScrollProcess = 0f
customTextView.mTextScale = 0.7f
customTextView.layoutParams = layoutParams
addView(customTextView)
}
/**
* 關聯(lián)ViewPage
*/
fun relationViewPage(viewPage: ViewPager) {
viewPage.addOnPageChangeListener(this)
}
/**
* 滑動回調
*/
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if (positionOffset > 0) {
val tabView = getChildAt(position) as CustomTextView
tabView.mScrollToLeft = false
tabView.mScrollProcess = 1 - positionOffset
tabView.mTextScale = if ((1 - positionOffset) > mTextScale) 1 - positionOffset else mTextScale
tabView.invalidate()
val rightTabView = getChildAt(position + 1) as CustomTextView
rightTabView.mScrollToLeft = true
rightTabView.mScrollProcess = positionOffset
rightTabView.mTextScale = if (positionOffset > mTextScale) positionOffset else mTextScale
rightTabView.invalidate()
}
}
/**
* 選中某個item回調
*/
override fun onPageSelected(position: Int) {
}
/**
* 滑動狀態(tài)改變回調
*/
override fun onPageScrollStateChanged(state: Int) {
}
}1,循環(huán)添加多個顏色漸變的CustomTextView,默認選中第一個
2,關聯(lián)ViewPage,給ViewPage設置一個OnPageChangeListener監(jiān)聽器,監(jiān)聽其滑動距離,根據滑動百分比計算出CustomTextView的滑動比例,CustomTextView的文字縮放比例
3,效果如下,如有不理解的版代碼過去自行改改參數就理解其中奧秘了,動起來


基線計算方式
在自定義View的過程中canvas.drawText(mText,0,y,mPaint);很容易出現(xiàn)字體不能完全漏出的問題,y的值其實不是距離畫布的距離,這個y是基準線的距離,所以在繪制的過程中一定要求得正確的基準線。所以在draw的過程中首先要計算好基準線的y

Paint.FontMetricsInt fontMetrics = p.getFontMetricsInt();
p.descent, //底部文本的最低點距離基準線的y值(正數)
p.ascent, //頂部文本的最高點距離及基準線的y值(負數)
到此這篇關于Android自定義超級炫酷的ViewPage指示器的文章就介紹到這了,更多相關Android ViewPage內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
AndroidStudio中AVD虛擬機設備空間不足調試過程出現(xiàn)的黑屏問題及解決方案
這篇文章主要介紹了解決AndroidStudio中AVD虛擬機設備空間不足調試過程出現(xiàn)的黑屏問題,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
Android實現(xiàn)可瀏覽和搜索的聯(lián)系人列表
這篇文章主要為大家詳細介紹了Android實現(xiàn)可瀏覽和搜索的聯(lián)系人列表的相關代碼,瀏覽所有聯(lián)系人和根據名稱搜索聯(lián)系人,感興趣的小伙伴們可以參考一下2016-07-07
Android自定義View onDraw()方法會調用兩次的問題解決
這篇文章主要介紹了Android自定義View onDraw()方法會調用兩次的問題解決,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-01-01

