Android自定义view有时需要完成一些复杂的动画,这时后会发现代码看起来还是蛮复杂的,如何实现让代码结构清晰,对提供代码稳定性以及对代码修改有着非常大的帮助。下面是我提供的一种思路。
思路原理: 我们知道一个动画其实是有固定流程的,比如一个走动的小人,他先从左往右走,然后跳一下,然后从左往右走。这时我们可以把这个动画划分为三个小流程:1.左往右走。2.跳一下。3.右往左走。 三个流程依次执行,执行完上一个就到下一个。所以整个设计思路就是:流程划分、流程控制。
原理是这样,下面通过代码来实现一个比较帅的加载动画,效果如下:
这个是个加载控件动画,主要包含了两个方法,一个是显示show,一个是隐藏hide,show时有它显示时的动画效果,hide时有它的动画效果,所以我们分别对这两个动画效果进行流程划分。
show时的动画:
这个动画我划分了两个流程:1.执行加载动画前显示。2.执行加载动画。
@Override
public void onShow() {
loadingState = LoadingState.show;
mShowController.clearProcess();
canceltimer();//清除之前的动画
//添加流程1,执行动画前显示八个小球界面
mShowController.addProcess(new ShowProcess1(mShowController));
//添加流程2执行加载动画
mShowController.addProcess(new ShowProcess2(mShowController));
mShowController.start();
postInvalidate();
}
可以看到,这时代码结构就非常清晰,如果这个动画需要改动,只需要增删改想要的流程就可以了。
上面的代码中的mShowController是显示show动画的流程控制器,它的作用是对添加的流程就行控制与管理。在view的onDraw的方法里面把canvas传入给当前执行的流程,就可以指定只让当前的流程进行刷新了。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (loadingState) {
case show:
mShowController.canvas = canvas;
//刷新当前流程
mShowController.inValid(canvas);
break;
case hide:
mHideController.canvas = canvas;
mHideController.inValid(canvas);
break;
case resume:
mResumeController.canvas = canvas;
mResumeController.inValid(canvas);
break;
default:
break;
}
下面看看流程ShowProcess1 的代码:
/**
* @author bifan 第一个,初始化界面
*
*/
private class ShowProcess1 extends CanvaProcess {
public ShowProcess1(CanvaProcessController processController) {
super(processController);
}
@Override
public void inValid(Canvas canvas) {
//view刷新时,如果当前流程是这个,就会调用到这个方法,在这里执行当前流程的界面操作。
canvas.drawColor(Bgcolor);
initBallSize();
drawLoadingBitmap(canvas, 0);
NextProcess();
}
@Override
public void startProcess() {
//执行该流程时调用该方法,进行初始化操作或者启动动画等
}
@Override
public void release() {
}
}
在看看ShowProcess2 流程2:
/**
* @author bifan 第一个,执行加载动画
*
*/
private class ShowProcess2 extends CanvaProcess {
public ShowProcess2(CanvaProcessController processController) {
super(processController);
}
@Override
public void inValid(Canvas canvas) {
canvas.drawColor(Bgcolor);//画出背景颜色
drawLoadingBitmap(canvas, Rotatedegree);//画出小球
}
@Override
public void startProcess() {
//启动动画
startloadinganimation();
}
@Override
public void release() {
}
}
private void startloadinganimation() {//通过Timer实现动画
canceltimer();
mTimer = new Timer();
float br = Ballradius;
int nums;// 铺满的圆个数
float angle;
long fretime;
if (br > 0 && Loadingradius > 0) {
angle = (float) (Math.toDegrees(Math.asin(br / Loadingradius))) * 2;
} else {
angle = 360 / 8 / 2;
}
nums = (int) (360 / 8 / angle);
fretime = 1000 / 25 / nums;
final float anglec = angle;
mTimer.schedule(new TimerTask() {
@Override
public void run() {
Rotatedegree = Rotatedegree + anglec;
postInvalidate();
}
}, 200, fretime);
}
**
hide时的动画:
**
这个动画划分了5个流程,具体如下:
@Override
public void onHide() {
loadingState = LoadingState.hide;
mShowController.clearProcess();
mHideController.clearProcess();
canceltimer();
//流程1:隐藏时界面初始化
mHideController.addProcess(new HideProcess1(mHideController));
//流程2:界面小球向外扩展动画
mHideController.addProcess(new HideProcess2(mHideController));
//流程3:界面小球向内收缩
mHideController.addProcess(new HideProcess3(mHideController));
//流程4:小球收缩到中心时的一个动画效果
mHideController.addProcess(new HideProcess4(mHideController));
//流程5:让界面以圆的范围扩展可见
mHideController.addProcess(new HideProcess5(mHideController));
mHideController.start();
postInvalidate();
}
具体各个流程的详细代码就不贴了。
小结:可以明显的看到,这种按流程划分的代码设计,对于带有很复杂的动画效果的自定义viwe来说,代码结构会非常清晰,逻辑很很好把握。如果你有更好的方式,还望指教一下。