天共水,水远与天连
天净水平寒月漾,水光月色两相兼

简单展示一下动画效果:

项目地址在此,大家若是喜欢的话,不妨点个赞吧

好了,简单阐述一下本次动画的原理:

光的效果使用Paint设置Shader来实现,具体则是LinearGradient水平渐变渲染。
光影的平移依赖于LinearGradientsetLocalMatrix,通过Matrixtranslate来促使光影移动。

在自定义View的onSizeChanged(int w, int h, int oldw, int oldh)方法内初始化引擎:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
tryInitEngine(w);
}

private void tryInitEngine(int w) {
if (mShadowMatrix == null) {
if (w > 0) {
//控制阴影的Matrix,通过Matrix的变化来实现闪光的滑过效果
mShadowMatrix = new Matrix();
//因为使用了LinearGradient,所以Paint本身的color将毫无意义,所以colors的起始点的色值必须和本来色值一致
int currentTextColor = getCurrentTextColor();
//渐变色层.x0,y0是起点坐标,x1,y1是终点坐标
mLinearGradient = new LinearGradient(0, 0, 50, 0, new int[] {currentTextColor, Color.GREEN, currentTextColor},
null, Shader.TileMode.CLAMP);
//画笔设置Shader
getPaint().setShader(mLinearGradient);
//使用属性动画作为引擎,数值从-SHADOW变化到TextView本身的宽度。间隔时间未1500ms
mValueAnimator = ValueAnimator.ofFloat(-50, w).setDuration(1500);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
//Matrix移动来实现闪光滑动
mShadowMatrix.setTranslate(value, 0);
invalidate();
}
});
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
mShadowMatrix.reset();
}

@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mShadowMatrix.reset();
}
});
mValueAnimator.setRepeatCount(mRepeatCount);
}
}
}
  • 简要说明几个重要变量:
    • mShadowMatrix,用来控制Shader位置的Matrix
    • mLinearGradient,实现闪光效果的Shader,水平渐变层。
    • mValueAnimator,属性动画引擎。
这里面使用了一个LinearGradient线性渐变的着色器。着重说一下它的使用方法。

先看一下LinearGradient的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 /** Create a shader that draws a linear gradient along a line. 
@param x0 The x-coordinate for the start of the gradient line
@param y0 The y-coordinate for the start of the gradient line
@param x1 The x-coordinate for the end of the gradient line
@param y1 The y-coordinate for the end of the gradient line
@param colors The colors to be distributed along the gradient line
@param positions May be null. The relative positions [0..1] of
each corresponding color in the colors array. If this is null,
the the colors are distributed evenly along the gradient line.
@param tile The Shader tiling mode
*/
public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
TileMode tile) {
.........
.....
......
}

这其中:
x0,y0—->代表起点的坐标
x1,y1—->代表终点的坐标
colors—->colors表示渲染的颜色,它是一个颜色数组,数组长度必须大于等于2
positions—->positions表示colors数组中几个颜色的相对位置,是一个float类型的数组,该数组的长度必须与colors数组的长度相同。如果这个参数使用null也可以,这时系统会按照梯度线来均匀分配colors数组中的颜色
title—->代表了系统提供的几种渲染模式,这里选用了LinearGradient.TileMode.CLAMP模式,表示重复colors数组里的最后一种颜色直到该View结束的地方

这里我们来做个试验,将colors的最后一个色值改为Color.BLUE

1
mLinearGradient = new LinearGradient(0, 0, SHADOW_W, 0, new int[] {currentTextColor, Color.GREEN, Color.BLUE},

OK,看一下试验效果.

可见设置了ClAMP模式后,的确是将colors最后的一个色值覆盖到了未渲染区域

监听属性动画的数值更新,来触发重绘,OnDraw()方法实现尤为简单。

1
2
3
4
5
6
7
8
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "onDraw: " + System.currentTimeMillis());
if (mLinearGradient != null) {
mLinearGradient.setLocalMatrix(mShadowMatrix);
}
}
  • Matrix,改变位置即可实现光芒滑过效果

以上就是本次动画的简要原理阐述,项目地址在这里,个中原理已在注释上写的明明白白。FlickerTextView,大家喜欢的话不妨点个赞吧。

基于需求的特殊性,本地动画使用了TextView。其实大可不必拘泥于此,本质上还是对Paint设置Shader的使用而已。