class ChangeColorTextView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet,
defStyle: Int = 0
) : AppCompatTextView(context, attributeSet, defStyle) {
private var mNormalPaint = TextPaint()
private var mSelectedPaint = TextPaint()
private var mNormalColor = Color.WHITE
private var mSelectedColor = Color.RED
private val rectF = Rect()
private val rectText = Rect()
private var mCurProgress = 0f
private var mDirection:Direction = Direction.Right_To_Left
enum class Direction {
Left_To_Right,
Right_To_Left
}
init {
//自定义属性获取
val ta = context.obtainStyledAttributes(attributeSet, R.styleable.ChangeColorTextView)
mNormalColor = ta.getColor(R.styleable.ChangeColorTextView_normalColor, mNormalColor)
mSelectedColor = ta.getColor(R.styleable.ChangeColorTextView_selectedColor, mSelectedColor)
ta.recycle()
getPaintByParams(mNormalPaint,mNormalColor)
getPaintByParams(mSelectedPaint,mSelectedColor)
}
private fun getPaintByParams(paint: Paint,color:Int) {
paint.isAntiAlias = true
paint.isDither = true
paint.textSize = textSize
paint.color = color
}
override fun onDraw(canvas: Canvas) {
//这里需要我们自己 canvas
// super.onDraw(canvas)
//从左到右
if (mDirection == Direction.Left_To_Right) {
drawContent(canvas,mNormalPaint, (mCurProgress*width).toInt(),width)
drawContent(canvas,mSelectedPaint,0, (mCurProgress*width).toInt())
}else{//从右到左
drawContent(canvas,mNormalPaint,0, (width-mCurProgress*width).toInt())
drawContent(canvas,mSelectedPaint, (width-mCurProgress*width).toInt(),width)
}
}
private fun drawContent(canvas: Canvas, paint: TextPaint, left:Int, right: Int) {
canvas.save()
rectF.left = left
rectF.top = 0
rectF.right = right
rectF.bottom = height
//主要使用这个属性来完成”着色“
canvas.clipRect(rectF)
var fontMetricsInt = paint.fontMetricsInt
//获取文字基线
var vy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom
var baseLine = vy + height / 2
paint.getTextBounds(text.toString(), 0, text.length, rectText)
//文字起始位置
var startX = (width - rectText.width()) / 2
canvas.drawText(text.toString(), startX.toFloat(), baseLine.toFloat(), paint)
canvas.restore()
}
fun setCurProgress(progress:Float){
mCurProgress = progress
invalidate()
}
fun setDirection(direction: Direction) {
mDirection = direction
}
fun setChangeColor(color:Int){
mSelectedPaint.color = color
}
fun setNormalColor(color: Int) {
mNormalPaint.color = color
}
}
实现原理:
因为一种是正常颜色,一种是选中颜色,所以这里我们要同时绘制两种颜色的文字,就如代码所示,根据滑动的进度同时去绘制两种文字。实现这个效果主要考察的是
canvas.clipRect()
方法的理解和使用,下面展示的就是clipRect()
的效果
clipRect()方法效果展示
class TabLayoutActivity : AppCompatActivity() {
private val mTitles =
arrayOf("关注", "推荐", "合肥", "热点", "幸福里", "电影", "生活", "影视", "足球", "篮球", "乒乓球", "排球", "羽毛球")
private val mFragments = ArrayList<Fragment>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityTablayoutBinding>(
this,
R.layout.activity_tablayout
)
mFragments.add(TabFragment.getInstance(mTitles[0]))
mFragments.add(TabFragment.getInstance(mTitles[1]))
mFragments.add(TabFragment.getInstance(mTitles[2]))
mFragments.add(TabFragment.getInstance(mTitles[3]))
mFragments.add(TabFragment.getInstance(mTitles[4]))
mFragments.add(TabFragment.getInstance(mTitles[5]))
mFragments.add(TabFragment.getInstance(mTitles[6]))
mFragments.add(TabFragment.getInstance(mTitles[7]))
mFragments.add(TabFragment.getInstance(mTitles[8]))
mFragments.add(TabFragment.getInstance(mTitles[9]))
mFragments.add(TabFragment.getInstance(mTitles[10]))
mFragments.add(TabFragment.getInstance(mTitles[11]))
mFragments.add(TabFragment.getInstance(mTitles[12]))
binding.tabLayout.setupWithViewPager(binding.vp)
binding.vp.adapter = VpAdapter(supportFragmentManager, mFragments)
val count = (binding.vp.adapter as VpAdapter).count
//动态修改 tab 为我们自定的 textview
for (i in 0 until count) {
val tab = binding.tabLayout.getTabAt(i)
val view = LayoutInflater.from(applicationContext).inflate(R.layout.item_tab, null)
val textView = view.findViewById<ChangeColorTextView>(R.id.changeTextColor)
textView?.text = mTitles[i]
tab?.customView = textView
}
//是不是点击,如果是,直接 setCurrentItem = curPosition
var isClickTab = false
//点击后,恢复上一个 position 文字为正常颜色
var prePosition = 0
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab) {
prePosition = tab.position
}
override fun onTabSelected(tab: TabLayout.Tab) {
isClickTab = true
//点击直接设置文字颜色
(tab.customView as ChangeColorTextView).setCurProgress(1f)
binding.vp.currentItem = tab.position
}
})
binding.vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
when (state) {
SCROLL_STATE_IDLE -> {
}
SCROLL_STATE_DRAGGING -> {
//当手动触发滑动时,需要重置
isClickTab = false
}
SCROLL_STATE_SETTLING -> {
}
}
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
if (!isClickTab) {
isClickTab = false
val curTab =
binding.tabLayout.getTabAt(position)?.customView as ChangeColorTextView
curTab?.setDirection(ChangeColorTextView.Direction.Right_To_Left)
curTab?.setCurProgress(1 - positionOffset)
try {
val nextTab =
binding.tabLayout.getTabAt(position + 1)?.customView as ChangeColorTextView
nextTab?.setDirection(ChangeColorTextView.Direction.Left_To_Right)
nextTab?.setCurProgress(positionOffset)
} catch (e: Exception) {
}
}
if (isClickTab) {
val tab =
binding.tabLayout.getTabAt(prePosition)?.customView as ChangeColorTextView
//重置上一个 position 文字的颜色
tab.setCurProgress(0f)
}
}
override fun onPageSelected(position: Int) {
}
})
}
}
到这里就结束了,具体实现和使用就这么多,我在关键的地方都写了注释,理解起来应该不太难。