您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關如何在Android應用中對圖片進行壓縮,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
1、質量壓縮法
設置bitmap options屬性,降低圖片的質量,像素不會減少
第一個參數為需要壓縮的bitmap圖片對象,第二個參數為壓縮后圖片保存的位置
設置options 屬性0-100,來實現壓縮。
private Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這里100表示不壓縮,把壓縮后的數據存放到baos中 int options = 100; while ( baos.toByteArray().length / 1024>100) { //循環判斷如果壓縮后圖片是否大于100kb,大于繼續壓縮 baos.reset();//重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, options, baos);//這里壓縮options%,把壓縮后的數據存放到baos中 options -= 10;//每次都減少10 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮后的數據baos存放到ByteArrayInputStream中 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream數據生成圖片 return bitmap; }
質量壓縮不會減少圖片的像素。它是在保持像素不變的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的。進過它壓縮的圖片文件大小會有改變,但是導入成bitmap后占得內存是不變的。因為要保持像素不變,所以它就無法無限壓縮,到達一個值之后就不會繼續變小了。顯然這個方法并不適用于縮略圖,其實也不適用于想通過壓縮圖片減少內存的適用,僅僅適用于想在保證圖片質量的同時減少文件大小的情況而已。
2、采樣率壓縮法
private Bitmap getimage(String srcPath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); //開始讀入圖片,此時把options.inJustDecodeBounds 設回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此時返回bm為空 newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; //現在主流手機比較多是1280*720分辨率,所以高和寬我們設置為 float hh = 1280f;//這里設置高度為1280f float ww = 720f;//這里設置寬度為720f //縮放比。由于是固定比例縮放,只用高或者寬其中一個數據進行計算即可 int be = 1;//be=1表示不縮放 if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//設置縮放比例 //重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了 bitmap = BitmapFactory.decodeFile(srcPath, newOpts); return compressImage(bitmap);//壓縮好比例大小后再進行質量壓縮 }
這個方法的好處是大大的縮小了內存的使用,在讀存儲器上的圖片時,如果不需要高清的效果,可以先只讀取圖片的邊,通過寬和高設定好取樣率后再加載圖片,這樣就不會過多的占用內存。
3、縮放法
通過縮放圖片像素來減少圖片占用內存大小。
方式一
public static void compressBitmapToFile(Bitmap bmp, File file){ // 尺寸壓縮倍數,值越大,圖片尺寸越小 int ratio = 2; // 壓縮Bitmap到對應尺寸 Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Config.ARGB_8888); Canvas canvas = new Canvas(result); Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio); canvas.drawBitmap(bmp, null, rect, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 把壓縮后的數據存放到baos中 result.compress(Bitmap.CompressFormat.JPEG, 100 ,baos); try { FileOutputStream fos = new FileOutputStream(file); fos.write(baos.toByteArray()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } }
方式二
ByteArrayOutputStream out = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 85, out); float zoom = (float)Math.sqrt(size * 1024 / (float)out.toByteArray().length); Matrix matrix = new Matrix(); matrix.setScale(zoom, zoom); Bitmap result = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true); out.reset(); result.compress(Bitmap.CompressFormat.JPEG, 85, out); while(out.toByteArray().length > size * 1024){ System.out.println(out.toByteArray().length); matrix.setScale(0.9f, 0.9f); result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true); out.reset(); result.compress(Bitmap.CompressFormat.JPEG, 85, out); }
縮放法其實很簡單,設定好matrix,在createBitmap就可以了。但是我們并不知道縮放比例,而是要求了圖片的最終大小。直接用大小的比例來做的話肯定是有問題的,用大小比例的開方來做會比較接近,但是還是有差距。但是只要再做一下微調應該就可以了,微調的話就是修改過的圖片大小比最終大小還大的話,就進行0.8的壓縮再比較,循環直到大小合適。這樣就能得到合適大小的圖片,而且也能比較保證質量。
4、JNI調用libjpeg庫壓縮
JNI靜態調用 bitherlibjni.c 中的方法來實現壓縮Java_net_bither_util_NativeUtil_compressBitmap
net_bither_util為包名,NativeUtil為類名,compressBitmap為native方法名,我們只需要調用saveBitmap()方法就可以,bmp 需要壓縮的Bitmap對象, quality壓縮質量0-100, fileName 壓縮后要保存的文件地址, optimize 是否采用哈弗曼表數據計算 品質相差5-10倍。
jstring Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv* env, jobject thiz, jobject bitmapcolor, int w, int h, int quality, jbyteArray fileNameStr, jboolean optimize) { AndroidBitmapInfo infocolor; BYTE* pixelscolor; int ret; BYTE * data; BYTE *tmpdata; char * fileName = jstrinTostring(env, fileNameStr); if ((ret = AndroidBitmap_getInfo(env, bitmapcolor, &infocolor)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return (*env)->NewStringUTF(env, "0");; } if ((ret = AndroidBitmap_lockPixels(env, bitmapcolor, &pixelscolor)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); } BYTE r, g, b; data = NULL; data = malloc(w * h * 3); tmpdata = data; int j = 0, i = 0; int color; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { color = *((int *) pixelscolor); r = ((color & 0x00FF0000) >> 16); g = ((color & 0x0000FF00) >> 8); b = color & 0x000000FF; *data = b; *(data + 1) = g; *(data + 2) = r; data = data + 3; pixelscolor += 4; } } AndroidBitmap_unlockPixels(env, bitmapcolor); int resultCode= generateJPEG(tmpdata, w, h, quality, fileName, optimize); free(tmpdata); if(resultCode==0){ jstring result=(*env)->NewStringUTF(env, error); error=NULL; return result; } return (*env)->NewStringUTF(env, "1"); //success }
5、質量壓縮+采樣率壓縮+JNI調用libjpeg庫壓縮結合使用
首先通過尺寸壓縮,壓縮到手機常用的一個分辨率(1280*960 微信好像是壓縮到這個分辨率),然后我們要把圖片壓縮到一定大小以內(比如說200k),然后通過循環進行質量壓縮來計算options需要設置為多少,最后調用JNI壓縮。
計算縮放比
/** * 計算縮放比 * @param bitWidth 當前圖片寬度 * @param bitHeight 當前圖片高度 * @return int 縮放比 */ public static int getRatioSize(int bitWidth, int bitHeight) { // 圖片最大分辨率 int imageHeight = 1280; int imageWidth = 960; // 縮放比 int ratio = 1; // 縮放比,由于是固定比例縮放,只用高或者寬其中一個數據進行計算即可 if (bitWidth > bitHeight && bitWidth > imageWidth) { // 如果圖片寬度比高度大,以寬度為基準 ratio = bitWidth / imageWidth; } else if (bitWidth < bitHeight && bitHeight > imageHeight) { // 如果圖片高度比寬度大,以高度為基準 ratio = bitHeight / imageHeight; } // 最小比率為1 if (ratio <= 0) ratio = 1; return ratio; }
質量壓縮+JNI壓縮
/** * @Description: 通過JNI圖片壓縮把Bitmap保存到指定目錄 * @param curFilePath * 當前圖片文件地址 * @param targetFilePath * 要保存的圖片文件地址 */ public static void compressBitmap(String curFilePath, String targetFilePath) { // 最大圖片大小 500KB int maxSize = 500; //根據地址獲取bitmap Bitmap result = getBitmapFromFile(curFilePath); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 質量壓縮方法,這里100表示不壓縮,把壓縮后的數據存放到baos中 int quality = 100; result.compress(Bitmap.CompressFormat.JPEG, quality, baos); // 循環判斷如果壓縮后圖片是否大于500kb,大于繼續壓縮 while (baos.toByteArray().length / 1024 > maxSize) { // 重置baos即清空baos baos.reset(); // 每次都減少10 quality -= 10; // 這里壓縮quality,把壓縮后的數據存放到baos中 result.compress(Bitmap.CompressFormat.JPEG, quality, baos); } // JNI保存圖片到SD卡 這個關鍵 NativeUtil.saveBitmap(result, quality, targetFilePath, true); // 釋放Bitmap if (!result.isRecycled()) { result.recycle(); } }
JNI圖片壓縮工具類
package net.bither.util; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; import android.media.ExifInterface; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; /** * JNI圖片壓縮工具類 * * @Description TODO * @Package net.bither.util * @Class NativeUtil */ public class NativeUtil { private static int DEFAULT_QUALITY = 95; /** * @Description: JNI基本壓縮 * @param bit * bitmap對象 * @param fileName * 指定保存目錄名 * @param optimize * 是否采用哈弗曼表數據計算 品質相差5-10倍 */ public static void compressBitmap(Bitmap bit, String fileName, boolean optimize) { saveBitmap(bit, DEFAULT_QUALITY, fileName, optimize); } /** * @Description: 通過JNI圖片壓縮把Bitmap保存到指定目錄 * @param image * bitmap對象 * @param filePath * 要保存的指定目錄 */ public static void compressBitmap(Bitmap image, String filePath) { // 最大圖片大小 150KB int maxSize = 150; // 獲取尺寸壓縮倍數 int ratio = NativeUtil.getRatioSize(image.getWidth(),image.getHeight()); // 壓縮Bitmap到對應尺寸 Bitmap result = Bitmap.createBitmap(image.getWidth() / ratio,image.getHeight() / ratio, Config.ARGB_8888); Canvas canvas = new Canvas(result); Rect rect = new Rect(0, 0, image.getWidth() / ratio, image.getHeight() / ratio); canvas.drawBitmap(image,null,rect,null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 質量壓縮方法,這里100表示不壓縮,把壓縮后的數據存放到baos中 int options = 100; result.compress(Bitmap.CompressFormat.JPEG, options, baos); // 循環判斷如果壓縮后圖片是否大于100kb,大于繼續壓縮 while (baos.toByteArray().length / 1024 > maxSize) { // 重置baos即清空baos baos.reset(); // 每次都減少10 options -= 10; // 這里壓縮options%,把壓縮后的數據存放到baos中 result.compress(Bitmap.CompressFormat.JPEG, options, baos); } // JNI保存圖片到SD卡 這個關鍵 NativeUtil.saveBitmap(result, options, filePath, true); // 釋放Bitmap if (!result.isRecycled()) { result.recycle(); } } /** * @Description: 通過JNI圖片壓縮把Bitmap保存到指定目錄 * @param curFilePath * 當前圖片文件地址 * @param targetFilePath * 要保存的圖片文件地址 */ public static void compressBitmap(String curFilePath, String targetFilePath) { // 最大圖片大小 500KB int maxSize = 500; //根據地址獲取bitmap Bitmap result = getBitmapFromFile(curFilePath); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 質量壓縮方法,這里100表示不壓縮,把壓縮后的數據存放到baos中 int quality = 100; result.compress(Bitmap.CompressFormat.JPEG, quality, baos); // 循環判斷如果壓縮后圖片是否大于500kb,大于繼續壓縮 while (baos.toByteArray().length / 1024 > maxSize) { // 重置baos即清空baos baos.reset(); // 每次都減少10 quality -= 10; // 這里壓縮quality,把壓縮后的數據存放到baos中 result.compress(Bitmap.CompressFormat.JPEG, quality, baos); } // JNI保存圖片到SD卡 這個關鍵 NativeUtil.saveBitmap(result, quality, targetFilePath, true); // 釋放Bitmap if (!result.isRecycled()) { result.recycle(); } } /** * 計算縮放比 * @param bitWidth 當前圖片寬度 * @param bitHeight 當前圖片高度 * @return int 縮放比 */ public static int getRatioSize(int bitWidth, int bitHeight) { // 圖片最大分辨率 int imageHeight = 1280; int imageWidth = 960; // 縮放比 int ratio = 1; // 縮放比,由于是固定比例縮放,只用高或者寬其中一個數據進行計算即可 if (bitWidth > bitHeight && bitWidth > imageWidth) { // 如果圖片寬度比高度大,以寬度為基準 ratio = bitWidth / imageWidth; } else if (bitWidth < bitHeight && bitHeight > imageHeight) { // 如果圖片高度比寬度大,以高度為基準 ratio = bitHeight / imageHeight; } // 最小比率為1 if (ratio <= 0) ratio = 1; return ratio; } /** * 通過文件路徑讀獲取Bitmap防止OOM以及解決圖片旋轉問題 * @param filePath * @return */ public static Bitmap getBitmapFromFile(String filePath){ BitmapFactory.Options newOpts = new BitmapFactory.Options(); newOpts.inJustDecodeBounds = true;//只讀邊,不讀內容 BitmapFactory.decodeFile(filePath, newOpts); int w = newOpts.outWidth; int h = newOpts.outHeight; // 獲取尺寸壓縮倍數 newOpts.inSampleSize = NativeUtil.getRatioSize(w,h); newOpts.inJustDecodeBounds = false;//讀取所有內容 newOpts.inDither = false; newOpts.inPurgeable=true; newOpts.inInputShareable=true; newOpts.inTempStorage = new byte[32 * 1024]; Bitmap bitmap = null; File file = new File(filePath); FileInputStream fs = null; try { fs = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { if(fs!=null){ bitmap = BitmapFactory.decodeFileDescriptor(fs.getFD(),null,newOpts); //旋轉圖片 int photoDegree = readPictureDegree(filePath); if(photoDegree != 0){ Matrix matrix = new Matrix(); matrix.postRotate(photoDegree); // 創建新的圖片 bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } } } catch (IOException e) { e.printStackTrace(); } finally{ if(fs!=null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } return bitmap; } /** * * 讀取圖片屬性:旋轉的角度 * @param path 圖片絕對路徑 * @return degree旋轉的角度 */ public static int readPictureDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } /** * 調用native方法 * @Description:函數描述 * @param bit * @param quality * @param fileName * @param optimize */ private static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) { compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize); } /** * 調用底層 bitherlibjni.c中的方法 * @Description:函數描述 * @param bit * @param w * @param h * @param quality * @param fileNameBytes * @param optimize * @return */ private static native String compressBitmap(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes, boolean optimize); /** * 加載lib下兩個so文件 */ static { System.loadLibrary("jpegbither"); System.loadLibrary("bitherjni"); } }
圖片壓縮處理中可能遇到的問題:
請求系統相冊有三個Action
注意:圖庫(縮略圖) 和 圖片(原圖)
ACTION_OPEN_DOCUMENT 僅限4.4或以上使用 默認打開原圖
從圖片獲取到的uri 格式為:content://com.android.providers.media.documents/document/image%666>>>
ACTION_GET_CONTENT 4.4以下默認打開縮略圖 。 以上打開文件管理器 供選擇,選擇圖庫打開為縮略圖頁面,選擇圖片打開為原圖瀏覽。
從圖庫獲取到的uri格式為:content://media/external/images/media/666666
ACTION_PICK 都可用,打開默認是縮略圖界面,還需要進一步點開查看。
參考代碼:
public void pickFromGallery() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT).setType("image/*"), REQUEST_PICK_IMAGE); } else { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/*"); startActivityForResult(intent, REQUEST_KITKAT_PICK_IMAGE); } }
根據URI獲取對應的文件路徑
在我們從圖庫中選擇圖片后回調給我們的data.getData()可能是URI,我們平時對文件的操作基本上都是基于路徑然后進行各種操作與轉換,如今我們需要將URI對應的文件路徑找出來,具體參考代碼如下:
public static String getPathByUri(Context context, Uri data){ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return getPathByUri4BeforeKitkat(context, data); }else { return getPathByUri4AfterKitkat(context, data); } } //4.4以前通過Uri獲取路徑:data是Uri,filename是一個String的字符串,用來保存路徑 public static String getPathByUri4BeforeKitkat(Context context, Uri data) { String filename=null; if (data.getScheme().toString().compareTo("content") == 0) { Cursor cursor = context.getContentResolver().query(data, new String[] { "_data" }, null, null, null); if (cursor.moveToFirst()) { filename = cursor.getString(0); } } else if (data.getScheme().toString().compareTo("file") == 0) {// file:///開頭的uri filename = data.toString().replace("file://", "");// 替換file:// if (!filename.startsWith("/mnt")) {// 加上"/mnt"頭 filename += "/mnt"; } } return filename; } //4.4以后根據Uri獲取路徑: @SuppressLint("NewApi") public static String getPathByUri4AfterKitkat(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { if (isExternalStorageDocument(uri)) {// ExternalStorageProvider final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } else if (isDownloadsDocument(uri)) {// DownloadsProvider final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } else if (isMediaDocument(uri)) {// MediaProvider final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[] { split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore // (and // general) return getDataColumn(context, uri, null, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) {// File return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context * The context. * @param uri * The Uri to query. * @param selection * (Optional) Filter used in the query. * @param selectionArgs * (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri * The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri * The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri * The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); }
以上就是如何在Android應用中對圖片進行壓縮,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。