这篇文章介绍一个有趣的数字雨的view
先上效果图
这个效果是在逛gitbug的时候发现,流动的数字雨颇有骇客的意味。
查看代码发现实现起来相当简单.
看着满屏的数字不断变化,实际上你只需要关注某一列的数字变化即可。而每一列的数字变化实现的原理就是每隔一个新的时间点,在原有的基础上多绘制一个数字。比如原来一列显示两个数字,下一个时间点显示三个数字,这样就能达到数字流动的视觉效果。
不多说了,直接上代码.
分两个步骤
1.将整个界面划分成多个列,用来承载数字
创建NumberRainView.java
public class NumberRainView extends LinearLayout {
private int normalColor = Color.GREEN;
private int highLightColor = Color.WHITE;
private Context context;
private float textSize;
public NumberRainView(Context context) {
this(context, null);
}
public NumberRainView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
textSize = 15 * context.getResources().getDisplayMetrics().density;
if (attrs != null) {
TypedArray typedArray = this.getContext().obtainStyledAttributes(attrs, R.styleable.NumberRainView);
normalColor = typedArray.getColor(R.styleable.NumberRainView_normalColor, Color.GREEN);
highLightColor = typedArray.getColor(R.styleable.NumberRainView_highLightColor, Color.WHITE);
textSize = typedArray.getDimension(R.styleable.NumberRainView_textSize, textSize);
typedArray.recycle();
}
setOrientation(LinearLayout.HORIZONTAL);
setBackgroundColor(Color.BLACK);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
addRainItems();
}
//核心代码
private void addRainItems() {
int count = (int) (getMeasuredWidth() / textSize);
for (int i = 0; i < count; i++) {
NumberRainItemView itemView = new NumberRainItemView(context);
itemView.setNormalColor(normalColor);
itemView.setHighLightColor(highLightColor);
itemView.setTextSize(textSize);
itemView.setStartDelay((long) (Math.random() * 1000));
LayoutParams params = new LayoutParams((int) textSize + 10, getMeasuredHeight());
addView(itemView, params);
}
}
}
2.绘制每一列的随机数字,同时要注意有一个高亮的数字
创建 NumberRainItemView.java
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(textSize);
mPaint.setColor(normalColor);
if (isShowAllNumbers()) {
drawAllNumbers(canvas);
} else {
drawPartNumbers(canvas);
}
}
判断是显示部分数字还是所有数字,最开始的时候,只显示部分数字,随着时间,才会显示完整的数字
//判断条件
private boolean isShowAllNumbers() {
return nowHeight >= getHeight();
}
//显示所有数字,根据view的高度来计算count
private void drawAllNumbers(Canvas canvas) {
int count = (int) (getHeight() / textSize);
drawSingleNumber(canvas, count);
}
//只显示部分数字,根据当前的变量值nowHeight计算count
private void drawPartNumbers(Canvas canvas) {
int count = (int) (nowHeight / textSize);
nowHeight += textSize;
drawSingleNumber(canvas, count);
}
开始绘制数字
private void drawSingleNumber(Canvas canvas, int count) {
if (count == 0) {
//这段延迟为了达到各列数字雨不同步的效果
postInvalidateDelayed(startDelay);
} else {
float numberOffset = 0f;
for (int i = 0; i < count; i++) {
String randomNumber = String.valueOf((int) (Math.random() * 2));
//高亮的字符串
mPaint.setColor(highLightIndex == i ? highLightColor : normalColor);
//核心代码,绘制文字,且不停的变化绘制的位置
canvas.drawText(randomNumber, 0, numberOffset, mPaint);
numberOffset += textSize;
}
//计算下一次的高亮位置
if (isShowAllNumbers()) {
highLightIndex++;
highLightIndex = highLightIndex % count;
} else {
highLightIndex++;
}
postInvalidateDelayed(100);
}
}
完整代码
NumberRainItemView.java
public class NumberRainItemView extends View {
private int normalColor = Color.GREEN;
private int highLightColor = Color.RED;
private float nowHeight;
private float textSize;
private long startDelay;
private Paint mPaint;
private int highLightIndex;
public NumberRainItemView(Context context) {
super(context);
textSize = 15 * getResources().getDisplayMetrics().density;
mPaint = new Paint();
}
public void setNormalColor(int normalColor) {
this.normalColor = normalColor;
}
public void setHighLightColor(int highLightColor) {
this.highLightColor = highLightColor;
}
public void setTextSize(float textSize) {
this.textSize = textSize;
}
public void setStartDelay(long startDelay) {
this.startDelay = startDelay;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(textSize);
mPaint.setColor(normalColor);
if (isShowAllNumbers()) {
drawAllNumbers(canvas);
} else {
drawPartNumbers(canvas);
}
}
private void drawAllNumbers(Canvas canvas) {
int count = (int) (getHeight() / textSize);
drawSingleNumber(canvas, count);
}
private void drawPartNumbers(Canvas canvas) {
int count = (int) (nowHeight / textSize);
nowHeight += textSize;
drawSingleNumber(canvas, count);
}
private void drawSingleNumber(Canvas canvas, int count) {
if (count == 0) {
postInvalidateDelayed(startDelay);
} else {
float numberOffset = 0f;
for (int i = 0; i < count; i++) {
String randomNumber = String.valueOf((int) (Math.random() * 2));
mPaint.setColor(highLightIndex == i ? highLightColor : normalColor);
canvas.drawText(randomNumber, 0, numberOffset, mPaint);
numberOffset += textSize;
}
if (isShowAllNumbers()) {
highLightIndex++;
highLightIndex = highLightIndex % count;
} else {
highLightIndex++;
}
postInvalidateDelayed(100);
}
}
private boolean isShowAllNumbers() {
return nowHeight >= getHeight();
}
}
attrs属性
相关链接
仿知乎关注按钮的波纹效果
仿知乎广告效果
最后给出一个原始地址 kotlin版
原始地址github
java版
本文对应的代码地址