編輯:關於Android編程
我們知道android系統已經為我們提供好了一個展示圖片的“容器”——Gallery,但是這個Gallery顯示的效果是平面化的,動態效果不強。這裡,我們動手做一個自定義的Gallery組件,實現圖片的3D效果展示,想想應該不錯吧,先看看效果圖:

實現這個3D效果的Gallery該怎麼做呢?首先,分析一下,
1,展示圖片,系統自帶Gallery組件,可以基於這個Gallery組件擴展我們所需要的效果。
2,展示效果需要進行3D成像。
3,展示的圖片下方需要顯示圖片的倒影。
4,展示圖片的倒影需要加上“遮罩”效果。
好了,問題列好了,我們一個個來解決吧!代碼量不多,直接上代碼好了。
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> mImageCache //
= new Hashtable>();
/**
* 根據id返回一個處理後的圖片
*
* @param res
* @param resID
* @return
*/
public static Bitmap getImageBitmap(Resources res, int resID) {
// 先去集合中取當前resID是否已經拿過圖片,如果集合中有,說明已經拿過,直接使用集合中的圖片返回
SoftReference 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(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下加載圖片很容易就處理OOM,當然了,避免OOM的方式有很多,我在這是使用了內存緩存機制來避免了,即使用Java給我們提供好的“軟引用”來解決。接下來,就是怎麼引用這個畫廊組件了。
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_8888、ARGB_4444、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)。
從上面的幾個例子我們很容易就能明白顏色矩陣中的每個分量(每一列)的意義:
第一行決定紅色,
第二行決定綠色,
第三行決定藍色,
第四行決定了透明度,
第五列是顏色的偏移量。
至此我們應該能理解如何通過顏色矩陣來改變顏色值的各個分量了。
下面是用於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 坐標變換矩陣
結果為:
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;
}
下面將介紹幾種常用的變換矩陣:
1.旋轉

繞原點逆時針旋轉θ度角的變換公式是 x' = xcosθ − ysinθ 與 y' = xsinθ + ycosθ
2. 縮放

變換後長寬分別放大x'=scale*x;y'=scale*y.
3.切變

4.反射

5.正投影

Android的圖像矩陣絕對不止這些,這是一個很復雜的知識,涉及到大學相關數學的課程,能了解大學線性代數裡的矩陣知識,對學習Android下的圖像矩陣有很好的幫助,在這裡限於篇幅,我只做了簡單的基礎講解,基本可以理解,可以使用即可,如果想深入學習一下的話,請查看下方的資料鏈接,去下載我今天上傳到CSDN資源庫裡面的資料。
Android圖像矩陣基礎與詳解資料
源碼請在這裡下載
Android實現通訊錄效果——獲取手機號碼和姓名
首先給大家展示下運行效果圖:由於通訊錄在手機裡是以數據庫貯存的 所以我們可以通過一個方法context.getContentResolver().query(Phone.
新浪微博Oauth2.0授權認證及SDK、API的使用(Android)
一、微博開發者平台的使用新浪微博這裡主要是介紹使用新浪微博的開發者平台。想要做一個基於微博登陸或者其他一系列操作的,我們先要登陸他們的官網進行注冊使用,首先是要注冊成為一
Android面試准備 第一天 第2-4例
2、如果有個100M大的文件,需要上傳至服務器中,而服務器form表單最大只能上傳2M,可以用什麼方法。個人理解:所謂表單最大只能上傳2M,是不是指一個表單中附件只能上傳
Android循環滾動廣告條的完美實現,封裝方便,平滑過渡,從網絡加載圖片,點擊廣告進入對應網址
今天給大家帶來一點干貨,就是橫向循環滾動的廣告條。有點類似淘寶的banner廣告位,可以手勢滑動,也會依據固定時間間隔自動滾動,滑到盡頭時會一直循環。過渡非常