Android学习小Demo(6)图片折叠效果的实现

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

具体的效果如下

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

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

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

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

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

public class MainActivity extends Activity {

	private ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f,1f);
	
	private PolyToPolyView polyView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		...
		
		valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
			
			@Override
			public void onAnimationUpdate(ValueAnimator arg0) {
				float rotateFactor = (Float)arg0.getAnimatedValue();
				polyView.setRotateFactor(rotateFactor);
			}
		});
		
		...
		btnRotate.setOnClickListener(new OnClickListener() {			
			@Override
			public void onClick(View v) {
				valueAnimator.start();				
			}
		});
				
	}
    ...
}	

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

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

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

2)在自定义View中,

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


代码如下:

		bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.photo1);		
		
		bitmapWidth = bitmap.getWidth();		
		bitmapHeight  = bitmap.getHeight();
		
		widthPerFold = Math.round((float)bitmapWidth /(float)folds);
		heightPerFold = bitmapHeight;				
		for (int i = 0; i < folds; i++) {
			rects[i] = new Rect(i * widthPerFold, 0, i* widthPerFold + widthPerFold, heightPerFold);	
		}		
		
		for(int i=0;i<folds;i++){
			matrices[i] = new Matrix();
		}
因为分成的第一份都要实现一个往后推的效果,所以分成多少份,对应的我们也要为各个长方形准备对应的matrix来实现变化,所以在下面也会同时new出一个matrix。

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

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

		translateFactor  = 1 - foldFactor;
		
		translateWidth = bitmapWidth  * translateFactor;
		
		translateWidthPerFold = Math.round(translateWidth / folds);
		
		foldDrawWidth = widthPerFold < translateWidthPerFold ? translateWidthPerFold : widthPerFold;
		foldDrawHeight = heightPerFold;
		
		float translateWidthPerfoldsquare = translateWidthPerFold * translateWidthPerFold;
		float deepth = (float)Math.sqrt(foldDrawWidth * foldDrawWidth - translateWidthPerfoldsquare);
		
		scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + deepth);
		
		float scaleWidth = foldDrawWidth * translateFactor; // from 1 to 0, means width becomes small				
		float scaleHeight = foldDrawHeight * scaleFactor;
		float topScaleHeightPoint = (foldDrawHeight - scaleHeight) / 2.f;		
		float bottomScaleHeightPoint = topScaleHeightPoint + scaleHeight;

		srcPoly[0] = 0;
		srcPoly[1] = 0;		
		srcPoly[2] = 0;
		srcPoly[3] = foldDrawHeight;
		srcPoly[4] = foldDrawWidth;
		srcPoly[5] = 0;
		srcPoly[6] = foldDrawWidth;
		srcPoly[7] = foldDrawHeight;
				
		for (int i = 0; i < folds; i++) {
			matrices[i].reset();
			boolean isEven = (i % 2 == 0);						
			dstPoly[0] = i * scaleWidth;
			dstPoly[1] = isEven ? 0 : topScaleHeightPoint;
			dstPoly[2] = dstPoly[0];
			dstPoly[3] = isEven ? foldDrawHeight : bottomScaleHeightPoint;
			dstPoly[4] = (i + 1) * scaleWidth;
			dstPoly[5] = isEven ? topScaleHeightPoint : 0;
			dstPoly[6] = dstPoly[4];
			dstPoly[7] = isEven ? bottomScaleHeightPoint : foldDrawHeight;
			
			if(dstPoly[4] <= dstPoly[0] || dstPoly[6] <= dstPoly[2]){
				shouldDraw = false;
				return;
			}
			
			matrices[i].setPolyToPoly(srcPoly, 0, dstPoly, 0, POLY_POINTS / 2);
		}

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

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

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

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

        int alpha = (int) (foldFactor * 255 * SHADOW_APLHA);
		paintSolid.setColor(Color.argb(alpha, 0, 0, 0));
		matrixShadowGradient.setScale(foldDrawWidth, 1);
		linearGradientShadow.setLocalMatrix(matrixShadowGradient);		
		paintGradientShadow.setAlpha(alpha);
		...
		if (i % 2 == 0) {
				canvas.drawRect(0, 0, foldDrawWidth, foldDrawHeight, paintSolid);
			} else {
				canvas.drawRect(0, 0, foldDrawWidth, foldDrawHeight, paintGradientShadow);
			}	
其实总的实现很简单,关键是理解了Matrix的setPolyToPoly方法中对于点映射的变化。

源代码请点击

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。