Android gallery 3D效果

yangxi_001發表於2014-06-30

在看了iOS上面的CoverFlow後,感覺效果真的不錯,就想在android上面實現一個,這個程式在網上參考了一此核心的程式碼,當然我新增了一些其他的東西,廢話不多說,先看效果,不然就是無圖無真相。

        Demo下載地址:GalleryFlow



其實實現這個效果很簡單,下面作一個簡單的介紹


一,建立倒影效果

這個基本思路是:

1,建立一個源圖一樣的圖,利用martrix將圖片旋轉180度。這個倒影圖的高是源圖的一半。

[java] view plaincopy
  1. Matrix matrix = new Matrix();  
  2.   
  3. // 1表示放大比例,不放大也不縮小。  
  4. // -1表示在y軸上相反,即旋轉180度。  
  5. matrix.preScale(1, -1);  
  6.   
  7. Bitmap reflectionBitmap = Bitmap.createBitmap(  
  8.     srcBitmap,  
  9.     0,   
  10.     srcBitmap.getHeight() / 2,  // top為源圖的一半  
  11.     srcBitmap.getWidth(),       // 寬度與源圖一樣  
  12.     srcBitmap.getHeight() / 2,  // 高度與源圖的一半  
  13.     matrix,  
  14.     false);  

2,建立一個最終效果的圖,即源圖 + 間隙 + 倒影。

[java] view plaincopy
  1. final int REFLECTION_GAP = 5;  
  2.   
  3. Bitmap bitmapWithReflection = Bitmap.createBitmap(  
  4.        reflectionWidth,  
  5.        srcHeight + reflectionHeight + REFLECTION_GAP,   
  6.        Config.ARGB_8888);  

3,依次將源圖、倒影圖繪製在最終的bitmap上面。

[java] view plaincopy
  1. // Prepare the canvas to draw stuff.  
  2. Canvas canvas = new Canvas(bitmapWithReflection);  
  3.               
  4. // Draw the original bitmap.  
  5. canvas.drawBitmap(srcBitmap, 00null);  
  6.               
  7. // Draw the reflection bitmap.  
  8. canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);  
4,建立LinearGradient,從而給定一個由上到下的漸變色。

[java] view plaincopy
  1. Paint paint = new Paint();  
  2. paint.setAntiAlias(true);  
  3. LinearGradient shader = new LinearGradient(  
  4.         0,   
  5.         srcHeight,   
  6.         0,   
  7.         bitmapWithReflection.getHeight() + REFLECTION_GAP,   
  8.         0x70FFFFFF,   
  9.         0x00FFFFFF,  
  10.         TileMode.MIRROR);  
  11. paint.setShader(shader);  
  12. paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));  
  13.   
  14. // Draw the linear shader.  
  15. canvas.drawRect(  
  16.         0,   
  17.         srcHeight,   
  18.         srcWidth,   
  19.         bitmapWithReflection.getHeight() + REFLECTION_GAP,   
  20.         paint);  

二,擴充套件Gallery

擴充套件系統的gallery,我們需要重寫兩個方法,getChildStaticTransformation()和getChildDrawingOrder(),同時,要使這兩個方法能被呼叫,必須執行如下兩行程式碼,文件上面是有說明的。

[java] view plaincopy
  1. // Enable set transformation.  
  2. this.setStaticTransformationsEnabled(true);  
  3. // Enable set the children drawing order.  
  4. this.setChildrenDrawingOrderEnabled(true);  

  • getChildDrawingOrder的實現

[java] view plaincopy
  1. @Override  
  2. protected int getChildDrawingOrder(int childCount, int i)  
  3. {  
  4.     // Current selected index.  
  5.     int selectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();  
  6.     if (selectedIndex < 0)   
  7.     {  
  8.         return i;  
  9.     }  
  10.       
  11.     if (i < selectedIndex)  
  12.     {  
  13.         return i;  
  14.     }  
  15.     else if (i >= selectedIndex)  
  16.     {  
  17.         return childCount - 1 - i + selectedIndex;  
  18.     }  
  19.     else  
  20.     {  
  21.         return i;  
  22.     }  
  23. }  

這裡為什麼要計算drawing order,因為從上圖中看到,我們的效果是:中間左邊的順序是 0, 1, 2,右邊的child覆蓋左邊的child,而在中間右邊的順序正好相反,左邊的覆蓋右邊的,所以我們要重寫這個方法,而gallery自身的實現,不是這種效果。

  • getChildStaticTransformation的實現

