注:因部分原因,本篇主要讲解动效分析的思路,不提供源码下载,请见谅 ... ...

上一篇只讲了Canvas中的drawBitmap方法,并且还说的这个方法好像很腻害、能做出很多牛逼效果的样子,接下来这篇文章只是为了作为上一篇文章的一个小栗子,进一步拓展大家利用drawBitmap 完成动效的思路!

好了,先上失真的不能再失真的效果图:



android 5s动效实现 安卓动效开发_html

咱们先一起来分析下上面的效果:

假定这是你刚从UE 或动效射鸡湿手里拿到上面的动效设计图,映入眼帘的是苍茫的星空,漂浮的星球营造出的深邃、浩瀚的宇宙,好了,不多BB了,针对上图你会想到什么样的实现方案?

1. 有些同学可能会想到创建对应数量个ImageView,然后针对每一个ImageView使用 Animation或Animator 去做对应的移动效果;

2.采用自己绘制的方式进行实现,不就是漂浮的星球吗,咱都给画出来;


不用说,上面第二种方案肯定更可取,第一种方案有以下几个缺陷:

1. 创建的view 个数过多,性能太差;

2. 灵活性太差,比如UE或产品要增加或减少星球数量,都会是个麻烦事儿;

3. 对每一个view做移动动画,开销太大还不太可控或修改;

针对于此,咱们坚定不移的走自己绘制完成漂浮动效的路线;

既然要绘制,那首先得拿到星球的位图,根据星球的种类拿到所有的位图:


[html]  view plain  copy

1. /**  
2.  * init bitmap info  
3.  */  
4. private void initBitmapInfo() {  
5.   
6. mBackBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.back))  
7.             .getBitmap();  
8. mBackWidth = mBackBitmap.getWidth();  
9. mBackHeight = mBackBitmap.getHeight();  
10.   
11. mStarOne = ((BitmapDrawable) mResources.getDrawable(R.drawable.star2))  
12.             .getBitmap();  
13. mStarOneWidth = mStarOne.getWidth();  
14. mStarOneHeight = mStarOne.getHeight();  
15.   
16. mStarTwo = ((BitmapDrawable) mResources.getDrawable(R.drawable.star1))  
17.             .getBitmap();  
18. mStarTwoWidth = mStarTwo.getWidth();  
19. mStarTwoHeight = mStarTwo.getHeight();  
20.   
21. mStarThree = ((BitmapDrawable) mResources.getDrawable(R.drawable.star3))  
22.             .getBitmap();  
23.   
24. mStarThreeWidth = mStarThree.getWidth();  
25. mStarThreeHeight = mStarThree.getHeight();  
26.   
27. }



上面拿到了背景和三种类型星球的位图,根据上面的效果,我们来分析下,有哪些特征性数据:


1. 同一种星球有大有小;

2. 彼此之间有透明度的差别;

3. 漂浮的方向不一样;

4. 漂浮的速度不一样;

5. 每个星球都得有自己的位置;

我们暂且只分析这么多,基于此,我们抽象出星球对象:


[html]  view plain  copy

1. /**  
2.  * 星球  
3.  * @author AJian  
4.  */  
5. private class StarInfo {  
6.   
7.     // 缩放比例  
8.     float sizePercent;  
9.     // x位置  
10.     int xLocation;  
11.     // y位置  
12.     int yLocation;  
13.     // 透明度  
14.     float alpha;  
15.     // 漂浮方向  
16.     int direction;  
17.     // 漂浮速度  
18.     int speed;  
19. }

为了得到上面的部分数据,我们先写一些数据或方法:

1. 为了初始化星球的位置,我们用数组先定义一批星球的位置(基于view宽高的比例),当然大家也可以随机,只是随机可能出现扎堆情况:


