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来说,代码结构会非常清晰,逻辑很很好把握。如果你有更好的方式,还望指教一下。