位圖文件(Bitmap),擴展名能夠是.bmp或者.dib。位圖是Windows標準格式圖形文件,它將圖像定義爲由點(像素)組成,每一個點能夠由多種色彩表示,包括二、四、八、1六、24和32位色彩。位圖文件是非壓縮格式的,須要佔用較大存儲空間。java
例如,一幅1920X1080分辨率的32位圖片,其所佔存儲字節數爲:1920×1080×32/(8*1024)=8100KB=7.91Mandroid
在安卓系統中bitmap圖片通常是以ARGB_8888(ARGB分別表明的是透明度,紅色,綠色,藍色,每一個值分別用8bit來記錄,也就是一個像素會佔用4byte,共32bit。)來進行存儲的。canvas
顏色格式 | 每一個像素佔用內存(單位byte) | 每一個像素佔用內存(單位bit) |
---|---|---|
ALPHA_8 | 1 | 8 |
ARGB_8888(默認) | 4 | 32 |
ARGB_4444 | 2 | 16 |
RGB_565 | 2 | 16 |
ARGB_8888佔位計算: 8+8+8+8 =32緩存
1 bit 0/1 ;最小單位bash
1 byte = 8bit 10101010 (0-255)00000000 --- 11111111dom
說明:函數
ARGB_8888:ARGB分別表明的是透明度,紅色,綠色,藍色,每一個值分別用8bit來記錄,也就是一個像素會佔用4byte,共32bit.post
ARGB_4444:ARGB的是每一個值分別用4bit來記錄,一個像素會佔用2byte,共16bit.動畫
RGB_565:R=5bit,G=6bit,B=5bit,不存在透明度,每一個像素會佔用2byte,共16bit.ui
ALPHA_8:該像素只保存透明度,會佔用1byte,共8bit.
在實際應用中而言,建議使用ARGB_8888以及RGB_565。 若是你不須要透明度,選擇RGB_565,能夠減小一半的內存佔用.
在Android3.0之前Bitmap是存放在內存中的,咱們須要回收native層和Java層的內存
在Android3.0之後Bitmap是存放在堆中的,咱們只要回收堆內存便可
官方建議咱們3.0之後使用recycle()方法進行回收,該方法能夠不主動調用,由於垃圾回收器會自動收集不可用的Bitmap對象進行回收
釋放與此位圖關聯的本地對象,並清除對像素數據的引用。這不會同步釋放像素數據;它只是容許它被垃圾收集,若是沒有其餘的參考。位圖被標記爲「dead」,意味着若是getPixels()或setPixels()被調用,它將拋出異常,而且不會畫任何東西。這個操做是不能逆轉的,因此只有在你肯定沒有進一步的位圖使用時才能調用它。這是一個高級調用,一般不須要調用,由於當沒有更多的引用到這個位圖時,正常的GC進程將釋放這個內存。
LruCache是個泛型類,內部採用LinkedHashMap來實現緩存機制,它提供get方法和put方法來獲取緩存和添加緩存,其最重要的方法trimToSize是用來移除最少使用的緩存和使用最久的緩存,並添加最新的緩存到隊列中
經過指望的寬高來獲取壓縮比
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if (width > reqWidth || height > reqHeight) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
複製代碼
獲取指定的寬高的bitmap的縮略圖
public static Bitmap thumbnail(String path, int maxWidth, int maxHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(path,options);
int sampleSize = calculateInSampleSize(options,maxWidth,maxHeight);
options.inJustDecodeBounds = false;
options.inSampleSize =sampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inPurgeable =true;
options.inInputShareable = true;
if(bitmap !=null&&!bitmap.isRecycled()){
bitmap.recycle();
}
bitmap = BitmapFactory.decodeFile(path,options);
return bitmap;
}
複製代碼
將Bitmap保存到指定文件中,並設置圖片質量(下文會有介紹)
public static String save(Bitmap bitmap, Bitmap.CompressFormat format, int quality,File desFile) {
try{
FileOutputStream out = new FileOutputStream(desFile);
if(bitmap.compress(format,quality,out)){
out.flush();
out.close();
}
if(bitmap!=null&&!bitmap.isRecycled()){
bitmap.recycle();
}
return desFile.getAbsolutePath();
}catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
複製代碼
public static String save(Bitmap bitmap, Bitmap.CompressFormat format, int quality,Context context) {
if(!Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED)){
return null;
}
File dir = new File(Environment.getExternalStorageDirectory()+
"/"+context.getPackageName());
if(!dir.exists()){
dir.mkdir();
}
File desFile = new File(dir, UUID.randomUUID().toString());
return save(bitmap,format,quality,desFile);
}
複製代碼
用到的方法是Bitmap 的compress();
質量壓縮方法:在保持像素的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的:
一、bitmap圖片的大小不會改變
二、bytes.length是隨着quality變小而變小的。
這樣適合去傳遞二進制的圖片數據,好比分享圖片,要傳入二進制數據過去,限制500kb以內。
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把ByteArrayInputStream數據生成圖片
Bitmap bitmap = null;
image.compress(Bitmap.CompressFormat.JPEG, 5, baos);
byte[] bytes = baos.toByteArray();
// 把壓縮後的數據baos存放到bytes中
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null) {
loga(bitmap, baos.toByteArray());
}
return bitmap;
}
/**
* @param image 目標原圖
* @param maxSize 最大的圖片大小,
* @return
*/
public static Bitmap compressImage(Bitmap image,long maxSize) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把ByteArrayInputStream數據生成圖片
Bitmap bitmap = null;
// 質量壓縮方法,options的值是0-100,這裏100表示原來圖片的質量,不壓縮,把壓縮後的數據存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 90;
// 循環判斷若是壓縮後圖片是否大於maxSize,大於繼續壓縮
while (baos.toByteArray().length > maxSize) {
// 重置baos即清空baos
baos.reset();
// 這裏壓縮options%,把壓縮後的數據存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
// 每次都減小10,當爲1的時候中止,options<10的時候,遞減1
if(options == 1){
break;
}else if (options <= 10) {
options -= 1;
} else {
options -= 10;
}
}
byte[] bytes = baos.toByteArray();
// 把壓縮後的數據baos存放到bytes中
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null) {
loga(bitmap, baos.toByteArray());
}
return bitmap;
}
複製代碼
設置inSampleSize的值(int類型)後,假如設爲n,則寬和高都爲原來的1/n,寬高都減小,內存下降。上面的代碼沒用過options.inJustDecodeBounds = true; 由於我是固定來取樣的數據,爲何這個壓縮方法叫採樣率壓縮?
是由於配合inJustDecodeBounds,先獲取圖片的寬、高(這個過程就是取樣)。
而後經過獲取的寬高,動態的設置inSampleSize的值。 當inJustDecodeBounds設置爲true的時候, BitmapFactory經過decodeResource或者decodeFile解碼圖片時, 將會返回空(null)的Bitmap對象,這樣能夠避免Bitmap的內存分配, 可是它能夠返回Bitmap的寬度、高度以及MimeType。
int inSampleSize = getScaling(bitmap); bitmap = samplingRateCompression(path,inSampleSize);
private Bitmap samplingRateCompression(String path, int scaling) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = scaling;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
int size = (bitmap.getByteCount() / 1024 / 1024);
Log.i("wechat", "壓縮後圖片的大小" + (bitmap.getByteCount() / 1024 / 1024)
+ "M寬度爲" + bitmap.getWidth() + "高度爲" + bitmap.getHeight());
return bitmap;
}
/**
* 獲取縮放比例
* @param bitmap
* @return
*/
private int getScaling(Bitmap bitmap) {
//設置目標尺寸(以像素的寬度爲標準)
int Targetsize = 1500;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//選擇最大值做爲比較值(保證圖片的壓縮大小)
int handleValue = width > height ? width : height;
//循環計算壓縮比
int i = 1;
while (handleValue / i > Targetsize) {
i++;
}
}
複製代碼
Android中使用Matrix對圖像進行縮放、旋轉、平移、斜切等變換的。 Matrix是一個3*3的矩陣,其值對應以下:
|scaleX, skewX, translateX|
|skewY, scaleY, translateY|
|0 , 0 , scale |
Matrix提供了一些方法來控制圖片變換:
注意:以上的set方法,均有對應的post和pre方法, Matrix調用一系列set,pre,post方法時,可視爲將這些方法插入到一個隊列. 固然,按照隊列中從頭到尾的順序調用執行. 其中pre表示在隊頭插入一個方法,post表示在隊尾插入一個方法. 而set表示把當前隊列清空,而且老是位於隊列的最中間位置. 當執行了一次set後: pre方法老是插入到set前部的隊列的最前面,post方法老是插入到set後部的隊列的最後面
private Bitmap ScalingCompression(Bitmap bitmap) {
Matrix matrix = new Matrix();
matrix.setScale(0.25f, 0.25f);//縮放效果相似於方法2
Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024 / 1024)
+ "M寬度爲" + bm.getWidth() + "高度爲" + bm.getHeight());
return bm;
}
複製代碼
原圖大小:4M----轉化爲File---Bitmap大小
通常狀況下默認使用的是ARGB8888,由此可知它是最佔內存的,由於一個像素佔32位,8位=1字節,因此一個像素佔4字節的內存。假設有一張480x800的圖片,若是格式爲ARGB8888,那麼將會佔用1500KB的內存。
private Bitmap bitmapConfig(String path) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bm = BitmapFactory.decodeFile(path, options);
Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024f / 1024f)
+ "M寬度爲" + bm.getWidth() + "高度爲" + bm.getHeight());
return bm;
}
複製代碼
Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter);
在Android中,目前,我知道有兩種出現鋸齒的狀況。
咱們分別以這兩種狀況加以考慮。 用Canvas繪製位圖的的狀況。 在用Canvas繪製位圖時,通常地,咱們使用drawBitmap函數家族, 在這些函數中,都有一個Paint參數, 要作到防止鋸齒,咱們就要使用到這個參數。以下:
首先在你的構造函數中,須要建立一個Paint。 Paint mPaint = new Paint(); 而後,您須要設置兩個參數:
第一個函數是用來防止邊緣的鋸齒, (true時圖像邊緣相對清晰一點,鋸齒痕跡不那麼明顯, false時,寫上去的字不飽滿,不美觀,看地不太清楚)。
第二個函數是用來對位圖進行濾波處理。
最後,在畫圖的時候,調用drawBitmap函數,只須要將整個Paint傳入便可。
有時候,當你作RotateAnimation時, 你會發現,鋸齒又出現了。 這個時候,因爲你不能控制位圖的繪製, 只能用其餘方法來實現防止鋸齒。 另外,若是你畫的位圖不少。 不想每一個位圖的繪製都傳入一個Paint。 還有的時候,你不可能控制每一個窗口的繪製的時候, 您就須要用下面的方法來處理——對整個Canvas進行處理。
另外,在Drawable類及其子類中, 也有函數setFilterBitmap能夠用來對Bitmap進行濾波處理, 這樣,當你選擇Drawable時,會有抗鋸齒的效果。
private Bitmap createScaledBitmap(Bitmap bitmap) {
Bitmap bm = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
Log.i("wechat", "壓縮後圖片的大小" + (bm.getByteCount() / 1024) + "KB寬度爲"
+ bm.getWidth() + "高度爲" + bm.getHeight());
return bm;
}
複製代碼
private Bitmap getBitmapByPath(String path) {
if (!new File(path).exists()) {
System.err.println("getBitmapByPath: 文件不存在");
return null;
}
byte[] buf = new byte[1024 * 1024];// 1M
Bitmap bitmap = null;
try {
FileInputStream fis = new FileInputStream(path);
int len = fis.read(buf, 0, buf.length);
bitmap = BitmapFactory.decodeByteArray(buf, 0, len);
//當bitmap爲空的時候,說明解析失敗
if (bitmap == null) {
System.out.println("文件長度:" + len);
System.err.println("path: " + path + " 沒法解析!!!");
}
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
複製代碼
private void savaPictrue(Bitmap bitmap) {
File file = new File("storage/emulated/0/DCIM/Camera/test.jpg");
FileOutputStream stream = null;
try {
stream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
Log.e("圖片大小:", file.length() / 1024 / 1024 + "M");
} catch (Exception e) {
e.printStackTrace();
}
}
複製代碼
private void crop(Uri uri) {
// 裁剪圖片意圖
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
// 裁剪框的比例,1:1
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// 裁剪後輸出圖片的尺寸大小
intent.putExtra("outputX", 300);
intent.putExtra("outputY", 300);
intent.putExtra("outputFormat", "JPEG");// 圖片格式
intent.putExtra("noFaceDetection", true);// 取消人臉識別
intent.putExtra("return-data", true);
// 開啓一個帶有返回值的Activity,請求碼爲PHOTO_REQUEST_CUT
startActivityForResult(intent, 200);
}
private void logp(Bitmap bitmap) {
Log.i("wechat", "壓縮前圖片的大小" + (bitmap.getByteCount() / 1024 / 1024)
+ "M寬度爲" + bitmap.getWidth() + "高度爲" + bitmap.getHeight());
}
private static void loga(Bitmap bitmap, byte[] bytes) {
Log.i("wechat", "壓縮後圖片的大小" + (bitmap.getByteCount() / 1024 / 1024)
+ "M寬度爲" + bitmap.getWidth() + "高度爲" + bitmap.getHeight()
+ "bytes.length= " + (bytes.length / 1024) + "KB"
);
}
複製代碼