[java] view plaincopy
  1. @Override  
  2. protected boolean getChildStaticTransformation(View child, Transformation t)  
  3. {  
  4.     super.getChildStaticTransformation(child, t);  
  5.       
  6.     final int childCenter = getCenterOfView(child);  
  7.     final int childWidth  = child.getWidth();  
  8.       
  9.     int rotationAngle = 0;  
  10.     t.clear();  
  11.     t.setTransformationType(Transformation.TYPE_MATRIX);  
  12.       
  13.     // If the child is in the center, we do not rotate it.  
  14.     if (childCenter == mCoveflowCenter)  
  15.     {  
  16.         transformImageBitmap(child, t, 0);  
  17.     }  
  18.     else  
  19.     {  
  20.         // Calculate the rotation angle.  
  21.         rotationAngle = (int)(((float)(mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);  
  22.           
  23.         // Make the angle is not bigger than maximum.  
  24.         if (Math.abs(rotationAngle) > mMaxRotationAngle)  
  25.         {  
  26.             rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;  
  27.         }  
  28.           
  29.         transformImageBitmap(child, t, rotationAngle);  
  30.     }  
  31.       
  32.     return true;  
  33. }  

這個方法就是根據child來計算它的transformation(變換),我們需要去修改它裡面的matrix,從而達到旋轉的效果。根據位置和角度來計算的matrix的方法寫在另外一個方法transformImageBitmap中實現。

  • transformImageBitmap()的實現

[java] view plaincopy
  1. private void transformImageBitmap(View child, Transformation t, int rotationAngle)  
  2. {  
  3.     mCamera.save();  
  4.       
  5.     final Matrix imageMatrix = t.getMatrix();  
  6.     final int imageHeight = child.getHeight();  
  7.     final int imageWidth  = child.getWidth();  
  8.     final int rotation    = Math.abs(rotationAngle);  
  9.       
  10.     // Zoom on Z axis.  
  11.     mCamera.translate(00, mMaxZoom);  
  12.       
  13.     if (rotation < mMaxRotationAngle)  
  14.     {  
  15.         float zoomAmount = (float)(mMaxZoom + rotation * 1.5f);  
  16.         mCamera.translate(00, zoomAmount);  
  17.     }  
  18.       
  19.     // Rotate the camera on Y axis.  
  20.     mCamera.rotateY(rotationAngle);  
  21.     // Get the matrix from the camera, in fact, the matrix is S (scale) transformation.  
  22.     mCamera.getMatrix(imageMatrix);  
  23.       
  24.     // The matrix final is T2 * S * T1, first translate the center point to (0, 0),   
  25.     // then scale, and then translate the center point to its original point.  
  26.     // T * S * T  
  27.       
  28.     // S * T1  
  29.     imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));  
  30.     // (T2 * S) * T1  
  31.     imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));  
  32.       
  33.     mCamera.restore();  
  34. }  

這裡,簡單說明一個,

        第一,先在Z軸上平稱,其實就是得到一個縮放矩陣變換,我這裡簡寫為 S。

        第二,是利用camera這個類來生成matrix,其實mCamera.rotateY就是圍繞Y軸旋轉。這裡生成了一個旋轉矩陣,記為 R 。經過這兩步,此時呼叫mCamera.getMatrix(imageMatrix); 從Camera中得到matrix,此時這個矩陣中包含了S * R。

        第三,最關鍵是下面兩句       

[java] view plaincopy
  1. // S * T1  
  2.  imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));  
  3.  // (T2 * S) * T1  
  4.  imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));  
        由於這裡涉及到旋轉與縮放,縮放操作其實應該是針對Child中點進行了,這裡就是作一個平衡操作,我們必須是先平移,再縮放,再平移回原來位置,所以,我們最終的矩陣變換應該是這樣的:

        M = T * (S * R) * T1   (這裡在T1表示與T相反)。

三,完整程式碼

GalleryFlow.java

