🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] 2022年3月18日16:18:39 # 1. 前言 在Shader一节中提到了: > Shader 类其实是一个空类,它的功能主要是靠它的派生类来实现的,有BitmapShader、ComposeShader、LinearGradient、RadialGradient、SweepGradient。 Shader的子类LinearGradient可用于实现线性渐变效果。其构造函数: ~~~ public LinearGradient(float x0, float y0, float x1, float y1, @ColorLong long color0, @ColorLong long color1, @NonNull TileMode tile) ~~~ 也就是完成从点(x0,y0)到(x1,x2)的线性渐变,颜色为从color0到color1,TileMode与 BitmapShader 一样,用于**指定当控件区域大于指定的渐变区域时, 空白区域的颜色填充模式**。当然如果需要多种颜色的渐变,就可以传入一个数组,使用下面构造函数: ~~~ LinearGradient(     x0: Float,     y0: Float,     x1: Float,     y1: Float,     colors: IntArray,     positions: FloatArray?,     tile: Shader.TileMode) ~~~ * positions\[\]与渐变的颜色相对应,取值是 0~1 的 Float 类型数据,表示每种颜色在整条 渐变线中的百分比位置。 # 2. 案例 ## 2.1 两种颜色线性渐变 ~~~ private lateinit var mPaint: Paint private var mHeight: Int = 300 private var mWidth: Int = 900 private lateinit var linearGradient: LinearGradient private fun init() { mPaint = Paint() mPaint.color = Color.GRAY // 黑色 mPaint.style = Paint.Style.FILL // 不填充 mPaint.isAntiAlias = true // 抗锯齿 mPaint.isDither = true // 防抖动 // 设置线性渐变器 linearGradient = LinearGradient( 0f, mHeight / 2f, mWidth.toFloat(), mHeight / 2f, Color.RED, Color.BLUE, Shader.TileMode.CLAMP ) // 关闭硬件加速 setLayerType(LAYER_TYPE_SOFTWARE, null) } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas?.apply { // 通过setShader来传入线性渐变器 mPaint.setShader(linearGradient) // 绘制矩形区域 drawRect(0f, 0f, mWidth.toFloat(), mHeight.toFloat(), mPaint) } } ~~~ 效果: ![](https://img.kancloud.cn/7d/fd/7dfdfec43940a95ee0c3a17833edcafe_766x262.png) ## 2.2 多种颜色线性渐变 也就是对应的修改一下数据: ~~~ val colors = intArrayOf( 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xffffff00.toInt() ) val positions = floatArrayOf(0f, 0.5f, 1f, 0.2f) linearGradient = LinearGradient( 0f, mHeight / 2f, mWidth.toFloat(), mHeight / 2f, colors, positions, Shader.TileMode.CLAMP ) ~~~ 其余和上面保持一致,效果: ![](https://img.kancloud.cn/36/ee/36ee0dad0449e59ba9a0ac62c847da9f_744x255.png) ## 2.3 将渐变应用在字体上 比如简单在onDraw方法中修改为: ~~~ private fun init() { mPaint = Paint() mPaint.color = Color.GRAY // 黑色 mPaint.style = Paint.Style.FILL // 不填充 mPaint.isAntiAlias = true // 抗锯齿 mPaint.isDither = true // 防抖动 val colors = intArrayOf( 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xffffff00.toInt() ) val positions = floatArrayOf(0f, 0.5f, 1f, 0.2f) linearGradient = LinearGradient( 0f, mHeight / 2f, mWidth.toFloat(), mHeight / 2f, colors, positions, Shader.TileMode.CLAMP ) // 关闭硬件加速 setLayerType(LAYER_TYPE_SOFTWARE, null) } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas?.apply { // 通过setShader来传入线性渐变器 mPaint.setShader(linearGradient) // 绘制矩形区域 // drawRect(0f, 0f, mWidth.toFloat(), mHeight.toFloat(), mPaint) mPaint.textSize = Tool.dp2px(resources, 30f) drawText("在文本上应用色彩渐变效果", 20f, 200f, mPaint) } } ~~~ 结果: ![](https://img.kancloud.cn/f0/fd/f0fd83ce57aad2585b56ae8bb62a5e90_1031x173.png) ## 2.4 闪光文字效果 在《Android自定义控件开发入门与实战》一书中提到可以在在文字中间不断滚动一个闪光条来实现这个效果,动态效果挺好看的,作者给出了二维码: ![](https://img.kancloud.cn/cc/fc/ccfc394f665dd5d0cf93cab85dc64b08_245x201.png) ~~~ class ShimmerTextView : AppCompatTextView { constructor(context: Context?) : super(context) { init() } constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { init() } constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( context, attrs, defStyleAttr ) { init() } private lateinit var mPaint: Paint private lateinit var mMatrix: Matrix private lateinit var mLinearGradient: LinearGradient private var mFontSize: Float = Tool.dp2px(resources, 24f) private var mAnimatedValue: Float = 0f /** * 初始化 */ private fun init() { // 这里的画笔不再是自己new的,而是从父控件中获取 mPaint = paint paint.color = Color.BLACK // 黑色 paint.style = Paint.Style.FILL // 不填充 paint.isAntiAlias = true // 抗锯齿 paint.isDither = true // 防抖动 paint.textSize = mFontSize // 字体大小 paint.strokeWidth = 5f // 画笔宽度 // 测量字体大小 val textLength = mPaint.measureText(text.toString()) // 借助fontMetrics来计算字体高度 val fontMetrics = mPaint.fontMetrics val textHeight = fontMetrics.bottom - fontMetrics.top // 根据字体的大小创建线性渐变 createLinearGradient(textLength, textHeight) // 动画效果 createAnim(textLength) // 变换矩阵 mMatrix = Matrix() } /** * 根据文本长度来创建动画效果 */ private fun createAnim(textLength: Float) { val animator = ValueAnimator.ofFloat(-textLength, textLength) animator.repeatCount = ValueAnimator.INFINITE animator.repeatMode = ValueAnimator.RESTART animator.duration = 2000 animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener { override fun onAnimationUpdate(animation: ValueAnimator?) { // 获取值 mAnimatedValue = animator.animatedValue as Float invalidate() } }) animator.start() } /** * 根据字体的大小来创建对应区域的线性渐变 * @param width 字体宽度 * @param height 字体高度 */ private fun createLinearGradient(width: Float, height: Float) { println("textWidth: $width, textHeight: $height") val colors = intArrayOf( currentTextColor, 0xFF018786.toInt(), currentTextColor ) val positions = floatArrayOf(0.3f, 1f, 0.2f) mLinearGradient = LinearGradient( 0f, height / 2f, width, height / 2f, colors, positions, Shader.TileMode.CLAMP ) } override fun onDraw(canvas: Canvas?) { canvas?.apply { mMatrix.setTranslate(mAnimatedValue, 0f) mLinearGradient.setLocalMatrix(mMatrix) mPaint.setShader(mLinearGradient) super.onDraw(canvas) } } } ~~~ 在这个案例中,需要注意的是因为我们使用的是继承自TextView,而其内部定义了画笔的一系列样式,且我们最终需要完成通过画笔来设置Shaer传入线性渐变的这个过程。故而这里是获取自带画笔。且在这个案例中,使用了Matrix来完成对后面背景的移动效果。 所以这个案例的重点其实在于这两行代码: ~~~ mLinearGradient.setLocalMatrix(mMatrix) mPaint.setShader(mLinearGradient) ~~~ 所以在接下来就来继续学习Matrix的变换效果。