使用ObjectAnimator做一个自定义动画 动画效果大致类似水波纹的效果 先看图 没有GIF图 直接上静态图了
看完效果图 开始码代码 首先理一下思路 做这个效果需要解决的几个问题
1.外层两个圆圈扩大的时候的不会超出屏幕宽度
2.不定时出现的水波纹效果的出现区域应该在最外层圆圈没有放大时的区域
3.使用两个ObjectAnimator 分别控制圆圈的扩大和缩小,和水波纹效果的出现
还是直接先上代码
这个自定义view继承与View 然后重写draw()方法 看代码
首先初始化各种值 初始化了三个圆圈的初始半径 和颜色 以及控制圆圈和OBjectAnimator和水波纹的颜色以及控制水波纹的ObjectAnimator 先说一下圆圈的大小收缩是利用scale实现的 然后水波纹的效果是利用颜色的渐变去实现圆圈的颜色透明度 以及圆圈的半径不断扩大 这个说波纹的效果就出来了
长话短说,主要讲具体实现的地方
在初始化这个自定义控件后干了一件事 重写View 的onSizeChanged()方法,在这里面去测量了控件的宽度 然后 宽度/10 就是最内圈圆的大小,接着第二个圆圈就在内圈的基础上加了一个值 外圈的半径又加一个更大的值 就确定下来了
然后就是使用ObjectAnimator去不断改变圆圈的属性 然后draw 就可以了 使用了ObjectAnimator.ofFloat(); 查api可以知道 用我用的这个方法构造
mCircleAnimator = ObjectAnimator.ofFloat(null, new FloatProperty() {
@Override
public void setValue(Object object, float value) {
mIsStartAnim = true;
mCircleWidth = value;
invalidate();
}
}, 1.0f, 1.6f);
mCircleAnimator.setDuration(3000);
mCircleAnimator.setRepeatMode(ValueAnimator.REVERSE);
mCircleAnimator.setRepeatCount(ValueAnimator.INFINITE);
mCircleAnimator.start();
首先讲一下 FloatProperty() 这个对象 这个不是API提供的 这里我使用了一个动画库 compile"com.andkulikov:transitionseverywhere:1.7.0"
把圆圈缩放的比例,写在第三,第四个参数 然后在FloatProperty中重写的setValue() 就可以实时的取到这个动画从1.0-1.6的变化过程中的具体的变化值 这个变化的值就记为圆圈大小变化的倍数 这个在画圆的时候用以圆圈的半径乘上这个系数就可以得到一个新的圆圈的半径 然后在这个方法中调用View的invalidate();方法去不断重新的在画布上画圆就可以了
// 画内圈圆
initPaint(mInsideColor,10,255,Paint.Style.STROKE);
canvas.drawCircle(getWidth() /2,getHeight() /2,mStartInsideTrackWidth,mPaint);
floatbigWidth =mCircleWidth*mStartBigWidth;
if(bigWidth *2<= getWidth()) {
initPaint(mBigColor,1,255,Paint.Style.STROKE);
canvas.drawCircle(getWidth() /2,getHeight() /2,bigWidth,mPaint);
initPaint(mSmallColor,4,255,Paint.Style.STROKE);
canvas.drawCircle(getWidth() /2,getHeight() /2,mCircleWidth*mStartSmallWidth,mPaint);
以上就是画圆时的具体做法就是不断通过系数的改变去改变每一次画的圆的半径就行了,并且每次画新的圆之前都初始化一次画笔这样画笔的颜色就改变了
接着画水波纹
mWaterX=mRandom.nextInt(mStartBigWidth*2) +mSpaceWidth;
mWaterY=mRandom.nextInt(mStartBigWidth*2) +mSpaceWidth;
mWaterAnimator= ObjectAnimator.ofFloat(null, newFloatProperty() {
@Override
public voidsetValue(Object object, floatvalue) {
mIsStartWaterAnim=true;
mWaterWidth= value;
}
},0f,1.0f);
mWaterAnimator.addListener(newSimpleAnimatorListener() {
@Override
public voidonAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mIsStartWaterAnim=true;
}
@Override
public voidonAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mIsStartWaterAnim=false;
}
});
mWaterAnimator.setDuration(1000);
mWaterAnimator.start();
在画水波纹是首先就是要确定水波纹出现的区域不能超过最圆的区域 所以生成这个水波纹的坐标的时候使用的外圈圆的直径作为生成随机数的种子 这样 生成的 水波纹的x y坐标就在这个区域中
然后还是同样的为了同样的构造函数创建一个ObjectAnimator对象 然后还是同样的搞一个渐变的系数 这里用了一个boolean去控制这个水波纹是不是要画在画布上 如果不使用boolean会出现一次画完后 不会自动消失
if(mIsStartWaterAnim) {
// 画水波纹
// 画内圈固定的圆
initPaint(mWaterColor,0,255,Paint.Style.FILL);
canvas.drawCircle(mWaterX,mWaterY,6,mPaint);
// 画外圈扩散的圆
initPaint(mWaterColor,10,(int) ((1-mWaterWidth) *255),Paint.Style.FILL);
floatcircleWidth =mWaterWidth*10+6;
canvas.drawCircle(mWaterX,mWaterY,circleWidth,mPaint);
}
这里的画水波纹的具体做法就是画一个固定的小圆点 然后接着 在同一个坐标点上 换一个不断扩大并且颜色不断变浅的圆 水波纹效果就出来了
因为在构造函数中传入的透明度是从0-1.0 所以这里在通过系数计算透明度时 就是用了1-系数 这样就是从不透明到透明 然后同样把这个系数*10 就是一个整数了 这样一个大于1的整数乘上内圈固定圆的大小就可以画一个不断扩大的圆 这样水波纹效果就完成
最后怎么控制这个水波纹效果每隔2s或者5s出现一次呢
privateRunnablemAnimRunnable=newRunnable() {
@Override
public voidrun() {
startWaterAnim();
postDelayed(this,2000);
}
};
这里就写了一个线程去启动这个动画 接着就是调用postDelayed延迟2s后执行一次
这样就把启动画水波纹动画的线程启动了 内部又2s后执行一次 这样就循环每2s执行一次画水波纹的动画了。
最后 ObjectAnimator 会出现内存泄漏 还有使用ValueAnimator 是会直接出现内存问题 可以用ValueAnimator写一个动画 然后脸上Android studio就会发现内存走向会出现锯齿状 具体原因就不说了
怎么避免ObjectAnimator出现内存泄漏呢
就是在acvtivity 的onDestory()的时候去移除这个动画
public void cancelAnim() {
removeCallbacks(mAnimRunnable);
if (mCircleAnimator!=null) {
mCircleAnimator.cancel();
}
if(mWaterAnimator!=null) {
mWaterAnimator.cancel();
}
}
首先记得移除这个控件中的线程 然后直接把两个动画calcel()调就可以了
自定义动画基本就完成了 欢迎拍砖