Android 流光动画和流光字体实现详解
在 Android 开发中,自定义 View 是实现复杂 UI 效果的核心手段。流光动画(Flowing Light Animation)和流光字体(Shine Text Effect)是常见的视觉增强方案,常用于提升界面的科技感和交互反馈。本文将深入解析基于 Canvas、Paint、Shader 以及 ValueAnimator 实现这两种效果的原理与代码细节。
一、核心原理
1. 自定义 View 绘制流程
Android 的 View 系统通过 onDraw(Canvas canvas) 方法完成视图的绘制。开发者可以通过重写此方法,结合 Paint 对象来绘制图形、文字或应用着色器(Shader)。
2. 线性渐变(LinearGradient)
LinearGradient 是 Shader 的一种,用于定义颜色在两个或多个点之间的平滑过渡。通过动态改变渐变的起始坐标或矩阵变换,可以实现颜色的流动效果。
3. 属性动画(ValueAnimator)
ValueAnimator 用于生成随时间变化的数值序列。在流光效果中,通常用来驱动渐变色的位置偏移,配合 invalidate() 触发重绘,从而形成动画。
二、流光背景动画实现
流光背景通常表现为一个矩形区域内,有一道光带从左向右(或从右向左)循环移动。
1. 代码实现
/**
* 流光动画 View
*/
public class FlowingLightView extends View {
private Paint mPaint;
private Path mPath;
private LinearGradient mLinearGradient;
private ValueAnimator mValueAnimator;
public FlowingLightView(Context context) {
this(context, null);
}
public FlowingLightView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowingLightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void {
mPaint = ();
mPaint.setAntiAlias();
mPath = ();
}
{
(, );
(w, );
(w, h);
(, h);
mPath.moveTo(point1.x, point1.y);
mPath.lineTo(point2.x, point2.y);
mPath.lineTo(point3.x, point3.y);
mPath.lineTo(point4.x, point4.y);
mPath.close();
* h / w;
* w / ;
mValueAnimator = ValueAnimator.ofFloat( - offset/, w + offset/);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.setInterpolator( ());
mValueAnimator.setDuration();
mValueAnimator.addUpdateListener( .AnimatorUpdateListener() {
{
() animation.getAnimatedValue();
mLinearGradient = (value, k * value, value + , k * (value + ),
[]{Color.parseColor(), Color.parseColor(), Color.parseColor()},
, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
invalidate();
}
});
mValueAnimator.start();
}
{
.onMeasure(widthMeasureSpec, heightMeasureSpec);
MeasureSpec.getSize(widthMeasureSpec);
MeasureSpec.getSize(heightMeasureSpec);
(mValueAnimator == || !mValueAnimator.isRunning()) {
initPointAndAnimator(widthSize, heightSize);
}
}
{
.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
}
{
.onDetachedFromWindow();
(mValueAnimator != ) {
mValueAnimator.cancel();
mValueAnimator = ;
}
}
}


