Android学习 图片折叠效果的实现

来源:互联网 发布:c语言心形图案代码 编辑:程序博客网 时间:2024/04/29 22:59

上一篇文章,我们利用Matrix的setPolyToPoly来实现图片的3D旋转,这一次,我们来实现一个漂亮一点的效果,让一张图片像折扇一样可以折叠起来。

具体的效果如下

这个效果是我有一次在DevBytes上看到的一个视频,由Google Android Team的员工介绍的一个效果,不过它们是把这个做成了一个可重复利用的自定义ViewGroup,我当时看了,发现这效果真是太帅了。于是自己就琢磨着应该怎么实现,不过最后,还是跑去GitHub下了它的一份代码,参考着,争取把里面主要的逻辑给理清了,给大家介绍一下。

其实我之所以写前面那篇文章《Android学习小Demo(5)结合Matrix跟Porperty Animatin 实现推拉门效果》,目的只是为了先让大家先熟悉一下matrix的setPolyToPoly方法,因为这个效果的实现就是利用matrix的这个方法的。

下面我们结合一下代码来讲一下思路,然后在最后,大家再下载源代码去学习吧。

在主Activity上,有一个自定义的FoldingView,主要是实现折叠效果的自定义View,下面有一个输入框,用户可以输入数字,表明是要将这张图片分成多少部分,然后点击按钮,开始动画。

1)我们先看一下主Activity中的代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.     private ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f,1f);  
  4.       
  5.     private PolyToPolyView polyView;  
  6.       
  7.     @Override  
  8.     protected void onCreate(Bundle savedInstanceState) {  
  9.         ...  
  10.           
  11.         valueAnimator.addUpdateListener(new AnimatorUpdateListener() {  
  12.               
  13.             @Override  
  14.             public void onAnimationUpdate(ValueAnimator arg0) {  
  15.                 float rotateFactor = (Float)arg0.getAnimatedValue();  
  16.                 polyView.setRotateFactor(rotateFactor);  
  17.             }  
  18.         });  
  19.           
  20.         ...  
  21.         btnRotate.setOnClickListener(new OnClickListener() {              
  22.             @Override  
  23.             public void onClick(View v) {  
  24.                 valueAnimator.start();                
  25.             }  
  26.         });  
  27.                   
  28.     }  
  29.     ...  
  30. }     

这代码其实跟我们前面一篇文章的代码是一样的:

a)定义一个ValueAnimator,在其AnimatorUpdateListener中设置自定义View的旋转因子,并设置图片折叠的份数

b)点击按钮,开始动画。

2)在自定义View中,

a)我们会从资源中获取一张图片,然后根据Activity中输入框的值,将图片分成等宽的长方形,如下:


代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.photo1);         
  2.   
  3. bitmapWidth = bitmap.getWidth();          
  4. bitmapHeight  = bitmap.getHeight();  
  5.   
  6. widthPerFold = Math.round((float)bitmapWidth /(float)folds);  
  7. heightPerFold = bitmapHeight;                 
  8. for (int i = 0; i < folds; i++) {  
  9.     rects[i] = new Rect(i * widthPerFold, 0, i* widthPerFold + widthPerFold, heightPerFold);      
  10. }         
  11.   
  12. for(int i=0;i<folds;i++){  
  13.     matrices[i] = new Matrix();  
  14. }  
因为分成的第一份都要实现一个往后推的效果,所以分成多少份,对应的我们也要为各个长方形准备对应的matrix来实现变化,所以在下面也会同时new出一个matrix。

2)分成相同的等份之后,我们就要考虑如何为每一个长方形设置变化的矩阵了。

