Android——自定义View(二)
2019-09-29 本文已影响0人
如愿以偿丶
1.效果展示
在这里插入图片描述2.效果分析
1.绘制6个不同颜色的圆
2.通过属性动画不断改变每个圆的旋转角度进行旋转
3.旋转动画结束后不断改变大圆的半径将聚合到中间
4.聚合动画结束后在绘制一个圆,不断增大圆的半径
在这里插入图片描述3.效果实现
3.1.绘制6个不同颜色的圆,并开启旋转动画
public class LoadingView extends View {
private final String TAG = "LoadingView";
//是否初始化参数
private boolean mInitParams = false;
//动画旋转时间
private final long ROTATION_ANIMATION_TIME = 2500;
//当前大圆旋转的角度(弧度)
private float mCurrentRotationAngle = 0F;
//小圆颜色列表
private int[] mCircleColors;
//外圈大圆的半径,整个屏幕宽度的 1/4 半径的圆心是以屏幕左上角为中心,所以我们需要设置中心点
private int mRotationRadius;
//小圆半径时大圆半径的 1/8;
private int mCircleRadius;
//小圆画笔
private Paint mPaint;
//屏幕中心点
private int mCenterX,mCenterY;
//整体颜色背景
private int mSplashColor = Color.WHITE;
//代表当前状态所画动画
private LoadingState mLoadingState;
//获取当前大圆的半径
private float mCurrentRotationRadius;
//最后一步 空心圆初始半径
private float mHoleRadius = 0f;
//对角线的一半大小
private float mDiagonalDist;
//优化设置标记
private boolean isStopAnimator = false;
public LoadingView(Context context) {
this(context,null);
}
public LoadingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mInitParams){
//初始化获取宽高等信息
initParams();
}
if (mLoadingState == null){
mLoadingState = new RotationState();
}
//绘制我们的小圆,通过属性动画进行旋转
mLoadingState.draw(canvas);
}
private void initParams() {
//获取小圆的颜色数组
mCircleColors = getContext().getResources().getIntArray(R.array.splash_circle_colors);
//外围大圆的半径 = 屏幕宽度 / 4
mRotationRadius = getMeasuredWidth() / 4;
//旋转小圆的半径 = 大圆半径 / 8;
mCircleRadius = mRotationRadius / 8;
mCenterX = getMeasuredWidth() / 2;
mCenterY = getMeasuredHeight() / 2;
mDiagonalDist = (float) Math.sqrt(getMeasuredHeight() * getMeasuredHeight() + getMeasuredWidth() * getMeasuredWidth());
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mInitParams = true;
}
/**
* 动画消失,开始聚合,将我们代码进行封装
*/
public void disappear() {
if (isStopAnimator){
return;
}
//关闭旋转动画
if (mLoadingState instanceof RotationState){
RotationState rotationState = (RotationState) mLoadingState;
rotationState.cancel();
}
//开启我们聚合动画
mLoadingState = new MergeState();
}
//将我们三个动画进行封装,便于维护
public abstract class LoadingState{
public abstract void draw(Canvas canvas);
}
/**
* 旋转动画和绘制
*/
public class RotationState extends LoadingState{
private ValueAnimator mAnimation;
/**
* 为什么不旋转?
* 当我们每次onDraw的时候都会创建一个animator,每次都从0开始,将animator提为全局
*/
public RotationState(){
//搞一个变量不断 去改变采用属性动画 旋转0-360
mAnimation = ObjectAnimator.ofFloat(0F,2F * (float) Math.PI);
mAnimation.setDuration(ROTATION_ANIMATION_TIME);
mAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator mAnimation) {
//动画监听
mCurrentRotationAngle = (float) mAnimation.getAnimatedValue();
Log.e(TAG,"旋转动画1111111111");
//重新绘制
invalidate();
}
});
//匀速插值器
mAnimation.setInterpolator(new LinearInterpolator());
//不断反复执行
mAnimation.setRepeatCount(-1);
mAnimation.start();
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(mSplashColor);
//画六个圆 获取每个圆的角度
double percentAngle = 2 * Math.PI / mCircleColors.length;
for (int i = 0; i < mCircleColors.length; i++) {
//设置每一个圆画笔的颜色
mPaint.setColor(mCircleColors[i]);
// 当前角度 = 每份角度 + 旋转角度
double currentAngle = percentAngle * i + mCurrentRotationAngle;
// 通过公式计算
float cx = (float) (mCenterX + mRotationRadius * Math.cos(currentAngle));
float cy = (float) (mCenterY + mRotationRadius * Math.sin(currentAngle));
canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
}
}
/**
* 关闭动画
*/
public void cancel() {
mAnimation.cancel();
}
}
}
3.2.开启我们的聚合动画
/**
* 聚合动画
*/
public class MergeState extends LoadingState{
private ValueAnimator mAnimation;
public MergeState(){
//不断平移动画,减小我们大圆半径就可以,刚开始有一个插值器,先往后跑,在往中间聚合
mAnimation = ObjectAnimator.ofFloat(mRotationRadius,0);
mAnimation.setDuration(ROTATION_ANIMATION_TIME / 2);
mAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator mAnimation) {
//动画监听
mCurrentRotationRadius = (float) mAnimation.getAnimatedValue();
//重新绘制
invalidate();
Log.e(TAG,"合并动画222222222");
}
});
mAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimation.cancel();
mLoadingState = new ExpendState();
}
});
//匀速插值器
mAnimation.setInterpolator(new AnticipateInterpolator(5f));
mAnimation.start();
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(mSplashColor);
//画六个圆 获取每个圆的角度
double percentAngle = 2 * Math.PI / mCircleColors.length;
for (int i = 0; i < mCircleColors.length; i++) {
//设置每一个圆画笔的颜色
mPaint.setColor(mCircleColors[i]);
// 当前角度 = 每份角度 + 旋转角度
double currentAngle = percentAngle * i + mCurrentRotationAngle;
// 通过公式计算
float cx = (float) (mCenterX + mCurrentRotationRadius * Math.cos(currentAngle));
float cy = (float) (mCenterY + mCurrentRotationRadius * Math.sin(currentAngle));
canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
}
}
}
3.3.聚合动画结束后在绘制一个圆,不断增大圆的半径
/**
* 展开动画
*/
public class ExpendState extends LoadingState{
private ValueAnimator mAnimation;
public ExpendState(){
//从屏幕中心点开始扩散,不断改变圆的半径大小,最终半径为屏幕对角线的一般
mAnimation = ObjectAnimator.ofFloat(mHoleRadius,mDiagonalDist);
mAnimation.setDuration(ROTATION_ANIMATION_TIME / 2);
mAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator mAnimation) {
//动画监听
mHoleRadius = (float) mAnimation.getAnimatedValue();
Log.e(TAG,"扩散动画33333333333");
//重新绘制
invalidate();
}
});
mAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
loadEndListener.loadEnd();
}
});
mAnimation.start();
}
@Override
public void draw(Canvas canvas) {
//画笔的宽度
float strokeWidth = mDiagonalDist - mHoleRadius;
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(strokeWidth);
mPaint.setColor(mSplashColor);
//此时圆心就是屏幕的中心点
float cx = (float) mCenterX;
float cy = (float) mCenterY;
//注意圆的半径
float radius = strokeWidth / 2 + mHoleRadius;
canvas.drawCircle(cx,cy,radius,mPaint);
}
}
4.最后优化
1.当我们网络请求结束后,将我们的View设置为INVISIBLE,减少布局的测量和摆放,减少系统的View的绘制流程
2.清除我们的动画
3.设置一个标志位,通过标志来判断是否需要执行动画
4.判断我们父容器是否存在,把自己移除
@Override
public void setVisibility(int visibility) {
//1.将我们的View设置为INVISIBLE,减少布局的测量和摆放,减少系统的View的绘制流程
super.setVisibility(INVISIBLE);
//2.清除我们的动画
clearAnimation();
//3.设置一个标志位,通过标志位来执行动画
isStopAnimator = true;
//4.判断父容器是否还在,把自己移除,清除自己的所有View
ViewGroup parent = (ViewGroup) getParent();
if (parent!=null){
//把自己从父容器移除
parent.removeView(this);
//移除自己的所有子View
this.removeAllViews();
}