大家新年好!
前面我们讲了矩阵在平面上的一些常用的变换,再前面我们讲了关于Android中属性动画的一些应用,不过这些变换都是发生在平面上的,是二维的,没有立体效果。立体效果的产生,是透视的效果,远处的东西看起来小,而近处的东西看起来大,大小的变化,再加上一些光影的处理,就形成了一种立体的感观,比如下面的立方体:
在Android中,实现3D的效果,可以通过OpenGL/ES,Camera,也可以通过Matrix的PolyToPoly来实现。OpenGL比较复杂麻烦,但是其实现的效果也最好,在游戏制作中利用的比较多,而Camera是Android中提供的一个包装好的类,模拟了一个观察点,通过改变这个观察点,从而实现纵深的变化,达到3d的效果。
我们前两篇文章都在讲Matrix,那么这一章我们也通过Matrix,再通过属性动画中ValueAnimator的应用, 来简单实现一个推拉门的效果吧。
下面是效果图:
这是一个很简单的推拉门效果,但是却是理解Matrix中SetPolyToPoly的一个很好的例子。
在当前的Activity上面,上面是一张图片,下面是一个按钮。当点击按钮的时候,图片的右上角和右下角会慢慢往后移动,从而形成一个推的效果。
下面我们结合代码来讲一下实现的思路。
1)在MainActivity中,创建一个ValueAnimator,设置一个从0 到 1变化的比例因子,叫rotateFactor,在变化的同时去更新PolyToPolyView。
PolyToPolyView是一个自定义的View。
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f,1f); 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(); } });
2)创建自定义的View的时候,首先获得图片的宽高(即右上角和右下角的坐标),并初始化两个数组,一个是图片原点的几个角的坐标点,一个是图片在新的位置的坐标点。
private void init(){ bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.photo1); w = bitmap.getWidth(); h = bitmap.getHeight(); srcPoly = new float[POINTS_NUMBER]; dstPoly = new float[POINTS_NUMBER]; matrix = new Matrix(); }
3)根据每次传进来的rotateFactor,计算新的图片右上角,右下角所处的位置(右上角的位置要比原来的低,右下角的点要比原来的高,从而形成一种透视的效果),然后利用Matrix.setPolyToPoly方法来将原来的点映射上新的点上。
public void calculateMatrix(){ shouldDraw = true; if(rotateFactor == 1){ shouldDraw = false; return; } translateFactor = 1 - rotateFactor; float translateWidth = w * translateFactor;//移动的距离 float translateWidthPerfoldsquare = translateWidth * translateWidth; float deepth = (float)Math.sqrt(w * w - translateWidthPerfoldsquare);//利用勾股定理算出移动时,深度的变化 float scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + deepth);//以这个深度来计算缩放的一个比例 float scaleWidth = w * translateFactor;//在图片右边往后移动的时候,在平面看去,图片的宽也会慢慢缩小。 float scaleHeight = h * scaleFactor;//高也是一样,离得越远,高度就会越小 float topScalePoint = (h - scaleHeight) / 2.f;//求出新的右上角的点 float bottomScalePoint = topScalePoint + scaleHeight;//求出新的右下角的点 srcPoly[0] = 0; srcPoly[1] = 0; srcPoly[2] = 0; srcPoly[3] = h; srcPoly[4] = w; srcPoly[5] = 0; srcPoly[6] = w; srcPoly[7] = h; dstPoly[0] = 0; //左上角跟左下角的点是不动的 dstPoly[1] = 0; dstPoly[2] = dstPoly[0]; dstPoly[3] = h; dstPoly[4] = scaleWidth; //右上角跟右下角的点是会慢慢往后移动的 dstPoly[5] = topScalePoint; dstPoly[6] = dstPoly[4]; dstPoly[7] = bottomScalePoint; if(dstPoly[4] <= dstPoly[0]){ shouldDraw = false; return; } matrix.reset(); matrix.setPolyToPoly(srcPoly, 0, dstPoly, 0, POINTS_NUMBER / 2);//将原来的坐标映射到新的点上面 }在这里,我们看一下setPolyToPoly这个方法。
/** * Set the matrix such that the specified src points would map to the * specified dst points. The "points" are represented as an array of floats, * order [x0, y0, x1, y1, ...], where each "point" is 2 float values. * * @param src The array of src [x,y] pairs (points) * @param srcIndex Index of the first pair of src values * @param dst The array of dst [x,y] pairs (points) * @param dstIndex Index of the first pair of dst values * @param pointCount The number of pairs/points to be used. Must be [0..4] * @return true if the matrix was set to the specified transformation */ public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) {这里我们看到pointCount这个参数,最大必须是4,也就是说,matric支持的映射点数最多就是4个点,而src和dst里面则是我们定义的坐标点,以[x0,y0,x1,y1,...]的形式存在,每两位为一个点,而srcIndex和dstIndex,则是表明我们要从哪个点开始映射的。在ApiDemo中,有提供一个PolyToPoly的例子,大家有兴趣可以去看一下。
3)当Animator在进行的时候,rotateFactor也会一直变化,相对应的,图片右上角和右下角的位置会一直被重新计算并刷新,就形成了往后推的效果。
4)在Canvas中,要去应用上面计算好的matrix,这里是利用了canvas.concat的方法,这是一个先乘(也即右乘),也就是实现这个matrix的变换。
public void onDraw(Canvas canvas){ calculateMatrix(); if(shouldDraw){ canvas.concat(matrix); canvas.drawBitmap(bitmap, 0,0, null); } }总的实现思路大概就是这样。方法总比问题多,像我们如果要形成3d效果,方法其实还是有各种各样的,我们可能多学习一点,从而对android的应用也多了解一点。
Android学习小Demo(5)结合Matrix跟Porperty Animatin 实现推拉门效果
原文:http://blog.csdn.net/linmiansheng/article/details/18893375