您的位置:首页 > 移动开发 > Android开发

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

2014-02-01 23:17 405 查看
上一篇文章,我们利用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方法中对于点映射的变化。

源代码请点击
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息