[html]  view plain  copy

    1. private static final float[][] STAR_LOCATION = new float[][] {  
    2.             {0.5f, 0.2f}, {0.68f, 0.35f}, {0.5f, 0.05f},  
    3.             {0.15f, 0.15f}, {0.5f, 0.5f}, {0.15f, 0.8f},  
    4.             {0.2f, 0.3f}, {0.77f, 0.4f}, {0.75f, 0.5f},  
    5.             {0.8f, 0.55f}, {0.9f, 0.6f}, {0.1f, 0.7f},  
    6.             {0.1f, 0.1f}, {0.7f, 0.8f}, {0.5f, 0.6f}  
    7.     };


    2. 获取星球大小的方法(基于原始Bitmap缩放比例):


    [html]  view plain  copy

    1. /**  
    2.  * 获取星球大小  
    3.  */  
    4. private float getStarSize(float start, float end) {  
    5. nextFloat = (float) Math.random();  
    6. < nextFloat && nextFloat < end) {  
    7.         return nextFloat;  
    8.     } else {  
    9.         // 如果不处于想要的数据段,则再随机一次,因为不断递归有风险  
    10.         return (float) Math.random();  
    11.     }  
    12.   
    13. }


    3. 定义三种不同快慢的漂浮速度:


    [html]  view plain  copy

      1. mFloatTransLowSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f,  
      2.         mResources.getDisplayMetrics());  
      3. mFloatTransMidSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.75f,  
      4.         mResources.getDisplayMetrics());  
      5. mFloatTransFastSpeed = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f,  
      6.         mResources.getDisplayMetrics());



      4. 获取星球漂浮方向的方法:



      [html]  view plain  copy

      1. /**  
      2.  * 初始化星球运行方向  
      3.  */  
      4. private int getStarDirection() {  
      5. random = new Random();  
      6. randomInt = random.nextInt(4);  
      7. direction = 0;  
      8.     switch (randomInt) {  
      9.         case 0:  
      10. direction = LEFT;  
      11.             break;  
      12.         case 1:  
      13. direction = RIGHT;  
      14.             break;  
      15.         case 2:  
      16. direction = TOP;  
      17.             break;  
      18.         case 3:  
      19. direction = BOTTOM;  
      20.             break;  
      21.   
      22.         default:  
      23.             break;  
      24.     }  
      25.     return direction;  
      26. }


      有了上面的数据和方法,我们首先初始化一定数量的星球数据:


      [html]  view plain  copy

      1. /**  
      2.  * 初始化星球信息  
      3.  */  
      4. private void initStarInfo() {  
      5.   
      6. starInfo = null;  
      7. random = new Random();  
      8. i = 0; i < mStarCount; i++) {  
      9.         // 获取星球大小比例  
      10. starSize = getStarSize(0.4f, 0.9f);  
      11.         // 初始化星球大小  
      12. starLocation = STAR_LOCATION[i];  
      13. starInfo = new StarInfo();  
      14. starInfo.sizePercent = starSize;  
      15.   
      16.         // 初始化漂浮速度  
      17. randomSpeed = random.nextInt(3);  
      18.         switch (randomSpeed) {  
      19.             case 0:  
      20. starInfo.speed = mFloatTransLowSpeed;  
      21.                 break;  
      22.             case 1:  
      23. starInfo.speed = mFloatTransMidSpeed;  
      24.                 break;  
      25.             case 2:  
      26. starInfo.speed = mFloatTransFastSpeed;  
      27.                 break;  
      28.   
      29.             default:  
      30. starInfo.speed = mFloatTransMidSpeed;  
      31.                 break;  
      32.         }  
      33.   
      34.         // 初始化星球透明度  
      35. starInfo.alpha = getStarSize(0.3f, 0.8f);  
      36.         // 初始化星球位置  
      37. starInfo.xLocation = (int) (starLocation[0] * mTotalWidth);  
      38. starInfo.yLocation = (int) (starLocation[1] * mTotalHeight);  
      39. xLocation = " + starInfo.xLocation + "--yLocation = "  
      40.                 + starInfo.yLocation);  
      41. stoneSize = " + starSize + "---stoneAlpha = "  
      42.                 + starInfo.alpha);  
      43.         // 初始化星球位置  
      44. starInfo.direction = getStarDirection();  
      45.         mStarInfos.add(starInfo);  
      46.     }  
      47.   
      48. }



      有了这些数据,我们已经可以将星球绘制在屏幕上:



      [html]  view plain  copy

      1. private void drawStarDynamic(int count, StarInfo starInfo,  
      2.            Canvas canvas, Paint paint) {  
      3.   
      4. starAlpha = starInfo.alpha;  
      5. xLocation = starInfo.xLocation;  
      6. yLocation = starInfo.yLocation;  
      7. sizePercent = starInfo.sizePercent;  
      8.   
      9. xLocation = (int) (xLocation / sizePercent);  
      10. yLocation = (int) (yLocation / sizePercent);  
      11.   
      12. bitmap = null;  
      13. srcRect = null;  
      14. destRect = new Rect();  
      15.   
      16. 3 == 0) {  
      17.   
      18. bitmap = mStarOne;  
      19. srcRect = mStarOneSrcRect;  
      20.            destRect.set(xLocation, yLocation,  
      21.                    xLocation + mStarOneWidth, yLocation  
      22.                            + mStarOneHeight);  
      23. 2 == 0) {  
      24. bitmap = mStarThree;  
      25. srcRect = mStarThreeSrcRect;  
      26.            destRect.set(xLocation, yLocation, xLocation  
      27.                    + mStarThreeWidth, yLocation + mStarThreeHeight);  
      28.        } else {  
      29. bitmap = mStarTwo;  
      30. srcRect = mStarTwoSrcRect;  
      31.            destRect.set(xLocation, yLocation, xLocation  
      32.                    + mStarTwoWidth, yLocation + mStarTwoHeight);  
      33.        }  
      34.   
      35.        paint.setAlpha((int) (starAlpha * 255));  
      36.        canvas.save();  
      37.        canvas.scale(sizePercent, sizePercent);  
      38.        canvas.drawBitmap(bitmap, srcRect, destRect, paint);  
      39.        canvas.restore();  
      40.   
      41.    }



      接下来要考虑的只是如何让星球动起来,有了以上数据和思路,相信大家让星球动起来就不是难事了,只需要根据星球运动的方向,每次重绘的时候将星球的x、y增加或减小对应大小即可:



      [html]  view plain  copy

      1. private void resetStarFloat(StarInfo starInfo) {  
      2.         switch (starInfo.direction) {  
      3.             case LEFT:  
      4. -= starInfo.speed;  
      5.                 break;  
      6.             case RIGHT:  
      7.                 starInfo.xLocation += starInfo.speed;  
      8.                 break;  
      9.             case TOP:  
      10. -= starInfo.speed;  
      11.                 break;  
      12.             case BOTTOM:  
      13.                 starInfo.yLocation += starInfo.speed;  
      14.                 break;  
      15.             default:  
      16.                 break;  
      17.         }  
      18.     }



      这时候有部分同学可能会说了,尼玛, 星球直接移出屏幕了怎么办,这个问题相信大家都能解决,只是一个值的判断和重新修复,不再多言;


      最后针对这一类动效小谈一下,其实很大一部分效果和上面的动效是类似的,不信?我举几个栗子:

      1. 雪花飘落的效果

      尼玛,扯淡呢!雪花和这类似?雪花从上往下飞,并且还旋转;

      针对于此,我们只需要在抽取对象的时候加上旋转角度和旋转速度,至于从上往下飞的问题,我们只需要修改x、y的更新策略即可;

      2. 很多桌面类应用的花瓣飘落、落英缤纷效果;

      基本都是采用以上的分析和实现原理,重要的是对于数据的抽取和灵活运用,其他的也就是根据具体的需求动态的更新需要更新的数据,比如位置、大小、透明度等等;


      所以,从上面来看,这一类效果其实并不复杂,我们所需要做的只是将复杂动效进行分解、抽取,然后找到每一个小点最适合的实现方式,大的动效化小,然后逐个击破;

      我在写的过程中会尽可能的把思路描述清楚,因为在我看来,做动效,最主要的还是在于效果的拆解、衔接、解决的思路,思路清晰了,解决方案一般也就明了了;