Android鬼点子-不用百行代码,让文字闪闪发光

3,972 阅读2分钟

想做一个让文字有类似金属光泽效果的动画。效果如下:

图1
分享一下实现思路,使用kotlin实现。

需求是需要有很好的移植性,可以修改设置光泽颜色,光泽宽度,光泽速度,光泽角度。

最后的结果是只要一行代码即可调用:

start_bt.setOnClickListener {
            mTextView.setBling()
        }
stop_bt.setOnClickListener {
            mTextView.setBling(isStop = true)
        }

当然,也可以通过一些参数来设置一些效果mTextView.setBling(blingWidth = 80f,angle = 20f,speed = 6f)

这里的mTextView就是一个普通的TextView。我使用的是kotlin的扩展方法,对TextView进行了拓展,方便调用。

想要实现这种效果,我的思路是给paint设置LinearGradient。然后移动LinearGradient的位置就可以了。

第一步就是拿到TextView的paint,在TextView中找到private final TextPaint mTextPaint;这个mTextPaint就是用来绘制文字的。

但是mTextPaint是一个私有变量,不能访问到,于是我想通过反射拿到mTextPaint。

补充:感谢 @vi1zen 的留言,是有getPaint()方法可以拿到mTextPaint

代码如下:

//也可以使用getPaint()方法拿到mTextPaint
val class1 = Class.forName("android.widget.TextView")
val field = class1.getDeclaredField("mTextPaint")
field.isAccessible = true
var paint = field.get(this) as TextPaint

然后给mTextPaint设置LinearGradient:

this.context.runOnUiThread {
            paint.shader = lg
            this@blingText.postInvalidate()
        }

这里的lg,就是一个LinearGradient对象,设置完LinearGradient之后,呼叫重绘。

我用到LinearGradient是这样的: LinearGradient(0f + position, 0f, blingWidth + position, angle, textColor, blingColor, Shader.TileMode.MIRROR)

光泽的位置变化是通过position变量实现的,我用到了一个线程,并且把这个线程保存到tag里面。

完整代码如下,可以放置到项目中任意位置:

package top.greendami.blingview

import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Shader
import android.text.TextPaint
import android.widget.TextView
import org.jetbrains.anko.runOnUiThread

/**
 * Created by GreendaMi on 2018/1/16.
 */

//是否有动画
fun TextView.isBling(): Boolean {
    if (this.tag != null) {
        var mT = this.tag as Thread
        return mT.isAlive
    }
    return false
}


fun TextView.setBling(textColor: Int = Color.BLACK, blingColor: Int = Color.WHITE, blingWidth: Float = this.textSize, isStop: Boolean = false, speed: Float = 4f,angle :Float = 0f) {
    var position = 0f
    //如果已经有发光效果
    if (this.tag != null) {
        var mT = this.tag as Thread
        mT.interrupt()
        this.tag = null
        blingText(null)
    }
    if (isStop) {
        return
    }
    var mThread = Thread(Runnable {
        while (!Thread.currentThread().isInterrupted) {
            //下一帧
            position += speed
            blingText(LinearGradient(0f + position, 0f, blingWidth + position, angle, textColor, blingColor, Shader.TileMode.MIRROR))
            try {
                //延迟
                Thread.sleep(100)
            } catch (ex: InterruptedException) {
                Thread.currentThread().interrupt()
            }
        }
    })
    this.tag = mThread
    mThread.start()
}

private fun TextView.blingText( lg: LinearGradient?) {
    try {
        val class1 = Class.forName("android.widget.TextView")
        val field = class1.getDeclaredField("mTextPaint")
        field.isAccessible = true
        //拿到画笔对象
        var paint = field.get(this) as TextPaint

        this.context.runOnUiThread {
            //设置LinearGradient
            paint.shader = lg
            //呼叫重绘
            this@blingText.postInvalidate()
        }

    } catch (e: Exception) {
        e.printStackTrace()
    }
}

为了方便我用了anko,implementation "org.jetbrains.anko:anko-commons:0.10.4"

代码github

大兄弟,都看到这里了,不点个赞嘛?评论666,有彩蛋!