起因:因为逛CSDN的时候无意间看到这篇博文(传送门),想给自己的项目加上这个加载效果,但是原谅我放荡不羁用不来(不知道为什么是用那个报一堆不知名的错误),于是我决定自己动手,丰衣足食。
先上效果:
截图不是gif,请见谅。
下面来实现:(绘制一个正六边形我就不再多说)
先来绘制出所有的六边形:
按照上图所示:
六边形在没有x和y的偏移量的情况下,假设我们从左边的第一个定点开始绘制的话,那么
六边形的高等于:Math.sqrt(3)*六边形边长。
第一个出现位置的起始点坐标是:(六边形的高/2,六边形边长/2)。
第二个出现位置的起始点坐标是:((六边形的高/2)*3,六边形的边长/2).
第三个出现位置的起始点坐标是:(六边形的高*2,(六边形的边长/2)*3).
第四个出现位置的起始点坐标是:((六边形的高/2)*3,六边形边长*3).
第五个出现位置的起始点坐标是:(六边形的高/2,六边形边长*3).
第六个出现位置的起始点坐标是:(0,(六边形的边长/2)*3).
第七个出现位置的起始点坐标是:(六边形的高,(六边形的边长/2)*3).
其他点的计算就不一一列举了,都是很简单的数学计算。
先讲动画:
这里有一个动画,一种是显示的,一种隐藏的,但是隐藏的可以用显示的动画reverse()来实现逆向动画。
下面是动画代码:
mShowAni = ValueAnimator.ofFloat(0,1);
mShowAni.setDuration(200);
mShowAni.setInterpolator(new DecelerateInterpolator());
mShowAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animation){
float value = (float)animation.getAnimatedValue();
mScale = 0.5f+value/2;
mPaint.setAlpha((int)(value*255));
invalidate();//更新
}
});
添加动画监听:
mShowAni.addListener(new Animator.AnimatorListener(){
@Override
public void onAnimationStart(Animator animation){
}
@Override
public void onAnimationEnd(Animator animation){
mShowAni.removeAllListeners();
mShowAni = null;
initAni();
if(drawNum!=8) {
backDrawNum = 8;//赋值回退动画的位置
drawNum++;//正向动画的位置自增1
}
if(drawNum==8 && backDrawNum!=0)
{
//开始播放逆向动画
backDrawNum--;
mShowAni.reverse();
}
else if(drawNum==8 && backDrawNum==0)
{
//逆向动画播放完毕,播放正向动画
drawNum = 1;
}
invalidate();
}
@Override
public void onAnimationCancel(Animator animation){
}
@Override
public void onAnimationRepeat(Animator animation){
}
});
现在可以来看看绘画代码了(代码很简单,必要的地方我已经加上了注释):
public void DrawSix(Canvas canvas,Paint paint,int mLength,float Xoffset,float Yoffset,float scale)
{
Path mPath = new Path();
float height = (float)(Math.sqrt(3)*mLength);
mPath.moveTo(Xoffset,Yoffset+mLength/2);//初始点
mPath.lineTo(Xoffset+height/2,Yoffset);
mPath.lineTo(Xoffset+height,Yoffset+mLength/2);
mPath.lineTo(Xoffset+height,Yoffset+(mLength/2)*3);
mPath.lineTo(Xoffset+height/2,Yoffset+mLength*2);
mPath.lineTo(Xoffset,Yoffset+(mLength/2)*3);
mPath.lineTo(Xoffset,Yoffset+mLength/2);
mPath.close();//闭合路径
//Log.i("动画里","mScale:"+mScale);
if(scale!=-1) {//防止动画重复播放
if(mShowAni != null && ! mShowAni.isRunning()) {
mShowAni.start();
}
canvas.save();
canvas.scale(scale,scale,Xoffset + height / 2,Yoffset + mLength);
canvas.drawPath(mPath,paint);
canvas.restore();
}
else
{
//==-1的时候不播放动画,不处理会导致画面闪动
canvas.save();
canvas.scale(1,1,Xoffset + height / 2,Yoffset + mLength);
canvas.drawPath(mPath,paint);
canvas.restore();
}
}
/**
* 画守望六边形
* @param canvas
* @param paint
* @param mLength 半径
* @param Xoffset 偏移量
* @param Yoffset Y偏移量
* @param drawNum 绘画的位置
*/
public void DrawOWSix(Canvas canvas,Paint paint,int mLength,float Xoffset,float Yoffset,int drawNum,int backNum)
{
float height = (float)(Math.sqrt(3)*mLength);
float XoffsetZero = Xoffset;
float XoffsetOne = Xoffset+height/2;
float YoffsetOne = Yoffset;
float XoffsetTwo = Xoffset+(height/2)*3;
float XoffsetThree = Xoffset+height*2;
float YoffsetTwo = Yoffset+(mLength/2)*3;
float YoffsetThree = Xoffset+mLength*3;
float XoffsetCenter = Xoffset+height;
Paint defaultPaint = paint;//用来绘制已经播放完成动画的六边形
paint.setAlpha(255);
if(drawNum==8)
{
drawNum = backNum;
}
//画六边形,里面的-1赋值是用来让动画不再播放的
switch(drawNum) {
case 1:
DrawSix(canvas,paint,mLength,XoffsetOne,YoffsetOne,mScale);
break;
case 2:
DrawSix(canvas,defaultPaint,mLength,XoffsetOne,YoffsetOne,-1);
DrawSix(canvas,paint,mLength,XoffsetTwo + 3,YoffsetOne,mScale);
break;
case 3:
DrawSix(canvas,defaultPaint,mLength,XoffsetOne,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 3,YoffsetOne,-1);
DrawSix(canvas,paint,mLength,XoffsetThree + 6,YoffsetTwo + 3,mScale);
break;
case 4:
DrawSix(canvas,defaultPaint,mLength,XoffsetOne,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 3,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetThree + 6,YoffsetTwo + 3,-1);
DrawSix(canvas,paint,mLength,XoffsetTwo + 6,YoffsetThree + 6,mScale);
break;
case 5:
DrawSix(canvas,defaultPaint,mLength,XoffsetOne,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 3,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetThree + 6,YoffsetTwo + 3,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 6,YoffsetThree + 6,-1);
DrawSix(canvas,paint,mLength,XoffsetOne + 3,YoffsetThree + 6,mScale);
break;
case 6:
DrawSix(canvas,defaultPaint,mLength,XoffsetOne,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 3,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetThree + 6,YoffsetTwo + 3,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 6,YoffsetThree + 6,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetOne + 3,YoffsetThree + 6,-1);
DrawSix(canvas,paint,mLength,XoffsetZero,YoffsetTwo + 3,mScale);
break;
case 7:
DrawSix(canvas,defaultPaint,mLength,XoffsetOne,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 3,YoffsetOne,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetThree + 6,YoffsetTwo + 3,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetTwo + 6,YoffsetThree + 6,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetOne + 3,YoffsetThree + 6,-1);
DrawSix(canvas,defaultPaint,mLength,XoffsetZero,YoffsetTwo + 3,-1);
DrawSix(canvas,paint,mLength,XoffsetCenter + 3,YoffsetTwo + 3,mScale);
break;
default:
break;
}
}
接下来就要实现ondraw()方法了:
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
this.canvas = canvas;
DrawOWSix(this.canvas,mPaint,mLength,XOffset,YOffset,drawNum,backDrawNum);
}
实现构造方法(在这里对画笔,绘画大小,动画等进行初始化):
public DrawStar(Context context,AttributeSet attrs){
super(context,attrs);
final TypedArray array = context.getTheme().obtainStyledAttributes(attrs,R.styleable.OverWatchLoadingView, 0, 0);
mColor = array.getColor(R.styleable.OverWatchLoadingView_view_color, Color.parseColor("#FFCC00"));
array.recycle();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setAlpha(0);
mPaint.setColor(mColor);
View = this;
mScale = 0;
initAni();
}
public DrawStar(Context context,AttributeSet attrs,int defStyleAttr,Paint mPaint){
super(context,attrs,defStyleAttr);
this.mPaint = mPaint;
View = this;
mScale = 0;
initAni();
}
public DrawStar(Context context,AttributeSet attrs,int defStyleAttr,int defStyleRes,Paint mPaint){
super(context,attrs,defStyleAttr,defStyleRes);
this.mPaint = mPaint;
View = this;
mScale = 0;
initAni();
}
当然,我们需要外部可以控制这些动画的播放,加上几个方法应该就可以了(并没有测试)。
public void pauseLoading()
{
mShowAni.pause();
}
public void continueLoading()
{
mShowAni.start();
}
public void cancelLoading()
{
mShowAni.cancel();
}
源码传送门(传送门)