在Android手机中,我们经常可以看到类似于彩虹条的进度条,尤其是在显示下载进度的时候。下面就来讲解这种彩虹条是如何实现的:
1. 首先,我们来看一下彩虹条的效果图,并对其进行分析:
彩虹条的效果为:
- 一根实线连着一根实线,并且实线与实线之间有一点空隙
- 图像会整体向右移动
-下面的【图1】表示的是每一帧的图像,通过它我们可以看到实线移动的模拟效果
2. 那这种效果是如何做到的呢?
(1)首先图像会整体向右移动,那我们先来看一下图像整体向右移动的模拟图(见【图2】):
(2)可以看到第一帧图像从屏幕最左边开始
(3)第二帧图像从距离屏幕一个空格的地方开始,第三帧、第四帧以此类推,直到最后一帧移除屏幕
(4)我们可以看到图像整体向右移动,并最终移除屏幕
(5)但是屏幕逐渐移出屏幕的时候,我们可以看到左边逐渐变成空白
(6)因此我们还需要逐渐将左边进行填充
3. 左边填充的原理跟步骤2差不多,它是一个填充的过程,从右向左进行填充,直到左边靠近屏幕边缘,在此不多做详细介绍,请看如下的效果图(见【图3】):
4. 最终完整的效果图为:为了让读者更容易明白,我将线条分成了红、蓝两色,其实它们是一种颜色。【图四】
5. 从上面的分析可以看出,我们的整个实现过程分成两部分:左边移入屏幕,右边移除屏幕。原理分析完了,我们来看一下代码是如何实现的:
首先我们的彩虹条空间是一个自定义控件,关于自定义控件的相关知识请参考网上其他人写的一些博客,下面这篇博客比较通俗易懂,可以参考一下:
```java
private int barColor = Color.parseColor("#FF0000");
private int hSpace = Util.dpToPx(getResources(), 80);
private int vSpace = Util.dpToPx(getResources(), 4);
private int space = Util.dpToPx(getResources(), 10);
private float startX = 0;
```
下面我贴出源码:
第一部分:这一部分比较简单,主要是一些构造函数,构造函数里面主要是获取一些自定义属性的值,下面我们来看一下源代码:
```java
private int barColor = Color.parseColor("#FF0000");
private int hSpace = Util.dpToPx(getResources(), 80);
private int vSpace = Util.dpToPx(getResources(), 4);
private int space = Util.dpToPx(getResources(), 10);
private float startX = 0;
```
```java
private float delta = 10f;
Paint mPaint;
public RainbowBar(Context context) {
super(context);
}
public RainbowBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RainbowBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.rainbowbar, 0, 0);
hSpace = typedArray.getDimensionPixelSize(R.styleable.rainbowbar_rainbowbar_hspace, hSpace);
vSpace = typedArray.getDimensionPixelSize(R.styleable.rainbowbar_rainbowbar_vspace, vSpace);
barColor = typedArray.getColor(R.styleable.rainbowbar_rainbowbar_color, barColor);
typedArray.recycle();
mPaint = new Paint();
mPaint.setColor(barColor);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(vSpace);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float sw = this.getWidth();
float sh = this.getHeight();
int colorMode = ColorMode.HSV; // 可以设置为HSV、RGB等模式
int stepCount = (int) (2 * Math.PI * (sw * (float) Math.sqrt(delta))); // 根据宽度计算步数
GradientDrawable gradientDrawable = new GradientDrawable(); //创建渐变图对象
int[] colors;
int startColor = Color.RED; //起始颜色设为红色
int endColor = Color.GREEN; //结束颜色设为绿色
switch (colorMode) {
case HSV: //如果设置了HSV模式,则使用HSV色彩模式进行绘制
float[] hsv = new float[3];
for (int i = startColor; i <= endColor; i++) {
hsv[0] = i % (float) Math.pow(255, stepCount) * stepCount; //根据步数和最大值计算出H值
hsv[1] = maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random()))))))))) * maxValue((float) Math.random()); //根据随机值计算S和V值,保证颜色的过渡性
hsv[2] = maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random(), maxValue((float) Math.random())))))))); //确保饱和度不超过255,亮度不超过255
gradientDrawable.setColors(new ColorStateList(new int[][]{{i}}, new int[]{ColorUtils.rgbToColorInt(hsv)})); //将计算出的HSV颜色设置到渐变图中,并设置该色系的颜色为当前遍历的颜色
}
break;
case RGB: //如果设置了RGB模式,则直接使用RGB模式进行绘制
colors = new int[]{startColor, endColor}; //设置起始和结束颜色
break;
default: //默认使用HSV模式进行绘制
colors = new int[]{startColor, endColor}; //设置起始和结束颜色
break;
}
gradientDrawable.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT); //设置渐变方向为左右交替显示,即正弦函数形式展示彩虹效果
gradientDrawable.setSweepAngle(40f); //设置渐变角度为40°,即正弦函数振幅为2*40°=80°,即半圆弧形展示彩虹效果
RectF rectF = new RectF(0, sh * vSpace, sh * (sw + vSpace), sh * (vSpace + vSpace * steps)); //定义渐变区域范围,从左到右由大到小依次渐变到小再回到大的范围,即一个正弦函数周期的形状展示彩虹效果
gradientDrawable.setCornerRadius(Math.max(sw + vSpace * steps + hSpace * steps, vSpace * steps)); //设置圆角大小为最大的边宽加上两个边宽之和的一半作为半径,使彩虹条呈现圆形状展示彩虹效果。这样处理之后可以避免圆角导致的锯齿现象。同时也可以使得边缘平滑自然。在绘制完彩虹条后需要对画布进行清空操作才能继续绘制其他内容。这里为了避免重复绘制彩虹条导致画布重绘而产生卡顿现象。所以在绘制完彩虹条后调用canvas的drawChildren方法清空画布。然后再执行其他绘制操作即可。
请重构以下代码,并保持段落结构:
```java
.getMeasuredWidth();
if (startX >= sw + (hSpace + space) - (sw % (hSpace + space))) {
startX = 0;
} else {
startX += delta;
}
float start = startX;
while (start < sw) {
// 左边逐渐移出的过程
Log.e("main_B", "start = " + start + " hSpace = " + hSpace);
canvas.drawLine(start, 5, start + hSpace, 5, mPaint);
start += (hSpace + space);
}
start = startX - hSpace - space;
while (start >= -hSpace) {
// 右边逐渐移入的过程
Log.e("main_B", "start_ = " + start + " hSpace_ = " + hSpace);
canvas.drawLine(start, 5, start + hSpace, 5, mPaint);
start -= (hSpace + space);
}
invalidate();
```