Android 流光动画和流光字体实现详解
在 Android 开发中,自定义 View 是实现复杂 UI 效果的核心手段。流光动画(Flowing Light Animation)和流光字体(Shine Text Effect)是常见的视觉增强方案,常用于提升界面的科技感和交互反馈。本文将深入解析基于 Canvas、Paint、Shader 以及 ValueAnimator 实现这两种效果的原理与代码细节。
本文讲解了 Android 流光动画和流光字体的实现。通过自定义 View 结合 Canvas、Paint、Shader 及 ValueAnimator 技术,实现了背景流光效果和文字流光效果。重点分析了 LinearGradient 的坐标变换、Matrix 矩阵平移以及动画生命周期管理。提供了完整的 Java 代码示例,并给出了性能优化建议,如硬件加速和资源清理。适用于需要提升 UI 视觉质量的 Android 开发场景。

在 Android 开发中,自定义 View 是实现复杂 UI 效果的核心手段。流光动画(Flowing Light Animation)和流光字体(Shine Text Effect)是常见的视觉增强方案,常用于提升界面的科技感和交互反馈。本文将深入解析基于 Canvas、Paint、Shader 以及 ValueAnimator 实现这两种效果的原理与代码细节。
Android 的 View 系统通过 onDraw(Canvas canvas) 方法完成视图的绘制。开发者可以通过重写此方法,结合 Paint 对象来绘制图形、文字或应用着色器(Shader)。
LinearGradient 是 Shader 的一种,用于定义颜色在两个或多个点之间的平滑过渡。通过动态改变渐变的起始坐标或矩阵变换,可以实现颜色的流动效果。
ValueAnimator 用于生成随时间变化的数值序列。在流光效果中,通常用来驱动渐变色的位置偏移,配合 invalidate() 触发重绘,从而形成动画。
流光背景通常表现为一个矩形区域内,有一道光带从左向右(或从右向左)循环移动。
/**
* 流光动画 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 init() {
mPaint = new Paint();
mPaint.setAntiAlias(true); // 开启抗锯齿
mPath = new Path();
}
private void initPointAndAnimator(int w, int h) {
Point point1 = new Point(0, 0);
Point point2 = new Point(w, 0);
Point point3 = new Point(w, h);
Point point4 = new Point(0, 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();
// 斜率 k
float k = 0f * h / w;
// 偏移量,确保光影完全移出屏幕后再循环,避免闪烁
float offset = 1f * w / 8;
// 动画范围:从屏幕外左侧到屏幕外右侧
mValueAnimator = ValueAnimator.ofFloat(0f - offset/2, w + offset/2);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.setDuration(8000);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
// 创建新的渐变,位置随动画值变化
mLinearGradient = new LinearGradient(value, k * value, value + 2, k * (value + 2),
new int[]{Color.parseColor("#00FFE5EE"), Color.parseColor("#FFE5EE"), Color.parseColor("#00FFE5EE")},
null, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
invalidate();
}
});
mValueAnimator.start();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 注意:onMeasure 可能会被多次调用,需防止重复初始化
if (mValueAnimator == null || !mValueAnimator.isRunning()) {
initPointAndAnimator(widthSize, heightSize);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mValueAnimator != null) {
mValueAnimator.cancel();
mValueAnimator = null;
}
}
}
流光字体效果是在文字上叠加一层移动的渐变遮罩,使文字看起来像有光泽流动。
public class ShineTextView extends AppCompatTextView {
private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private Paint mPaint;
private int mViewWidth = 0;
private int mTranslate = 0;
public ShineTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0) {
mViewWidth = getMeasuredWidth();
if (mViewWidth > 0) {
mPaint = getPaint();
// 定义渐变颜色:紫红 -> 亮白 -> 紫红
mLinearGradient = new LinearGradient(
0, 0,
mViewWidth / 8, 0,
new int[]{0xFFFF5997, 0xFFFFE5EE, 0xFFFF5997},
null,
Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mGradientMatrix = new Matrix();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mGradientMatrix != null) {
// 移动渐变矩阵
mTranslate += mViewWidth / 10;
if (mTranslate > 1.5 * mViewWidth) {
mTranslate = -mViewWidth / 2;
}
mGradientMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
// 延迟重绘,控制动画速度
postInvalidateDelayed(100);
}
}
}
流光效果是 Android 自定义 View 中的经典案例,它结合了数学计算与图形渲染技术。掌握 Shader 与 Animator 的配合使用,能够显著提升应用的视觉质感。在实际项目中,应根据设备性能合理调整动画帧率和复杂度,确保用户体验流畅。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online