這一節講述如何利用已有的拍照應用獲取一張照片。
假設你實現了大量的天氣服務來繪製一張全球氣象圖,這個圖裏面的天氣圖片都是來自你的客戶端APP拍照獲取,收集圖片只是很小的一部分工作,對於你的APP來說。所以在拍照獲取圖片方面,要儘可能的最簡單化,做最少的工作,不需要完全去重新實現一個相機,絕大部分android設備都會至少有一個拍照應用。這一節裏面就學習,如何利用已經存在的拍照應用獲取照片。
請求拍照權限
如果你的APP要利用拍照獲取圖片,那麼就要在GooglePlay裏面限制,僅僅針對那些有相機的設備才能看到。要聲明你的APP需要相機,只需要在manifest裏面聲明即可,利用<uses-feature>
來聲明:
<manifest ... >
<uses-feature android:name="android.hardware.camera" />
...
</manifest ... >
如果你的APP使用了它,但是並不是說必須用camera來獲取一張圖片,那麼在這個標籤裏面加入android:required="false"。這樣做的的話GooglePlay允許那些沒有攝像頭的設備也來下載這個APP。然後你需要在運行的時候,通過hasSystemFeature(PackageManager.FEATURE_CAMERA)去檢測設備的相機是可用。如果相機不可用,那麼你就要隱藏你的APP的拍照特性。
利用相機APP獲取照片
android利用intent來授權另外一個app來做你希望它做的事情。這個過程包括三塊:intent自身的創建,調用方法來通過intent啓動一個外部拍照Activity,處理拍照返回的圖像數據。
下面的的方法就是利用intent來拍一張照片:
private void dispatchTakePictureIntent(int actionCode) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, actionCode);
}
利用這個代碼,你的APP可以讓另外一個相機應用來執行你要求的拍照動作。當然,如果沒有APP能夠響應這個intent,你的APP會沒有任何效果,拍照會失敗。下面的代碼可以檢測是否有app能夠處理這個intent.
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
如果返回值true,說明至少有一個應用可以處理這個intent.
查看照片
如果你的APP不是僅僅想拍照照片那麼簡單,也許你會想從相機應用得到拍照獲取的照片,然後對它進行一些處理。
android相機應用把照片數據放到一個intent裏面,然後傳遞給了onActivityResult()函數,作爲一個extra數據,是一個簡單Bitmap,使用”data” 這個extra屬性獲取。下面的代碼就是獲取到照片,然後顯示到一個ImageView.
private void handleSmallCameraPhoto(Intent intent) {
Bundle extras = intent.getExtras();
mImageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(mImageBitmap);
}
注意:從data獲取到的這個縮略圖,很適合做爲一個圖標,不適合做wie其他,想要獲得一個高分辨率的全圖,需要更多的工作。
保存照片
android的相機應用在你可以它一個存儲路徑的時候,會獲取到一張全幅的圖像。你必須要提供一個路徑,包括:存儲盤,文件夾,文件名。
下面的方法是簡單的一種獲取存儲照片路徑的方法,但是僅僅針對android2.2(API 8)和之上的設備版本。
storageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
),
getAlbumName()
);
對於早期的API,你要自己提供路徑名稱:
storageDir = new File (
Environment.getExternalStorageDirectory()
+ PICTURES_DIR
+ getAlbumName()
);
注意: PICTURES_DIR這個是的是
Pictures/
,是外部或者內部存儲器上標準的用來分享照片的位置。
設置文件名稱
如前面提到的,圖像文件的位置是有設備環境決定的。你要做的就是要選擇一種沒有衝突的照片命名方案。你也許想把文件路徑名字保存起來,供後面使用。下面是一種解決辦法:
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
File image = File.createTempFile(
imageFileName,
JPEG_FILE_SUFFIX,
getAlbumDir()
);
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
添加文件名到另外一個Intent
當有了一個文件保存的位置,那麼就把這個位置通話intent傳遞給拍照應用。
File f = createImageFile();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
把照片添加到圖庫
當通過intent請求拍照到一張圖片的時候,你應該是知道你的圖片被保存到哪裏,因爲已經指定了路徑。對於其他的應用來說,最簡單的方法獲取到你的照片是通過Media Provider來獲取。
下面的代碼就演示瞭如何讓你的照片和系統的多媒體掃描相關聯,來把你的照片添加到Media Provider的數據庫。讓其對android 圖庫類的應用可見或者是其他類似的應用。
private void galleryAddPic() {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
編碼一個縮放的圖像
管理多種全尺寸的照片,對於有限的內存來說是很費力的。如果你發現你的程序在顯示一部分圖片之後,內存溢出了,你可以減少那個已經被縮放到符合到比例的jepg展開時所用的內存堆。下面的代碼展示了這個技術:
private void setPic() {
// Get the dimensions of the View
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mImageView.setImageBitmap(bitmap);
}