Android gallery 3D效果

jopen 7年前

        在看了iOS上面的CoverFlow后,感觉效果真的不错,就想在android上面实现一个,这个程序在网上参考了一此核心的代码,当然我添加了一些其他的东西,废话不多话,先看效果,不然就是无图无真相了。

 

Android gallery 3D效果

 

其实实现这个效果很简单,下面作一个简单的介绍

 

一,创建倒影效果

这个基本思路是:

1,创建一个源图一样的图,利用martrix将图片旋转180度。这个倒影图的高是源图的一半。

Matrix matrix = new Matrix();    // 1表示放大比例,不放大也不缩小。  // -1表示在y轴上相反,即旋转180度。  matrix.preScale(1, -1);    Bitmap reflectionBitmap = Bitmap.createBitmap(      srcBitmap,      0,       srcBitmap.getHeight() / 2,  // top为源图的一半      srcBitmap.getWidth(),       // 宽度与源图一样      srcBitmap.getHeight() / 2,  // 高度与源图的一半      matrix,      false);

2,创建一个最终效果的图,即源图 + 间隙 + 倒影。

final int REFLECTION_GAP = 5;    Bitmap bitmapWithReflection = Bitmap.createBitmap(         reflectionWidth,         srcHeight + reflectionHeight + REFLECTION_GAP,          Config.ARGB_8888);

3,依次将源图、倒影图绘制在最终的bitmap上面。

// Prepare the canvas to draw stuff.  Canvas canvas = new Canvas(bitmapWithReflection);                // Draw the original bitmap.  canvas.drawBitmap(srcBitmap, 0, 0, null);                // Draw the reflection bitmap.  canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);
4,创建LinearGradient,从而给定一个由上到下的渐变色。

Paint paint = new Paint();  paint.setAntiAlias(true);  LinearGradient shader = new LinearGradient(          0,           srcHeight,           0,           bitmapWithReflection.getHeight() + REFLECTION_GAP,           0x70FFFFFF,           0x00FFFFFF,          TileMode.MIRROR);  paint.setShader(shader);  paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));    // Draw the linear shader.  canvas.drawRect(          0,           srcHeight,           srcWidth,           bitmapWithReflection.getHeight() + REFLECTION_GAP,           paint);

二,扩展Gallery