[java] view plaincopy
  1. import android.content.Context;  
  2. import android.graphics.Camera;  
  3. import android.graphics.Matrix;  
  4. import android.util.AttributeSet;  
  5. import android.view.View;  
  6. import android.view.animation.Transformation;  
  7. import android.widget.Gallery;  
  8.   
  9. public class GalleryFlow extends Gallery  
  10. {  
  11.     /** 
  12.      * The camera class is used to 3D transformation matrix. 
  13.      */  
  14.     private Camera mCamera = new Camera();  
  15.       
  16.     /** 
  17.      * The max rotation angle. 
  18.      */  
  19.     private int mMaxRotationAngle = 60;  
  20.       
  21.     /** 
  22.      * The max zoom value (Z axis). 
  23.      */  
  24.     private int mMaxZoom = -120;  
  25.       
  26.     /** 
  27.      * The center of the gallery. 
  28.      */  
  29.     private int mCoveflowCenter = 0;  
  30.       
  31.     public GalleryFlow(Context context)  
  32.     {  
  33.         this(context, null);  
  34.     }  
  35.       
  36.     public GalleryFlow(Context context, AttributeSet attrs)  
  37.     {  
  38.         this(context, attrs, 0);  
  39.     }  
  40.       
  41.     public GalleryFlow(Context context, AttributeSet attrs, int defStyle)  
  42.     {  
  43.         super(context, attrs, defStyle);  
  44.           
  45.         // Enable set transformation.  
  46.         this.setStaticTransformationsEnabled(true);  
  47.         // Enable set the children drawing order.  
  48.         this.setChildrenDrawingOrderEnabled(true);  
  49.     }  
  50.       
  51.     public int getMaxRotationAngle()  
  52.     {  
  53.         return mMaxRotationAngle;  
  54.     }  
  55.       
  56.     public void setMaxRotationAngle(int maxRotationAngle)  
  57.     {  
  58.         mMaxRotationAngle = maxRotationAngle;  
  59.     }  
  60.       
  61.     public int getMaxZoom()  
  62.     {  
  63.         return mMaxZoom;  
  64.     }  
  65.       
  66.     public void setMaxZoom(int maxZoom)  
  67.     {  
  68.         mMaxZoom = maxZoom;  
  69.     }  
  70.       
  71.     @Override  
  72.     protected int getChildDrawingOrder(int childCount, int i)  
  73.     {  
  74.         // Current selected index.  
  75.         int selectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();  
  76.         if (selectedIndex < 0)   
  77.         {  
  78.             return i;  
  79.         }  
  80.           
  81.         if (i < selectedIndex)  
  82.         {  
  83.             return i;  
  84.         }  
  85.         else if (i >= selectedIndex)  
  86.         {  
  87.             return childCount - 1 - i + selectedIndex;  
  88.         }  
  89.         else  
  90.         {  
  91.             return i;  
  92.         }  
  93.     }  
  94.       
  95.     @Override  
  96.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
  97.     {  
  98.         mCoveflowCenter = getCenterOfCoverflow();  
  99.         super.onSizeChanged(w, h, oldw, oldh);  
  100.     }  
  101.       
  102.     private int getCenterOfView(View view)  
  103.     {  
  104.         return view.getLeft() + view.getWidth() / 2;  
  105.     }  
  106.       
  107.     @Override  
  108.     protected boolean getChildStaticTransformation(View child, Transformation t)  
  109.     {  
  110.         super.getChildStaticTransformation(child, t);  
  111.           
  112.         final int childCenter = getCenterOfView(child);  
  113.         final int childWidth  = child.getWidth();  
  114.           
  115.         int rotationAngle = 0;  
  116.         t.clear();  
  117.         t.setTransformationType(Transformation.TYPE_MATRIX);  
  118.           
  119.         // If the child is in the center, we do not rotate it.  
  120.         if (childCenter == mCoveflowCenter)  
  121.         {  
  122.             transformImageBitmap(child, t, 0);  
  123.         }  
  124.         else  
  125.         {  
  126.             // Calculate the rotation angle.  
  127.             rotationAngle = (int)(((float)(mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);  
  128.               
  129.             // Make the angle is not bigger than maximum.  
  130.             if (Math.abs(rotationAngle) > mMaxRotationAngle)  
  131.             {  
  132.                 rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;  
  133.             }  
  134.               
  135.             transformImageBitmap(child, t, rotationAngle);  
  136.         }  
  137.           
  138.         return true;  
  139.     }  
  140.       
  141.     private int getCenterOfCoverflow()  
  142.     {  
  143.         return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();  
  144.     }  
  145.       
  146.     private void transformImageBitmap(View child, Transformation t, int rotationAngle)  
  147.     {  
  148.         mCamera.save();  
  149.           
  150.         final Matrix imageMatrix = t.getMatrix();  
  151.         final int imageHeight = child.getHeight();  
  152.         final int imageWidth  = child.getWidth();  
  153.         final int rotation    = Math.abs(rotationAngle);  
  154.           
  155.         // Zoom on Z axis.  
  156.         mCamera.translate(00, mMaxZoom);  
  157.           
  158.         if (rotation < mMaxRotationAngle)  
  159.         {  
  160.             float zoomAmount = (float)(mMaxZoom + rotation * 1.5f);  
  161.             mCamera.translate(00, zoomAmount);  
  162.         }  
  163.           
  164.         // Rotate the camera on Y axis.  
  165.         mCamera.rotateY(rotationAngle);  
  166.         // Get the matrix from the camera, in fact, the matrix is S (scale) transformation.  
  167.         mCamera.getMatrix(imageMatrix);  
  168.           
  169.         // The matrix final is T2 * S * T1, first translate the center point to (0, 0),   
  170.         // then scale, and then translate the center point to its original point.  
  171.         // T * S * T  
  172.           
  173.         // S * T1  
  174.         imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));  
  175.         // (T2 * S) * T1  
  176.         imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));  
  177.           
  178.         mCamera.restore();  
  179.     }  
  180. }  