2.1)分析拆分出来的矩形区域及折叠时候的效果,可以发现,偶数位(从0开始)的矩形是右边的那两个角往后推,而奇数位的矩形则刚好相反,当偶数位的矩形在往后推的时候,奇数位的矩形则相对着其也在往后推,并且在往后推的同时,每个矩形的宽度缩小的比例也是一致的。所以根据这几点,我们可以先算出一些公用的参数变化,比如每个矩形旋转的比例,平移的距离等等,下面看一下代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. translateFactor  = 1 - foldFactor;  
  2.   
  3. translateWidth = bitmapWidth  * translateFactor;  
  4.   
  5. translateWidthPerFold = Math.round(translateWidth / folds);  
  6.   
  7. foldDrawWidth = widthPerFold < translateWidthPerFold ? translateWidthPerFold : widthPerFold;  
  8. foldDrawHeight = heightPerFold;  
  9.   
  10. float translateWidthPerfoldsquare = translateWidthPerFold * translateWidthPerFold;  
  11. float deepth = (float)Math.sqrt(foldDrawWidth * foldDrawWidth - translateWidthPerfoldsquare);  
  12.   
  13. scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + deepth);  
  14.   
  15. float scaleWidth = foldDrawWidth * translateFactor; // from 1 to 0, means width becomes small                 
  16. float scaleHeight = foldDrawHeight * scaleFactor;  
  17. float topScaleHeightPoint = (foldDrawHeight - scaleHeight) / 2.f;         
  18. float bottomScaleHeightPoint = topScaleHeightPoint + scaleHeight;  
  19.   
  20. srcPoly[0] = 0;  
  21. srcPoly[1] = 0;       
  22. srcPoly[2] = 0;  
  23. srcPoly[3] = foldDrawHeight;  
  24. srcPoly[4] = foldDrawWidth;  
  25. srcPoly[5] = 0;  
  26. srcPoly[6] = foldDrawWidth;  
  27. srcPoly[7] = foldDrawHeight;  
  28.           
  29. for (int i = 0; i < folds; i++) {  
  30.     matrices[i].reset();  
  31.     boolean isEven = (i % 2 == 0);                        
  32.     dstPoly[0] = i * scaleWidth;  
  33.     dstPoly[1] = isEven ? 0 : topScaleHeightPoint;  
  34.     dstPoly[2] = dstPoly[0];  
  35.     dstPoly[3] = isEven ? foldDrawHeight : bottomScaleHeightPoint;  
  36.     dstPoly[4] = (i + 1) * scaleWidth;  
  37.     dstPoly[5] = isEven ? topScaleHeightPoint : 0;  
  38.     dstPoly[6] = dstPoly[4];  
  39.     dstPoly[7] = isEven ? bottomScaleHeightPoint : foldDrawHeight;  
  40.       
  41.     if(dstPoly[4] <= dstPoly[0] || dstPoly[6] <= dstPoly[2]){  
  42.         shouldDraw = false;  
  43.         return;  
  44.     }  
  45.       
  46.     matrices[i].setPolyToPoly(srcPoly, 0, dstPoly, 0, POLY_POINTS / 2);  
  47. }  

大家如果仔细看一下,会发现前面计算缩放比例及深度变化等,都跟前面的文章是一样的,关键是后面设置坐标数组的时候,会根据奇偶来判断。

2.2)在数组中,前面4位,分别是左上角,左下角的x,y座标,后面下位,则是右上角和右下角的坐标。对于偶数位矩形来说,在变化的过程中,其x坐标会根据平移和缩放的比例慢慢缩小并往左移,而左边的y坐标则是保持不变的,因为它们是这个矩形的轴,而右边的y坐标,则会根据缩放比例变小,而对于奇数位来说,则刚好相反。

3)第三步,分别利用canvas的save和restore函数保存各个矩形自己的matrix变化,利用clipRect剪裁出各个矩形区域,交将图片的对应的部分画到canvas上。

4)加上一些阴影和渐变交果,让其看起来是有纵容变化的感觉。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.       int alpha = (int) (foldFactor * 255 * SHADOW_APLHA);  
  2. paintSolid.setColor(Color.argb(alpha, 000));  
  3. matrixShadowGradient.setScale(foldDrawWidth, 1);  
  4. linearGradientShadow.setLocalMatrix(matrixShadowGradient);        
  5. paintGradientShadow.setAlpha(alpha);  
  6. ...  
  7. if (i % 2 == 0) {  
  8.         canvas.drawRect(00, foldDrawWidth, foldDrawHeight, paintSolid);  
  9.     } else {  
  10.         canvas.drawRect(00, foldDrawWidth, foldDrawHeight, paintGradientShadow);  
  11.     }     
其实总的实现很简单,关键是理解了Matrix的setPolyToPoly方法中对于点映射的变化。源代码请点击
0 0