轉載請註明出處:http://blog.csdn.net/allen315410/article/details/39932689html
咱們知道android系統已經爲咱們提供好了一個展現圖片的「容器」——Gallery,但是這個Gallery顯示的效果是平面化的,動態效果不強。java
這裏,咱們動手作一個本身定義的Gallery組件。實現圖片的3D效果展現。想一想應該不錯吧。先看看效果圖:android
實現這個3D效果的Gallery該怎麼作呢?首先。分析一下,canvas
1,展現圖片。系統自帶Gallery組件,可以基於這個Gallery組件擴展咱們所需要的效果。數組
2。展現效果需要進行3D成像。緩存
3,展現的圖片下方需要顯示圖片的倒影。數據結構
4。展現圖片的倒影需要加上「遮罩」效果。app
好了,問題列好了,咱們一個個來解決吧!ide
代碼量很少,直接上代碼好了。工具
package com.example.gallery.view; 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; import android.widget.ImageView; @SuppressWarnings("deprecation") public class CustomGallery extends Gallery { /** Gallery的中心點 */ private int galleryCenterPoint = 0; /** 攝像機對象 */ private Camera camera; public CustomGallery(Context context, AttributeSet attrs) { super(context, attrs); // 啓動getChildStaticTransformation setStaticTransformationsEnabled(true); camera = new Camera(); } /** * 當Gallery的寬和高改變時回調此方法。第一次計算gallery的寬和高時,也會調用此方法 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // TODO Auto-generated method stub super.onSizeChanged(w, h, oldw, oldh); galleryCenterPoint = getGalleryCenterPoint(); } /** * 返回gallery的item的子圖形的變換效果 * * @param t * 指定當前item的變換效果 */ @Override protected boolean getChildStaticTransformation(View child, Transformation t) { int viewCenterPoint = getViewCenterPoint(child); // view的中心點 int rotateAngle = 0; // 旋轉角度,默以爲0 // 假設view的中心點不等於gallery中心,兩邊圖片需要計算旋轉的角度 if (viewCenterPoint != galleryCenterPoint) { // gallery中心點 - view中心點 = 差值 int diff = galleryCenterPoint - viewCenterPoint; // 差值 / 圖片的寬度 = 比值 float scale = (float) diff / (float) child.getWidth(); // 比值 * 最大旋轉角度 = 終於view的旋轉角度(最大旋轉角度定爲50度) rotateAngle = (int) (scale * 50); if (Math.abs(rotateAngle) > 50) {// 當終於旋轉角度 》 最大旋轉角度,要改爲50或-50 rotateAngle = rotateAngle > 0 ?代碼中有凝視。你們可以看着凝視理解代碼,我在這裏要是說怎麼考慮的,顯得特別麻煩!這裏另外一個很是重要的概念——矩陣。這個我留在如下去解說。往下看吧。50 : -50; } } // 設置變換效果前。需要把Transformation中的上一個item的變換效果清除 t.clear(); t.setTransformationType(Transformation.TYPE_MATRIX); // 設置變換效果的類型爲矩陣類型 startTransformationItem((ImageView) child, rotateAngle, t); return true; } /** * 設置變換的效果 * * @param iv * gallery的item * @param rotateAngle * 旋轉的角度 * @param t * 變換的對象 */ private void startTransformationItem(ImageView iv, int rotateAngle, Transformation t) { camera.save(); // 保存狀態 int absRotateAngle = Math.abs(rotateAngle); // 1.放大效果(中間的圖片要比兩邊的圖片大) camera.translate(0, 0, 100f); // 給攝像機定位 int zoom = -250 + (absRotateAngle * 2); camera.translate(0, 0, zoom); // 2.透明度(中間的圖片全然顯示,兩邊有必定的透明度) int alpha = (int) (255 - (absRotateAngle * 2.5)); iv.setAlpha(alpha); // 3.旋轉(中間的圖片沒有旋轉角度。僅僅要不在中間的圖片都有旋轉角度) camera.rotateY(rotateAngle); Matrix matrix = t.getMatrix(); // 變換的矩陣,將變換效果加入到矩陣中 camera.getMatrix(matrix); // 把matrix矩陣給camera對象。camera對象會把上面加入的效果轉換成矩陣加入到matrix對象中 matrix.preTranslate(-iv.getWidth() / 2, -iv.getHeight() / 2); // 矩陣前乘 matrix.postTranslate(iv.getWidth() / 2, iv.getHeight() / 2); // 矩陣後乘 camera.restore(); // 恢復以前保存的狀態 } /** * 獲取Gallery的中心點 * * @return */ private int getGalleryCenterPoint() { return this.getWidth() / 2; } /** * 獲取item上view的中心點 * * @param v * @return */ private int getViewCenterPoint(View v) { return v.getWidth() / 2 + v.getLeft(); // 圖片寬度的一半+圖片距離屏幕左邊距 } }
獲取圖片的工具類:
package com.example.gallery.view; import java.lang.ref.SoftReference; import java.util.Hashtable; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Shader.TileMode; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.util.Log; public class ImageUtil { private static final String TAG = "ImageUtil"; /** 緩存集合 */ private static Hashtable<Integer, SoftReference<Bitmap>> mImageCache // = new Hashtable<Integer, SoftReference<Bitmap>>(); /** * 依據id返回一個處理後的圖片 * * @param res * @param resID * @return */ public static Bitmap getImageBitmap(Resources res, int resID) { // 先去集合中取當前resID是否已經拿過圖片,假設集合中有,說明已經拿過。直接使用集合中的圖片返回 SoftReference<Bitmap> reference = mImageCache.get(resID); if (reference != null) { Bitmap bitmap = reference.get(); if (bitmap != null) {// 從內存中取 Log.i(TAG, "從內存中取"); return bitmap; } } // 假設集合中沒有。就調用getInvertImage獲得一個圖片,需要向集合中保留一張,最後返回當前圖片 Log.i(TAG, "又一次載入"); Bitmap invertBitmap = getInvertBitmap(res, resID); // 在集合中保存一份,便於下次獲取時直接在集合中獲取 mImageCache.put(resID, new SoftReference<Bitmap>(invertBitmap)); return invertBitmap; } /** * 依據圖片的id,獲取處處理以後的圖片 * * @param resID * @return */ public static Bitmap getInvertBitmap(Resources res, int resID) { // 1.獲取原圖 Bitmap sourceBitmap = BitmapFactory.decodeResource(res, resID); // 2.生成倒影圖片 Matrix m = new Matrix(); // 圖片矩陣 m.setScale(1.0f, -1.0f); // 讓圖片依照矩陣進行反轉 Bitmap invertBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight() / 2, m, false); // 3.兩張圖片合成一張圖片 Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), (int) (sourceBitmap.getHeight() * 1.5 + 5), Config.ARGB_8888); Canvas canvas = new Canvas(resultBitmap); // 爲合成圖片指定一個畫板 canvas.drawBitmap(sourceBitmap, 0f, 0f, null); // 將原圖片畫在畫布的上方 canvas.drawBitmap(invertBitmap, 0f, sourceBitmap.getHeight() + 5, null); // 將倒影圖片畫在畫布的下方 // 4.加入遮罩效果 Paint paint = new Paint(); // 設置遮罩的顏色。這裏使用的是線性梯度 LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + 5, 0, resultBitmap.getHeight(), 0x70ffffff, 0x00ffffff, TileMode.CLAMP); paint.setShader(shader); // 設置模式爲:遮罩,取交集 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); canvas.drawRect(0, sourceBitmap.getHeight() + 5, sourceBitmap.getWidth(), resultBitmap.getHeight(), paint); return resultBitmap; } }這個工具類就是獲取整個圖片的,包含實現圖片的倒影和遮罩效果。看凝視。這裏需要解說的是,假設避免OOM,這是一個較複雜的概念,不是一兩句話就能講清楚的。android下載入圖片很是easy就處理OOM。固然了,避免OOM的方式有很是多,我在這是使用了內存緩存機制來避免了,即便用Java給咱們提供好的「軟引用」來解決。接下來,就是怎麼引用這個畫廊組件了。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" > <com.example.gallery.view.CustomGallery android:id="@+id/customgallery" android:layout_width="match_parent" android:layout_height="match_parent" > </com.example.gallery.view.CustomGallery> </RelativeLayout>
package com.example.gallery; import com.example.gallery.view.CustomGallery; import com.example.gallery.view.ImageUtil; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Gallery.LayoutParams; import android.widget.ImageView; public class MainActivity extends Activity { /** 圖片資源數組 */ private int[] imageResIDs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageResIDs = new int[]{// R.drawable.imgres_01, // R.drawable.imgres_02, // R.drawable.imgres_03, // R.drawable.imgres_04, // R.drawable.imgres_05, // R.drawable.imgres_06, // R.drawable.imgres_07, // R.drawable.imgres_08, // R.drawable.imgres_01, // R.drawable.imgres_02, // R.drawable.imgres_03, // R.drawable.imgres_04, // R.drawable.imgres_05, // R.drawable.imgres_06, // R.drawable.imgres_07, // R.drawable.imgres_08 // }; CustomGallery customGallery = (CustomGallery) findViewById(R.id.customgallery); ImageAdapter adapter = new ImageAdapter(); customGallery.setAdapter(adapter); } public class ImageAdapter extends BaseAdapter { @Override public int getCount() { // TODO Auto-generated method stub return imageResIDs.length; } @Override public Object getItem(int position) { // TODO Auto-generated method stub return imageResIDs[position]; } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ImageView imageView; if (convertView != null) { imageView = (ImageView) convertView; } else { imageView = new ImageView(MainActivity.this); } Bitmap bitmap = ImageUtil.getImageBitmap(getResources(), imageResIDs[position]); BitmapDrawable drawable = new BitmapDrawable(bitmap); drawable.setAntiAlias(true); // 消除鋸齒 imageView.setImageDrawable(drawable); LayoutParams params = new LayoutParams(240, 320); imageView.setLayoutParams(params); return imageView; } } }
===========================================華麗麗的切割線=============================================
UI開發過程當中,咱們經常需要對圖片進行處理。常見的如貼圖,複雜一些的還有位置變換、旋轉、濾鏡特效等。如下簡介一下關於圖片處理的一些基本知識和原理。
1 基本概念
對於圖片的處理。最常使用到的數據結構是Bitmap,它包括了一張圖片所有的數據,這些數據數據包括那些內容呢?簡單說來就是由點陣和顏色值組成的,所謂點陣就是一個在概念上是Width * Height的矩陣。每一個元素相應着圖片的一個像素,也就是說,點陣保存着圖片的空間位置信息;而顏色值即ARGB,分別相應透明度、紅、綠、藍這四個通道份量,每一個通道用8比特定義,因此一個顏色值就是一個int整型。可以表示256*256*256種顏色值。
Android中咱們常用到這麼幾個常量:ARGB_888八、ARGB_444四、RGB_565。
這幾個常量事實上就是告訴系統怎樣對圖片的顏色值進行處理,好比ARGB_8888是告訴系統透明度、R、G、B在顏色值中分別用8bit表示,這時顏色值爲32bit。這種定義能夠表示最多的顏色值。圖片質量也是最好的。ARGB_4444則是每個通道用4bit表示。這樣顏色值僅僅用16bit。節省了空間,但是卻僅僅能表示16*16*16種顏色,也就是說圖片很是失去很是多彩色信息;RGB_565類型的顏色值相同是16bit,但是它丟棄了透明度信息,能夠表示32*64*32種顏色值。
2 顏色矩陣
顏色矩陣是一個5*4的矩陣,用來對圖片顏色值進行處理。
定義顏色矩陣和顏色值例如如下例如如下:
進行例如如下矩陣運算:
結果R爲4*1的矩陣,這個矩陣就是新的顏色值,R中每個通道的值分別例如如下:
R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;
這樣看起來也許很是抽象,很是難理解顏色矩陣和結果R直接的關係,咱們若是顏色矩陣值例如如下所看到的:
那麼結果爲:
R’ = R;
G’ = G;
B’ = B;
A’ = A;
也就是說,新的顏色值跟原先的同樣!再看一個樣例。顏色矩陣取值爲:
結果爲:
R’ = R + 100;
G’ = G + 100;
B’ = B;
A’ = A;
新的顏色值中,紅色通道值和綠色通道值分別添加了100,此時圖片會泛黃(因爲R + G = Yellow)。
從上面的幾個樣例咱們很是easy就能明確顏色矩陣中的每個份量(每一列)的意義:
第一行決定紅色,
第二行決定綠色。
第三行決定藍色。
第四行決定了透明度。
第五列是顏色的偏移量。
至此咱們應該能理解怎樣經過顏色矩陣來改變顏色值的各個份量了。
如下是用於Android的一段代碼。用於將圖片處理成泛黃的效果:
public static Bitmap testBitmap(Bitmap bitmap){ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.RGB_565); Canvas canvas = new Canvas(output); Paint paint = new Paint(); ColorMatrix cm = new ColorMatrix(); float[] array = {1,0,0,0,100, 0,1,0,0,100, 0,0,1,0,0, 0,0,0,1,0}; cm.set(array); paint.setColorFilter(new ColorMatrixColorFilter(cm)); canvas.drawBitmap(bitmap, 0, 0, paint); return output; }3 座標變換矩陣
座標變換矩陣是一個3*3的矩陣,經過與一個相似(X,Y,1)的座標值的矩陣乘法運算。能夠將這個座標值轉換成一個新的座標值,計算步驟例如如下:
結果爲:
x’=a*x+b*y+c
y’=d*x+e*y+f
同顏色矩陣同樣,假設座標變換矩陣例如如下,則新的座標值X、Y添加50,也就是說圖片的每一點都平移了(50,50)的距離,即圖片整體平移到了(50,50)座標處。
假設座標變換矩陣例如如下,則所有的X、Y座標都增大兩倍,也就是說圖片被放大了兩倍,其它縮放效果原理相似。
更復雜一點的還有旋轉效果,一個旋轉變換矩陣例如如下:
結果爲x’ = xcosθ – ysinθ 與 y’ = xsinθ + ycosθ,這個結果的效果是繞原點逆時針旋轉θ度角。
如下是用於Android的一段演示樣例代碼,用於將圖片平移。也就是裁剪的效果。其它效果可以參照相應座標變換矩陣改動就能夠:
public static Bitmap test1Bitmap(Bitmap bitmap){ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.RGB_565); Canvas canvas = new Canvas(output); Paint paint = new Paint(); Matrix cm = new Matrix(); float[] array = {1,0,50, 0,1,50, 0,0,1}; cm.setValues(array); canvas.drawBitmap(bitmap, cm, paint); return output; }
繞原點逆時針旋轉θ度角的變換公式是 x' = xcosθ − ysinθ 與 y' = xsinθ + ycosθ
2. 縮放
變換後長寬分別放大x'=scale*x;y'=scale*y.
3.切變
Android的圖像矩陣絕對不止這些,這是一個很是複雜的知識。涉及到大學相關數學的課程,能瞭解大學線性代數裏的矩陣知識,對學習Android下的圖像矩陣有很是好的幫助,在這裏限於篇幅。我僅僅作了簡單的基礎解說,基本可以理解,可以使用就能夠,假設想深刻學習一下的話,請查看下方的資料連接,去下載我今天上傳到CSDN資源庫裏面的資料。