大家教师节快乐啊,不知道勤学的Coder们有没有去尝试下绘制上篇文章中最后留下的进阶效果,不管怎样,还是一起动手写一遍吧!看看套路是否一致。
水波纹
首先来看图-水波纹中的效果,其具有以下特点:
-
从内到外四层,内圆外环;
-
从内到外四个色值;
-
最内部圆局于View中心;
-
圆和环同心;
看出以下几点,我们就可以开始按照套路画图了,老套路走起(在以后自定义View部分新建项目及类相关的描述不再赘述)。
声明画笔和宽高
1.声明画笔及宽高
如上分析,我们需要两个画笔,一个用于绘制内圆,一个用于绘制外环。
/**
* 绘制最中心圆的Paint
*/
private Paint mInnerCirclePaint;
/**
* 绘制外部圆环的Paint
*/
private Paint mOutterRingPaint;
/**
* View宽度
*/
private int mWidth;
/**
* View高度
*/
private int mHeight;
/**
* 外环宽度
*/
private int mOuttterRingWidth = 50;
2.初始化画笔并在构造函数内调用
由于外部为圆环,所以外部画笔样式设置为空心,内部圆画笔样式设置为实心,同时设置画笔宽度为外部圆环宽度。
public WaveView(Context context) {
super(context);
init();
}
public WaveView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerCirclePaint.setColor(Color.BLUE);
/** 内部画笔为实心 **/
mInnerCirclePaint.setStyle(Style.FILL);
mOutterRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOutterRingPaint.setColor(Color.BLUE);
/** 外部画笔为空心 **/
mOutterRingPaint.setStyle(Style.STROKE);
mOutterRingPaint.setStrokeWidth(mOuttterRingWidth);
}
3.初始化宽高
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w > 0 && h > 0){
mWidth = w;
mHeight = h;
}
}
计算Points
声明内圆半径,圆环宽度,计算各圆的半径,同心圆及圆环,圆心位于View中心。
如图-水波纹内部距离说明,外环所在圆依次为(radius为外环半径,mInnerRadius为内圆半径,mOutterRingWidth为环宽度):
一环:半径与内圆半径一致,radius = mInnerRadius+(mOutterRingWidth/2)1; 二环:半径等于内圆半径+圆环宽度,radius = mInnerRadius + (mOutterRingWidth/2)3;
…
依次类推,我们可以得到外部圆环半径公式:
radius = mInnerRadius + (mOutterRingWidth/2) * i;(i取1,3,5,7,9…)
如此我们就可以开始撸码了。
/* 声明必要变量 */
/**
* 内圆半径
*/
private int mInnerRadius = 50;
/**
* 存储计算所得的外环半径
*/
private int[] mRadius = new int[4];
/* 计算外环半径 */
private void calculateRadius() {
for (int i = 0; i < 4; i++) {
mRadius[i] = mInnerRadius + (i * 2 + 1) * mOuttterRingWidth / 2;
}
}
获取画布绘制圆及圆环
重写onDraw函数获取canvas,并绘制圆环及内圆。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
calculateRadius();
canvas.drawCircle(mWidth / 2, mHeight / 2, mInnerRadius, mInnerCirclePaint);
for (int i = 0; i < 4; i++) {
/** 改变外环的颜色透明度 **/
mOutterRingPaint.setAlpha(255 - (int) (255 * ((float) (i + 1) / 5)));
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius[i], mOutterRingPaint);
}
}
至于运行效果,大家自己动手试一下喽!
动起来的水波纹
虽然我们画出来了水波纹,但是和蔼的产品还是会批斗你的,他会说,你家水波纹静态的哦?这一秒钟心里是不是有一万只羊驼奔腾?哈哈!奔腾归奔腾,需求我们还是要实现下滴。
那么我们来观察下现实中的水波纹,不难发现与图-水波纹形成 相似的过程。
我们可以看出水波纹动态形成的本质是,外部圆环逐步扩张增加形成的。那么我们只需要动态控制外部圆环的绘制个数,就可以让它动起来了。
属性动画
说到控制外环个数动态增长,做过动画的朋友第一个直觉肯定就是属性动画喽,这样是值变化,所以我们使用ValueAnimator
即可(关于属性动画,帧动画,View动画相关的细节,我们在后续文章中单独说明)。
1.声明动画相关的成员
/**
* 控制外环个数变化的属性动画对象
*/
private ValueAnimator mValueAnimator;
/**
* 绘制的外环总个数
*/
private int mOutterRingCount = 4;
2.初始化动画对象并开始
public void startAnimation(){
//创建ValueAnimator对象,按照整型值从0变化到4
mValueAnimator = ValueAnimator.ofInt(0,5);
//设置动画重复类型,RESTART--重新开始,REVERSE--值反转
mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
//设置动画重复次数,-1--不限制次数
mValueAnimator.setRepeatCount(-1);
mValueAnimator.setDuration(2000);
mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mOutterRingCount = (int) valueAnimator.getAnimatedValue();
postInvalidate();
}
});
mValueAnimator.start();
}
3.更新onDraw
循环参数
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
calculateRadius();
canvas.drawCircle(mWidth / 2, mHeight / 2, mInnerRadius, mInnerCirclePaint);
for (int i = 0; i < mOutterRingCount; i++) {
mOutterRingPaint.setAlpha(255 - (int) (255 * ((float) (i + 1) / 5)));
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius[i], mOutterRingPaint);
}
}
运行效果见gif,至此我们就完成了水波纹自定义View
的开发,怎么样?是不是很有趣啊.