扩展系统的gallery,我们需要重写两个方法,getChildStaticTransformation()和getChildDrawingOrder(),同时,要使这两个方法能被调用,必须执行如下两行代码,文档上面是有说明的。

        // Enable set transformation.          this.setStaticTransformationsEnabled(true);          // Enable set the children drawing order.          this.setChildrenDrawingOrderEnabled(true);

  • getChildDrawingOrder的实现

    @Override      protected int getChildDrawingOrder(int childCount, int i)      {          // Current selected index.          int selectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();          if (selectedIndex < 0)           {              return i;          }                    if (i < selectedIndex)          {              return i;          }          else if (i >= selectedIndex)          {              return childCount - 1 - i + selectedIndex;          }          else          {              return i;          }      }

这里为什么要计算drawing order,因为从上图中看到,我们的效果是:中间左边的顺序是 0, 1, 2,右边的child覆盖左边的child,而在中间右边的顺序正好相反,左边的覆盖右边的,所以我们要重写这个方法,而gallery自身的实现,不是这种效果。

  • getChildStaticTransformation的实现

    @Override      protected boolean getChildStaticTransformation(View child, Transformation t)      {          super.getChildStaticTransformation(child, t);                    final int childCenter = getCenterOfView(child);          final int childWidth  = child.getWidth();                    int rotationAngle = 0;          t.clear();          t.setTransformationType(Transformation.TYPE_MATRIX);                    // If the child is in the center, we do not rotate it.          if (childCenter == mCoveflowCenter)          {              transformImageBitmap(child, t, 0);          }          else          {              // Calculate the rotation angle.              rotationAngle = (int)(((float)(mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);                            // Make the angle is not bigger than maximum.              if (Math.abs(rotationAngle) > mMaxRotationAngle)              {                  rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;              }                            transformImageBitmap(child, t, rotationAngle);          }                    return true;      }

这个方法就是根据child来计算它的transformation(变换),我们需要去修改它里面的matrix,从而达到旋转的效果。根据位置和角度来计算的matrix的方法写在另外一个方法transformImageBitmap中实现。

  • transformImageBitmap()的实现

    private void transformImageBitmap(View child, Transformation t, int rotationAngle)      {          mCamera.save();                    final Matrix imageMatrix = t.getMatrix();          final int imageHeight = child.getHeight();          final int imageWidth  = child.getWidth();          final int rotation    = Math.abs(rotationAngle);                    // Zoom on Z axis.          mCamera.translate(0, 0, mMaxZoom);                    if (rotation < mMaxRotationAngle)          {              float zoomAmount = (float)(mMaxZoom + rotation * 1.5f);              mCamera.translate(0, 0, zoomAmount);          }                    // Rotate the camera on Y axis.          mCamera.rotateY(rotationAngle);          // Get the matrix from the camera, in fact, the matrix is S (scale) transformation.          mCamera.getMatrix(imageMatrix);                    // The matrix final is T2 * S * T1, first translate the center point to (0, 0),           // then scale, and then translate the center point to its original point.          // T * S * T                    // S * T1          imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));          // (T2 * S) * T1          imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));                    mCamera.restore();      }

这里,简单说明一个,

        第一,先在Z轴上平称,其实就是得到一个缩放矩阵变换,我这里简写为 S。

        第二,是利用camera这个类来生成matrix,其实mCamera.rotateY就是围绕Y轴旋转。这里生成了一个旋转矩阵,记为 R 。经过这两步,此时调用mCamera.getMatrix(imageMatrix); 从Camera中得到matrix,此时这个矩阵中包含了S * R。

        第三,最关键是下面两句       

       // S * T1          imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));          // (T2 * S) * T1          imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
        由于这里涉及到旋转与缩放,缩放操作其实应该是针对Child中点进行了,这里就是作一个平衡操作,我们必须是先平移,再缩放,再平移回原来位置,所以,我们最终的矩阵变换应该是这样的:

        M = T * (S * R) * T1   (这里在T1表示与T相反)。

三,完整代码

GalleryFlow.java

import android.content.Context;  import android.graphics.Camera;  import android.graphics.Matrix;  import android.util.AttributeSet;  import android.view.View;  import android.view.animation.Transformation;  import android.widget.Gallery;    public class GalleryFlow extends Gallery  {      /**       * The camera class is used to 3D transformation matrix.       */      private Camera mCamera = new Camera();            /**       * The max rotation angle.       */      private int mMaxRotationAngle = 60;            /**       * The max zoom value (Z axis).       */      private int mMaxZoom = -120;            /**       * The center of the gallery.       */      private int mCoveflowCenter = 0;            public GalleryFlow(Context context)      {          this(context, null);      }            public GalleryFlow(Context context, AttributeSet attrs)      {          this(context, attrs, 0);      }            public GalleryFlow(Context context, AttributeSet attrs, int defStyle)      {          super(context, attrs, defStyle);                    // Enable set transformation.          this.setStaticTransformationsEnabled(true);          // Enable set the children drawing order.          this.setChildrenDrawingOrderEnabled(true);      }            public int getMaxRotationAngle()      {          return mMaxRotationAngle;      }            public void setMaxRotationAngle(int maxRotationAngle)      {          mMaxRotationAngle = maxRotationAngle;      }            public int getMaxZoom()      {          return mMaxZoom;      }            public void setMaxZoom(int maxZoom)      {          mMaxZoom = maxZoom;      }            @Override      protected int getChildDrawingOrder(int childCount, int i)      {          // Current selected index.          int selectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();          if (selectedIndex < 0)           {              return i;          }                    if (i < selectedIndex)          {              return i;          }          else if (i >= selectedIndex)          {              return childCount - 1 - i + selectedIndex;          }          else          {              return i;          }      }            @Override      protected void onSizeChanged(int w, int h, int oldw, int oldh)      {          mCoveflowCenter = getCenterOfCoverflow();          super.onSizeChanged(w, h, oldw, oldh);      }            private int getCenterOfView(View view)      {          return view.getLeft() + view.getWidth() / 2;      }            @Override      protected boolean getChildStaticTransformation(View child, Transformation t)      {          super.getChildStaticTransformation(child, t);                    final int childCenter = getCenterOfView(child);          final int childWidth  = child.getWidth();                    int rotationAngle = 0;          t.clear();          t.setTransformationType(Transformation.TYPE_MATRIX);                    // If the child is in the center, we do not rotate it.          if (childCenter == mCoveflowCenter)          {              transformImageBitmap(child, t, 0);          }          else          {              // Calculate the rotation angle.              rotationAngle = (int)(((float)(mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);                            // Make the angle is not bigger than maximum.              if (Math.abs(rotationAngle) > mMaxRotationAngle)              {                  rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;              }                            transformImageBitmap(child, t, rotationAngle);          }                    return true;      }            private int getCenterOfCoverflow()      {          return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();      }            private void transformImageBitmap(View child, Transformation t, int rotationAngle)      {          mCamera.save();                    final Matrix imageMatrix = t.getMatrix();          final int imageHeight = child.getHeight();          final int imageWidth  = child.getWidth();          final int rotation    = Math.abs(rotationAngle);                    // Zoom on Z axis.          mCamera.translate(0, 0, mMaxZoom);                    if (rotation < mMaxRotationAngle)          {              float zoomAmount = (float)(mMaxZoom + rotation * 1.5f);              mCamera.translate(0, 0, zoomAmount);          }                    // Rotate the camera on Y axis.          mCamera.rotateY(rotationAngle);          // Get the matrix from the camera, in fact, the matrix is S (scale) transformation.          mCamera.getMatrix(imageMatrix);                    // The matrix final is T2 * S * T1, first translate the center point to (0, 0),           // then scale, and then translate the center point to its original point.          // T * S * T                    // S * T1          imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));          // (T2 * S) * T1          imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));                    mCamera.restore();      }  }

BitmapUtil.java

package com.lee.gallery3d.utils;    import android.graphics.Bitmap;  import android.graphics.Bitmap.Config;  import android.graphics.Canvas;  import android.graphics.LinearGradient;  import android.graphics.Matrix;  import android.graphics.Paint;  import android.graphics.PixelFormat;  import android.graphics.PorterDuffXfermode;  import android.graphics.Shader.TileMode;  import android.graphics.drawable.Drawable;    public class BitmapUtil  {      public static Bitmap createReflectedBitmap(Bitmap srcBitmap)      {          if (null == srcBitmap)          {              return null;          }                    // The gap between the reflection bitmap and original bitmap.           final int REFLECTION_GAP = 4;                    int srcWidth  = srcBitmap.getWidth();          int srcHeight = srcBitmap.getHeight();          int reflectionWidth  = srcBitmap.getWidth();          int reflectionHeight = srcBitmap.getHeight() / 2;                    if (0 == srcWidth || srcHeight == 0)          {              return null;          }                    // The matrix          Matrix matrix = new Matrix();          matrix.preScale(1, -1);                    try          {              // The reflection bitmap, width is same with original's, height is half of original's.              Bitmap reflectionBitmap = Bitmap.createBitmap(                      srcBitmap,                      0,                       srcHeight / 2,                      srcWidth,                       srcHeight / 2,                      matrix,                      false);                            if (null == reflectionBitmap)              {                  return null;              }                            // Create the bitmap which contains original and reflection bitmap.              Bitmap bitmapWithReflection = Bitmap.createBitmap(                      reflectionWidth,                      srcHeight + reflectionHeight + REFLECTION_GAP,                       Config.ARGB_8888);                            if (null == bitmapWithReflection)              {                  return null;              }                            // Prepare the canvas to draw stuff.              Canvas canvas = new Canvas(bitmapWithReflection);                            // Draw the original bitmap.              canvas.drawBitmap(srcBitmap, 0, 0, null);                            // Draw the reflection bitmap.              canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);                            Paint paint = new Paint();              paint.setAntiAlias(true);              LinearGradient shader = new LinearGradient(                      0,                       srcHeight,                       0,                       bitmapWithReflection.getHeight() + REFLECTION_GAP,                       0x70FFFFFF,                       0x00FFFFFF,                      TileMode.MIRROR);              paint.setShader(shader);              paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));                            // Draw the linear shader.              canvas.drawRect(                      0,                       srcHeight,                       srcWidth,                       bitmapWithReflection.getHeight() + REFLECTION_GAP,                       paint);                            return bitmapWithReflection;          }          catch (Exception e)          {              e.printStackTrace();          }                    return null;      }  }
来自:http://blog.csdn.net/leehong2005/article/details/8070538