BitmapUtil.java

[java] view plaincopy
  1. package com.lee.gallery3d.utils;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.Bitmap.Config;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.LinearGradient;  
  7. import android.graphics.Matrix;  
  8. import android.graphics.Paint;  
  9. import android.graphics.PixelFormat;  
  10. import android.graphics.PorterDuffXfermode;  
  11. import android.graphics.Shader.TileMode;  
  12. import android.graphics.drawable.Drawable;  
  13.   
  14. public class BitmapUtil  
  15. {  
  16.     public static Bitmap createReflectedBitmap(Bitmap srcBitmap)  
  17.     {  
  18.         if (null == srcBitmap)  
  19.         {  
  20.             return null;  
  21.         }  
  22.           
  23.         // The gap between the reflection bitmap and original bitmap.   
  24.         final int REFLECTION_GAP = 4;  
  25.           
  26.         int srcWidth  = srcBitmap.getWidth();  
  27.         int srcHeight = srcBitmap.getHeight();  
  28.         int reflectionWidth  = srcBitmap.getWidth();  
  29.         int reflectionHeight = srcBitmap.getHeight() / 2;  
  30.           
  31.         if (0 == srcWidth || srcHeight == 0)  
  32.         {  
  33.             return null;  
  34.         }  
  35.           
  36.         // The matrix  
  37.         Matrix matrix = new Matrix();  
  38.         matrix.preScale(1, -1);  
  39.           
  40.         try  
  41.         {  
  42.             // The reflection bitmap, width is same with original's, height is half of original's.  
  43.             Bitmap reflectionBitmap = Bitmap.createBitmap(  
  44.                     srcBitmap,  
  45.                     0,   
  46.                     srcHeight / 2,  
  47.                     srcWidth,   
  48.                     srcHeight / 2,  
  49.                     matrix,  
  50.                     false);  
  51.               
  52.             if (null == reflectionBitmap)  
  53.             {  
  54.                 return null;  
  55.             }  
  56.               
  57.             // Create the bitmap which contains original and reflection bitmap.  
  58.             Bitmap bitmapWithReflection = Bitmap.createBitmap(  
  59.                     reflectionWidth,  
  60.                     srcHeight + reflectionHeight + REFLECTION_GAP,   
  61.                     Config.ARGB_8888);  
  62.               
  63.             if (null == bitmapWithReflection)  
  64.             {  
  65.                 return null;  
  66.             }  
  67.               
  68.             // Prepare the canvas to draw stuff.  
  69.             Canvas canvas = new Canvas(bitmapWithReflection);  
  70.               
  71.             // Draw the original bitmap.  
  72.             canvas.drawBitmap(srcBitmap, 00null);  
  73.               
  74.             // Draw the reflection bitmap.  
  75.             canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);  
  76.               
  77.             Paint paint = new Paint();  
  78.             paint.setAntiAlias(true);  
  79.             LinearGradient shader = new LinearGradient(  
  80.                     0,   
  81.                     srcHeight,   
  82.                     0,   
  83.                     bitmapWithReflection.getHeight() + REFLECTION_GAP,   
  84.                     0x70FFFFFF,   
  85.                     0x00FFFFFF,  
  86.                     TileMode.MIRROR);  
  87.             paint.setShader(shader);  
  88.             paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));  
  89.               
  90.             // Draw the linear shader.  
  91.             canvas.drawRect(  
  92.                     0,   
  93.                     srcHeight,   
  94.                     srcWidth,   
  95.                     bitmapWithReflection.getHeight() + REFLECTION_GAP,   
  96.                     paint);  
  97.               
  98.             return bitmapWithReflection;  
  99.         }  
  100.         catch (Exception e)  
  101.         {  
  102.             e.printStackTrace();  
  103.         }  
  104.           
  105.         return null;  
  106.     }  
  107. }  

相關文章