編輯:關於android開發
目錄 概述 前言 拍照 創建存儲拍照圖片的文件 調用系統拍照程序 讀取相冊圖片 獲取返回URI中的圖片路徑 統一的回調處理 圖片縮略圖加載 加載流程 計算縮略圖比例 縮略圖加載 源碼 圖片加載結果 示例GIF
此類是用於簡便調用系統拍照及打開相冊選擇圖片.通用於多種機型.(親測魅族MX4,三星note 2,三星note 3)
在執行拍照和打開相冊之前,我們需要注意一下.
由於Android開放的系統,很多手機廠商是定制自己的系統的,那麼就存在一個問題.盡管大部分時候拍照或者打開相冊都有一個基本的標准,但可能不同的系統操作方式或者是細節表現是不同的.
可能存在以下的情況:
拍照時相機會自動旋轉一定的角度(出現在三星機型上) 相冊圖片返回路徑存在URI中(原生系統)
以上只是遇到過的情況,包括魅族也存在一些問題.不管是拍照還是相冊獲取圖片,我們都希望能正確獲取到圖片.因此,這裡采用的方式是:
拍照時將圖片保存到指定位置,加載圖片時通過圖片路徑加載 獲取相冊圖片時通過返回URI圖片路徑加載圖片
拍照調用的是系統的拍照程序而不是自己實現的拍照程序.通用性更廣泛,也不需要自己去處理各種情況從而實現拍照程序,節省了大量的時間.
但是系統拍照程序可能會存在的問題是,系統的拍照程序是我們無法監視和控制的.有可能某些系統的拍照程序會造成一些問題.比如部分機型會自動旋轉相片角度.
所以我們在拍照後獲取圖片時,可以檢測圖片是否存在旋轉角度從而修正圖片的方向.
存儲拍照圖片的路徑從外部提供,原因後面會解釋到.
檢測路徑下的文件是否存在,不存在則創建一個新的空文件.文件不存在的情況下無法將拍照所得的圖片寫入到文件中.
該路徑應該為完整路徑,帶圖片格式後綴.
//嘗試創建文件夾及用於存儲拍照後的文件
File outputImage = new File(filePath);
if (!outputImage.exists()) {
try {
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
調用系統拍照程序的Action
是通用的.我們需要注意的是調用時需要設置拍照參數為圖片輸出到指定路徑
//將File對象轉換為Uri並啟動照相程序
Uri photoUri = Uri.fromFile(outputImage);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); //照相
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); //指定圖片輸出地址
try {
act.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); //啟動照相
} catch (SecurityException se) {
se.printStackTrace();
return null;
}
其中啟動拍照程序使用的requestCode
默認使用自定義值REQUEST_CODE_TAKE_PHOTO
.因為後面會提供統一的處理方法.
讀取相冊圖片使用的也是系統的相冊工具.
//設置選擇Action
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
//存在多個相冊類型的應用時,顯示給用戶選擇的打開相冊的應用界面
act.startActivityForResult(Intet.createChooser(intent,"請選擇"),REQUEST_CODE_OPEN_GRALLY);
相冊打開圖片後返回的往往是一個URI,存放的是圖片的路徑.但有時又不一定是絕對路徑,即我們無法通過這個URI去讀取到圖片.可能返回的是一個如下的URI:
content://media/external/images/media/45928
這並不是一個文件的絕對路徑,我們也無法從該路徑直接獲取到圖片,但是此URI確實指向了某個資源文件.
因此我們需要將該URI轉化成我們能獲取到的圖片的路徑.
public static String getBmpPathFromContent(Context context, Intent bmpIntent) {
if (bmpIntent != null && bmpIntent.getData() != null) {
Uri contentUri = bmpIntent.getData();
String uriPath = contentUri.getPath();
//content存在時
if (!TextUtils.isEmpty(uriPath)) {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
//加載系統數據庫
Cursor cursor = context.getContentResolver().query(contentUri,
filePathColumn, null, null, null);
//移動到第一行,不移動將越界
cursor.moveToFirst();
//加載查詢數據的列號
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
//獲取圖片路徑
String picturePath = cursor.getString(columnIndex);
//游標應該關閉
cursor.close();
return picturePath;
} else {
return null;
}
} else {
return null;
}
}
通過ContentResolver
查詢該URI對應的圖片的信息,從而我們得到可以直接讀取加載的圖片路徑.
以上不管是拍照還是打開相冊,都是通過startActivityForResult()
的方式打開並獲取數據的.
因此可以使用統一的方式進行處理拍照或者是相冊獲取圖片的回調.
/**
* 處理拍照/打開相冊後進入裁剪工作
*
* @param requestCode 連接
* @param resultCode
* @param data Intent數據
* @param act 啟動裁剪Activity的act
* @param inputPath 裁剪圖片的路徑(針對拍照保存的路徑,相冊獲取的路徑在data中)
* @param outputPath 圖片裁剪後輸出的路徑(提供給裁剪Activity)
* @return
*/
public static boolean onActivityResult(int requestCode, int resultCode, Intent data, Activity act, String inputPath, String outputPath) {
switch (requestCode) {
case REQUEST_CODE_TAKE_PHOTO:
case REQUEST_CODE_OPEN_GRALLY:
//當啟動使用的activity不存在或者是拍照/打開相冊失敗時(resultCode不為RESULT_OK),不處理
if (act == null || resultCode != Activity.RESULT_OK) {
return false;
}
//若當前為拍照回調,嘗試讀取圖片的旋轉角度
int degree = requestCode == REQUEST_CODE_TAKE_PHOTO ? readPictureDegree(inputPath) : 0;
//獲取intent中返回的圖片路徑(如果是打開相冊部分機型可能將圖片路徑存放在intent中返回)
String tempPath = getBmpPathFromContent(act, data);
//若intent路徑存在,使用該路徑,否則使用inputPath
inputPath = TextUtils.isEmpty(tempPath) ? inputPath : tempPath;
//源圖片路徑或者輸出路徑為無效時,不處理
if (TextUtils.isEmpty(inputPath) || TextUtils.isEmpty(outputPath)) {
return false;
} else {
Intent cropIntent = new Intent(act, CropBitmapActivity.class);
cropIntent.putExtra("inputPath", inputPath);
cropIntent.putExtra("outputPath", outputPath);
cropIntent.putExtra("degree", degree);
//啟動裁剪工具
act.startActivityForResult(cropIntent, REQUEST_CODE_CROP_PHOTO);
return true;
}
default:
return false;
}
}
以上需要注意的是,同一時間onActivityResult()
僅會回調一次,也就是說任何時候只會處理拍照或者是相冊圖片中的一種情況.其中
//若當前為拍照回調,嘗試讀取圖片的旋轉角度
int degree = requestCode == REQUEST_CODE_TAKE_PHOTO ? readPictureDegree(inputPath) : 0;
為處理拍照後的圖片的角度旋轉問題.
//獲取intent中返回的圖片路徑(如果是打開相冊部分機型可能將圖片路徑存放在intent中返回)
String tempPath = getBmpPathFromContent(act, data);
處理相冊圖片路徑的問題.
inputPath
僅為拍照圖片存儲的路徑,而與相冊圖片路徑無關.所以最後只會保留一個.
//若intent路徑存在,使用該路徑,否則使用inputPath
inputPath = TextUtils.isEmpty(tempPath) ? inputPath : tempPath;
當存在相冊圖片路徑時,說明當前不是拍照回調,使用相冊圖片路徑;當不存在相冊圖片路徑時,使用拍照存儲的圖片路徑,即處理為拍照回調.
最後調用自定義的裁剪CropBitmapActivity
進行裁剪圖片.關於裁剪詳細可見另外的文章.
圖片旋轉角度的檢測僅針對拍照後的相片.如果需要對相冊圖片及相片都進行檢測則可以對最終的inputPath
路徑檢測.
int degree = readPictureDegree(inputPath);
在裁剪圖片時可能會需要用到加載圖片的縮略圖.這裡因為很多情況下裁剪的圖片並不需要很高的清晰度(往往作為頭像等小圖顯示).
此時使用加載一張高清圖占用的內存是可觀的,在內存緊張時甚至可能導致程序因為內存問題而崩潰.
圖片縮略圖加載的原理是:
通過按比例加載出更小比例的圖片從而減少圖片占用的內存.
實際上BitmapFactory
系統圖片加載工具本身就可以指定比例加載圖片.但是只能加載縮略圖而無法加載大於圖片本身比例的圖片.
加載縮略圖最重要的一個部分是,計算出需要加載的縮略圖與原圖的比例.在BitmapFactory.Options
中有一個變量是用於記錄加載圖片與原圖的比例的.
options.inSampleSize
//int類型
該變量只能是大於1的數,1表示按原圖比例加載,大於1表示長寬縮小為1/inSampleSize
進行加載.
除此之外,該值必須為2的N次方.即使給定的值不是2的N次方的時候,
BitmapFactory
加載時也會取該值最接近的2的N次方(非常有可能是向下取值)
/**
* 計算圖片的縮放比例;
* 此值是指圖片的寬高的縮放比例,圖片面積的縮放比例為返回值的2次方;
* 返回值必定大於0且為2的N次方(這是由於系統決定了返回值必須是2的N次方)
*
* @param options 加載圖片所得的配置信息
* @param reqSize 預期希望的的圖片最長邊大小,最長邊由圖片本身決定,可能是寬也可能是高,加載後的圖片最長邊不會超過此參數值
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options, float reqSize) {
if (reqSize <= 0) {
throw new RuntimeException("預期邊長不可小於0");
}
float bmpWidth = options.outWidth;
float bmpHeight = options.outHeight;
float largeSizeInBmp = 0;
int sampleSize = 1;
//記錄最大的邊
if (bmpWidth > bmpHeight) {
largeSizeInBmp = bmpWidth;
} else {
largeSizeInBmp = bmpHeight;
}
//將最大邊與預期的邊大小進行比較計算縮放比
if (largeSizeInBmp < reqSize) {
//最大邊小於預期,則sampleSize為1
sampleSize = 1;
} else {
//最大邊大於預期邊
sampleSize = (int) (largeSizeInBmp / reqSize + 0.5);
//計算所得縮放值為2的幾倍指數,即求 log2(sampleSize)
double powerNum = Math.log(sampleSize) / Math.log(2);
int tempPowerNum = (int) powerNum;
//將所得指數+1,確保盡可能小於指定值
if (powerNum > tempPowerNum) {
tempPowerNum += 1;
}
//反求sampleSize=2^tempPowerNum
sampleSize = (int) Math.pow(2, tempPowerNum);
}
return sampleSize;
}
縮略圖的加載的有多種方式,可以加載圖片數據流,圖片資源,圖片路徑.
但本質都是通過加載圖片數據流加載的.圖片資源與圖片路徑都是加載成流文件再加載的.
所以這裡提供的是針對流的加載.
/**
* 獲取圖片加載配置
*
* @param in 圖片流,此方法運行後該流會被關閉
* @param reqSize 預期圖片最長邊的最大值
* @return
*/
public static BitmapFactory.Options getStreamBitmapOptions(InputStream in, float reqSize) {
try {
if (in == null || reqSize <= 0) {
return null;
}
//僅加載圖片信息(不加載圖片數據),獲取配置文件
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
//計算圖片縮放比例
int sampleSize = calculateInSampleSize(options, reqSize);
//正常加載圖片
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
return options;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從圖片流數據中加載圖片
*
* @param in 圖片流數據,此參數值建議從文件重新加載出來(不要使用過的流)
* @param options 加載配置,此參數應該來自於{@link #getStreamBitmapOptions(InputStream, float)}
* @return
*/
public static Bitmap decodeBitmapInScale(InputStream in, BitmapFactory.Options options) {
try {
if (in == null || options == null || in.available() <= 0) {
return null;
}
Log.i("bmp", "sampleSize = " + options.inSampleSize + "\nsrcWidth = " + options.outWidth + "\nsrcHeight = " + options.outHeight);
return BitmapFactory.decodeStream(in, null, options);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以上圖片加載時是分為兩部分的.一部分是加載出圖片的信息並計算縮略圖比例後返回縮略圖加載的配置對象BitmapFactory.Options
另一部分是根據新的縮略圖配置加載出縮略圖對象.
這裡使用的是inputStream
流加載,分為兩次的原因是:
當使用該流
in
加載出配置信息後,再重復使用該流加載圖片時,很可能會加載不出圖片
以上問題為親測確定的.當使用BitmapFactory.decodeFile()
與BitmapFactory.decodeResource()
都是可以正常加載出圖片.
但使用BitmapFactory.decodeStream()
並使用同一個流(之前加載配置信息的流)加載時,就會出現無法成功加載出圖片的問題.
查詢一下BitmapFactory.decodeFile()
與BitmapFactory.decodeResource()
方法的源代碼可以確定,實際上此兩個方法也都是將文件及資源文件加載成流文件再加載圖片的.
因此,在加載配置信息及加載圖片時,需要使用同一個文件的兩個全新的流對象,而不能復用同一個流對象.
類似地提供了兩個加載路徑及資源的方法:
/**
* 加載縮略圖
*
* @param filePath 文件路徑
* @param reqSize 預期圖片最長邊的最大值,加載所有圖片的最大值不會超過此值
* @return
*/
public static Bitmap decodeBitmapInScale(String filePath, int reqSize) {
InputStream in = null;
in = getBmpInputStream(filePath);
BitmapFactory.Options options = getStreamBitmapOptions(in, reqSize);
in = getBmpInputStream(filePath);
return decodeBitmapInScale(in, options);
}
/**
* 加載圖片(適用於資源文件)
*
* @param res
* @param resID 資源ID
* @param reqSize 預期圖片最長邊的最大值,加載所有圖片的最大值不會超過此值
* @return
*/
public static Bitmap decodeBitmapInScale(Resources res, int resID, int reqSize) {
InputStream in = null;
in = getBmpInputStream(res, resID);
BitmapFactory.Options options = getStreamBitmapOptions(in, reqSize);
in = getBmpInputStream(res, resID);
return decodeBitmapInScale(in, options);
}
本質是加載兩次流文件並使用流加載出圖片.(與BitmapFactory
的加載本質是相同的)
源碼包括了圖片的旋轉,圖片的縮略加載,拍照/相冊調用,打開自定義裁剪CropBitmapActivity
/**
* Created by taro on 16/4/11.
*/
public class GrallyAndPhotoUtils {
/**
* 拍照請求
*/
public static final int REQUEST_CODE_TAKE_PHOTO = 0x1;
/**
* 打開相冊請求
*/
public static final int REQUEST_CODE_OPEN_GRALLY = 0x2;
/**
* 裁剪圖片請求
*/
public static final int REQUEST_CODE_CROP_PHOTO = 0x3;
/**
* 加載縮略圖
*
* @param filePath 文件路徑
* @param reqSize 預期圖片最長邊的最大值,加載所有圖片的最大值不會超過此值
* @return
*/
public static Bitmap decodeBitmapInScale(String filePath, int reqSize) {
InputStream in = null;
in = getBmpInputStream(filePath);
BitmapFactory.Options options = getStreamBitmapOptions(in, reqSize);
in = getBmpInputStream(filePath);
return decodeBitmapInScale(in, options);
}
/**
* 加載圖片(適用於資源文件)
*
* @param res
* @param resID 資源ID
* @param reqSize 預期圖片最長邊的最大值,加載所有圖片的最大值不會超過此值
* @return
*/
public static Bitmap decodeBitmapInScale(Resources res, int resID, int reqSize) {
InputStream in = null;
in = getBmpInputStream(res, resID);
BitmapFactory.Options options = getStreamBitmapOptions(in, reqSize);
in = getBmpInputStream(res, resID);
return decodeBitmapInScale(in, options);
}
/**
* 獲取圖片加載配置
*
* @param in 圖片流,此方法運行後該流會被關閉
* @param reqSize 預期圖片最長邊的最大值
* @return
*/
public static BitmapFactory.Options getStreamBitmapOptions(InputStream in, float reqSize) {
try {
if (in == null || reqSize <= 0) {
return null;
}
//僅加載圖片信息(不加載圖片數據),獲取配置文件
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
//計算圖片縮放比例
int sampleSize = calculateInSampleSize(options, reqSize);
//正常加載圖片
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
return options;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從圖片流數據中加載圖片
*
* @param in 圖片流數據,此參數值建議從文件重新加載出來(不要使用過的流)
* @param options 加載配置,此參數應該來自於{@link #getStreamBitmapOptions(InputStream, float)}
* @return
*/
public static Bitmap decodeBitmapInScale(InputStream in, BitmapFactory.Options options) {
try {
if (in == null || options == null || in.available() <= 0) {
return null;
}
Log.i("bmp", "sampleSize = " + options.inSampleSize + "\nsrcWidth = " + options.outWidth + "\nsrcHeight = " + options.outHeight);
return BitmapFactory.decodeStream(in, null, options);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 計算圖片的縮放比例;
* 此值是指圖片的寬高的縮放比例,圖片面積的縮放比例為返回值的2次方;
* 返回值必定大於0且為2的N次方(這是由於系統決定了返回值必須是2的N次方)
*
* @param options 加載圖片所得的配置信息
* @param reqSize 預期希望的的圖片最長邊大小,最長邊由圖片本身決定,可能是寬也可能是高,加載後的圖片最長邊不會超過此參數值
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options, float reqSize) {
if (reqSize <= 0) {
throw new RuntimeException("預期邊長不可小於0");
}
float bmpWidth = options.outWidth;
float bmpHeight = options.outHeight;
float largeSizeInBmp = 0;
int sampleSize = 1;
//記錄最大的邊
if (bmpWidth > bmpHeight) {
largeSizeInBmp = bmpWidth;
} else {
largeSizeInBmp = bmpHeight;
}
//將最大邊與預期的邊大小進行比較計算縮放比
if (largeSizeInBmp < reqSize) {
//最大邊小於預期,則sampleSize為1
sampleSize = 1;
} else {
//最大邊大於預期邊
sampleSize = (int) (largeSizeInBmp / reqSize + 0.5);
//計算所得縮放值為2的幾倍指數,即求 log2(sampleSize)
double powerNum = Math.log(sampleSize) / Math.log(2);
int tempPowerNum = (int) powerNum;
//將所得指數+1,確保盡可能小於指定值
if (powerNum > tempPowerNum) {
tempPowerNum += 1;
}
//反求sampleSize=2^tempPowerNum
sampleSize = (int) Math.pow(2, tempPowerNum);
}
return sampleSize;
}
/**
* 從圖片路徑獲取圖片輸入流
*
* @param filePath 圖片路徑
* @return 獲取失敗返回null
*/
public static InputStream getBmpInputStream(String filePath) {
try {
return new FileInputStream(new File(filePath));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 從資源文件獲取圖片輸入流
*
* @param res
* @param resID 資源ID
* @return 獲取失敗null
*/
public static InputStream getBmpInputStream(Resources res, int resID) {
if (res == null || resID == 0) {
return null;
}
return res.openRawResource(resID);
}
/**
* 讀取圖片屬性:旋轉的角度
*
* @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 (Exception e) {
e.printStackTrace();
}
return degree;
}
/**
* 旋轉圖片
*
* @param angle
* @param bitmap
* @return Bitmap
*/
public static Bitmap rotatingBitmap(int angle, Bitmap bitmap) {
if (bitmap == null || bitmap.isRecycled()) {
return null;
}
//旋轉圖片 動作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
System.out.println("angle2=" + angle);
// 創建新的圖片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
/**
* 將圖片保存到文件中
*
* @param outputPath 圖片輸出地址,完整的地址包括圖片格式
* @param bitmap 保存的圖片對象
* @param format 圖片格式類型
* @param quality 圖片質量,0-100,默認使用50.當類型為PNG時,此參數無效.
* @return
*/
public boolean saveBitmapToFile(String outputPath, Bitmap bitmap, Bitmap.CompressFormat format, int quality) {
if (bitmap == null || TextUtils.isEmpty(outputPath)) {
return false;
}
if (quality < 0 || quality > 100) {
quality = 50;
}
try {
File file = new File(outputPath);
//文件不存在,創建空文件
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(format, quality, out);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* 從系統content資源路徑中獲取圖片的真實地址路徑
*
* @param context
* @param bmpIntent 系統返回的Intent數據,包含圖片的content Uri
* @return
*/
public static String getBmpPathFromContent(Context context, Intent bmpIntent) {
if (bmpIntent != null && bmpIntent.getData() != null) {
Uri contentUri = bmpIntent.getData();
String uriPath = contentUri.getPath();
//content存在時
if (!TextUtils.isEmpty(uriPath)) {
String[] filePathColumn = {MediaStore.Images.Media.DATA};
//加載系統數據庫
Cursor cursor = context.getContentResolver().query(contentUri,
filePathColumn, null, null, null);
//移動到第一行,不移動將越界
cursor.moveToFirst();
//加載查詢數據的列號
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
//獲取圖片路徑
String picturePath = cursor.getString(columnIndex);
//游標應該關閉
cursor.close();
return picturePath;
} else {
return null;
}
} else {
return null;
}
}
/**
* 打開照相機並拍照(默認將照片存放到相冊中)
*
* @param act 啟動照相機的activity
* @return 返回用於存放拍照完的圖像的Uri
*/
public static Uri openCamera(Activity act, String filePath) {
if (TextUtils.isEmpty(filePath)) {
throw new RuntimeException("filePath can not be null");
}
//嘗試創建文件夾及用於存儲拍照後的文件
File outputImage = new File(filePath);
if (!outputImage.exists()) {
try {
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
//將File對象轉換為Uri並啟動照相程序
Uri photoUri = Uri.fromFile(outputImage);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); //照相
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); //指定圖片輸出地址
try {
act.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); //啟動照相
} catch (SecurityException se) {
se.printStackTrace();
return null;
}
return photoUri;
}
/**
* 打開相冊
*
* @param act 用於啟用系統相冊的Activity
* @return
*/
public static void openGrally(Activity act) {
if (act == null) {
return;
}
// //此action也可以使用,此action是選擇任何指定類型的文件
// Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
//存在多個相冊類型的應用時,顯示給用戶選擇的打個相冊的應用界面
act.startActivityForResult(Intent.createChooser(intent, "請選擇"), REQUEST_CODE_OPEN_GRALLY);
return;
}
/**
* 調用系統裁剪功能裁剪,部分機型可能不適用.
*
* @param act 啟動裁剪功能的activity
* @param bitmapIntent
* @param photoUri 拍照所得的uri
* @param width 頭像寬度
* @param height 頭像高度
* @deprecated
*/
public static void cropPhoto(Activity act, Intent bitmapIntent, Uri photoUri, int width, int height) {
if (photoUri == null) {
//若當前uri不存在(可能被系統清除了)
return;
}
if (width <= 0) {
width = 300;
}
if (height <= 0) {
height = 300;
}
Uri inputUri = photoUri;
if (bitmapIntent != null && bitmapIntent.getData() != null) {
inputUri = bitmapIntent.getData();
} else {
inputUri = photoUri;
}
//廣播刷新相冊
Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intentBc.setData(inputUri);
act.sendBroadcast(intentBc);
Intent intent = new Intent("com.android.camera.action.CROP"); //剪裁
intent.setDataAndType(inputUri, "image/*");
intent.putExtra("crop", true);
intent.putExtra("scale", true);
//設置寬高比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//設置裁剪圖片寬高
intent.putExtra("outputX", width);
intent.putExtra("outputY", height);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
intent.putExtra("return-data", false);
intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
act.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); //設置裁剪參數顯示圖片至ImageView
}
/**
* 處理拍照/打開相冊後進入裁剪工作
*
* @param requestCode 連接
* @param resultCode
* @param data Intent數據
* @param act 啟動裁剪Activity的act
* @param inputPath 裁剪圖片的路徑(針對拍照保存的路徑,相冊獲取的路徑在data中)
* @param outputPath 圖片裁剪後輸出的路徑(提供給裁剪Activity)
* @return
*/
public static boolean onActivityResult(int requestCode, int resultCode, Intent data, Activity act, String inputPath, String outputPath) {
switch (requestCode) {
case REQUEST_CODE_TAKE_PHOTO:
case REQUEST_CODE_OPEN_GRALLY:
//當啟動使用的activity不存在或者是拍照/打開相冊失敗時(resultCode不為RESULT_OK),不處理
if (act == null || resultCode != Activity.RESULT_OK) {
return false;
}
//若當前為拍照回調,嘗試讀取圖片的旋轉角度
int degree = requestCode == REQUEST_CODE_TAKE_PHOTO ? readPictureDegree(inputPath) : 0;
//獲取intent中返回的圖片路徑(如果是打開相冊部分機型可能將圖片路徑存放在intent中返回)
String tempPath = getBmpPathFromContent(act, data);
//若intent路徑存在,使用該路徑,否則使用inputPath
inputPath = TextUtils.isEmpty(tempPath) ? inputPath : tempPath;
//源圖片路徑或者輸出路徑為無效時,不處理
if (TextUtils.isEmpty(inputPath) || TextUtils.isEmpty(outputPath)) {
return false;
} else {
Intent cropIntent = new Intent(act, CropBitmapActivity.class);
cropIntent.putExtra("inputPath", inputPath);
cropIntent.putExtra("outputPath", outputPath);
cropIntent.putExtra("degree", degree);
//啟動裁剪工具
act.startActivityForResult(cropIntent, REQUEST_CODE_CROP_PHOTO);
return true;
}
default:
return false;
}
}
}
04-15 16:30:26.850 1644-1644/com.henrytaro.ct I/bmp﹕
sampleSize = 4//長度縮小為1/4,全圖縮小為1/16
srcWidth = 1152
srcHeight = 1920
04-15 16:30:26.895 1644-1644/com.henrytaro.ct I/bmp﹕
scaleWidth = 288
scaleHeight = 480
由以上打印數據可見,全圖縮小為原來的1/16,長寬由原本的1152*1920
縮小為288*480
以ARGB.8888
格式計算,每個像素使用4個字節保存.原圖占用空間為:
1152*1920*4/1024/1024 約 8M
縮小後占用空間為:
288*480*4/1024/1024 約 0.5M
所以在可以使用縮略圖的情況下,盡可能使用縮略圖才不會加重程序的內存負擔
至於保存圖片時,如果非PNG
格式時,可以降低保存圖片的質量從而降低圖片的大小.(大部分情況下用於JPG
)
以上工具類可以打開相冊及相機,此處只示例相冊打開並裁剪圖片.
此工具類不包括裁剪功能,僅為圖片處理方法而已.
詳細裁剪實現請參考相關文章CropView
及CropBitmapActivity
回到目錄
Apache Cordova開發Android應用程序——番外篇,cordovaandroid 很多天之前就安裝了visual studio community 201
Android Developer:Memory Monitor演示 這個演示展示了在Android Studio中Memory Monitor工具基本的用法和流程。M
android中菜單(menu)的基本知識,androidmenu(一)選項菜單 1、簡單的創建菜單: 1 @Override 2 public boole
Android數據存儲之GreenDao 3.0 詳解(一),androidgreendao前言: 今天一大早收到GreenDao 3.0 正式發布的消息,自