canvas的一些常用操作,先上图
上述动画包含三步
- 六个小球旋转动画
- 六个小球扩散聚合动画
- 水波纹动画
具体实现思路,还是老规矩,自定义View
public class SplashView extends View
{
//定义旋转圆的画笔
private Paint mPaint;
//表示旋转圆的中心坐标
//屏幕中心点
private float mCenterX;
private float mCenterY;
//小球的背景颜色数组,每个小球的颜色可能不一样
private int[] mCircleColors;
//背景色
private int mBackgroundColor = Color.WHITE;
//6个小球的半径
private float mCircleRadius = 18;
//旋转大圆的半径
private float mRotateRadius = 90;
//当前大圆的半径
private float mCurrentRotateRadius = mRotateRadius;
//当前大圆的旋转角度
private float mCurrentRotateAngle = 0F;
//属性动画
private ValueAnimator mValueAnimator;
//表示旋转动画的时长
private int mRotateDuration = 1200;
private SplashState mState;
//表示斜对角线长度的一半,扩散圆最大半径
private float mDistance;
//扩散圆的半径
private float mCurrentHoleRadius = 0F;
//扩散圆的画笔
private Paint mHolePaint;
public SplashView(Context context)
{
super(context);
init(context);
}
public SplashView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context);
}
public SplashView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context)
{
mPaint = new Paint();
mPaint.setAntiAlias(true);
mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHolePaint.setStyle(Paint.Style.STROKE);
mHolePaint.setColor(mBackgroundColor);
//从配置文件中拿到相应的小球颜色值
mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
}
复制代码
重写onSizeChanged方法,得到View的具体大小,计算出 mCenterX,mCenter屏幕中心点的位置
mDistance水波纹圆的最大半径
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w * 1f / 2;
mCenterY = h * 1f / 2;
//w和h平方和的二次方根 就是利用勾股定理 求出屏幕斜对角线的长度 除以2表示水波纹空心圆的最大半径
mDistance = (float) (Math.hypot(w, h) / 2);
}复制代码
//定义一个抽象类,各种状态的绘制
private abstract class SplashState
{
abstract void drawState(Canvas canvas);
}复制代码
接下来我们要将六个小圆依次放到屏幕上,利用三角函数,根据具体的角度数,可以求出每一个小球的 (x,y)坐标,然后绘制小球即可
//1.小球旋转动画类
private class RotateState extends SplashState
{
private RotateState()
{
mValueAnimator = ValueAnimator.ofFloat(0, (float) (Math.PI * 2));
mValueAnimator.setRepeatCount(2);
mValueAnimator.setDuration(mRotateDuration);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
mCurrentRotateAngle = (float) animation.getAnimatedValue();
invalidate();
}
});
//监听动画结束状态 结束过后就执行第二种动画效果
mValueAnimator.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator animation)
{
super.onAnimationEnd(animation);
mState = new MerginState();
}
});
mValueAnimator.start();
}
@Override
void drawState(Canvas canvas)
{
//绘制背景
drawBackground(canvas);
//绘制小球
drawCircles(canvas);
}
}复制代码
//绘制小球具体的位置
private void drawCircles(Canvas canvas)
{
float rotateAngle = (float) (Math.PI * 2 / mCircleColors.length);
for (int i = 0; i < mCircleColors.length; i++)
{
// x = r * cos(a) + centX;
// y = r * sin(a) + centY;
//mCurrentRotateAngle 不断变化,角度也随之变化,重新计算小球的 x,y坐标
float angle = i * rotateAngle + mCurrentRotateAngle;
float cx = (float) (Math.cos(angle) * mCurrentRotateRadius+mCenterX);
float cy = (float) (Math.sin(angle) * mCurrentRotateRadius+mCenterY);
mPaint.setColor(mCircleColors[i]);
canvas.drawCircle(cx, cy, mCircleRadius, mPaint);
}
}
private void drawBackground(Canvas canvas)
{
if (mCurrentHoleRadius > 0){
//绘制空心圆
float strokeWidth = mDistance - mCurrentHoleRadius;
float radius = strokeWidth / 2 + mCurrentHoleRadius;
mHolePaint.setStrokeWidth(strokeWidth);
canvas.drawCircle(mCenterX,mCenterY, radius, mHolePaint);
}else{
canvas.drawColor(mBackgroundColor);
}
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (mState == null)
{
mState = new RotateState();
}
mState.drawState(canvas);
}复制代码
当第一个动画执行完成后,就接着执行小球的扩散动画
//2.扩散聚合
private class MerginState extends SplashState
{
private MerginState()
{
mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);
mValueAnimator.setDuration(mRotateDuration);
mValueAnimator.setInterpolator(new OvershootInterpolator(10f));
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
mCurrentRotateRadius = (float) animation.getAnimatedValue();
invalidate();
}
});
mValueAnimator.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator animation)
{
super.onAnimationEnd(animation);
mState = new ExpandState();
}
});
//mValueAnimator.start();
mValueAnimator.reverse();
}
@Override
void drawState(Canvas canvas)
{
//绘制背景
drawBackground(canvas);
//绘制小球
drawCircles(canvas);
}
}复制代码
当第二个动画执行完后,执行水波纹动画,绘制空心圆
//3.水波纹
private class ExpandState extends SplashState{
public ExpandState() {
mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mDistance);
// mValueAnimator.setRepeatCount(2);
mValueAnimator.setDuration(mRotateDuration);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentHoleRadius = (float) animation.getAnimatedValue();
invalidate();
}
});
mValueAnimator.start();
}
@Override
void drawState(Canvas canvas) {
drawBackground(canvas);
}
}复制代码
private void drawBackground(Canvas canvas)
{
if (mCurrentHoleRadius > 0){
//绘制空心圆
float strokeWidth = mDistance - mCurrentHoleRadius;
float radius = strokeWidth / 2 + mCurrentHoleRadius;
mHolePaint.setStrokeWidth(strokeWidth);
canvas.drawCircle(mCenterX,mCenterY, radius, mHolePaint);
}else{
canvas.drawColor(mBackgroundColor);
}
}复制代码
最后附上xml文件
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
"@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@mipmap/content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
"@+id/splash"
android:layout_width="match_parent"
android:layout_height="match_parent" />
复制代码