這節(jié)課教我們?nèi)绾瓮ㄟ^將工作委托給設(shè)備上的另一個(gè)攝像頭應(yīng)用來拍攝一張照片儡湾。(如果你想構(gòu)建自己的相機(jī)功能,請(qǐng)參見控制相機(jī)。)
如果整合照片只是你應(yīng)用的小部分時(shí),你想要最低成本拍照输钩,而不是重新實(shí)現(xiàn)一個(gè)拍照功能。令人高興的是仲智,大多數(shù)android設(shè)備已經(jīng)安裝了至少一個(gè)攝像頭應(yīng)用程序买乃。在這節(jié)課中,你要學(xué)習(xí)如何讓它為你拍照坎藐。
請(qǐng)求相機(jī)功能
如果拍照是你應(yīng)用程序的一個(gè)基本功能为牍,那么將其在谷歌應(yīng)用市場(chǎng)上的可見性限制是哼绑,有攝像頭的設(shè)備岩馍。為了宣傳您的應(yīng)用程序依賴于有一個(gè)攝像頭,在您的清單文件中放置一個(gè)< uss -feature>標(biāo)記:
<manifest ...>
<uses-feature android:name="android.hardware.camera"android:required="true"/>
</manifest >
如果您的應(yīng)用程序使用抖韩,但不需要攝像頭來運(yùn)行蛀恩,則設(shè)置android:required="false"。通過這樣做茂浮,谷歌Play將允許沒有攝像頭的設(shè)備下載應(yīng)用程序双谆。然后,您有責(zé)任通過調(diào)用hasSystemFeature(packagemanagemanager . feature_camera)檢查相機(jī)在運(yùn)行時(shí)的可用性席揽。如果沒有攝像頭顽馋,你應(yīng)該禁用你的攝像頭功能。
用相機(jī)應(yīng)用拍照
Android中將操作委托給其他應(yīng)用程序的方式是調(diào)用一個(gè)意圖(意圖:用來描述您想要做的事情)幌羞。這個(gè)過程包括三個(gè)部分:意圖本身寸谜、啟動(dòng)外部活動(dòng)的調(diào)用,以及當(dāng)焦點(diǎn)返回到活動(dòng)時(shí)處理圖像數(shù)據(jù)的代碼属桦。
這是一個(gè)調(diào)用捕獲照片意圖的函數(shù)熊痴。
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult
(takePictureIntent, REQUEST_IMAGE_CAPTURE);}}
請(qǐng)注意他爸,startActivityForResult()方法受到調(diào)用resolveActivity()條件的保護(hù)(條件限制保護(hù)),該條件返回能夠處理意圖的第一個(gè)活動(dòng)組件果善。執(zhí)行這個(gè)檢查是很重要的诊笤,因?yàn)槿绻{(diào)用startActivityForResult()使用了任何應(yīng)用程序都無法處理的意圖,您的應(yīng)用程序?qū)⒈罎⒔砩隆K灾灰Y(jié)果不是空的讨跟,使用意圖是安全的。
獲取縮略圖
如果拍照這個(gè)簡(jiǎn)單的壯舉并不是你的應(yīng)用野心的頂點(diǎn)鄙煤,那么你可能想要從相機(jī)應(yīng)用中取回照片许赃,然后用它做點(diǎn)什么。
Android Camera應(yīng)用程序?qū)⒄掌幋a為onActivityResult()返回的意圖的Extra信息中的一個(gè)小位圖馆类,對(duì)應(yīng)鍵為“data”混聊。下面的代碼檢索此映像并將其顯示在ImageView中。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
? ? if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
? ? ? ? Bundle extras = data.getExtras();
? ? ? ? Bitmap imageBitmap = (Bitmap) extras.get("data");
? ? ? ? mImageView.setImageBitmap(imageBitmap);
? ? }
}
注意:這個(gè)來自“data”的縮略圖當(dāng)做一個(gè)圖標(biāo)icon可能ok乾巧,但不能再多要求了句喜。處理一個(gè)完整的圖像需要更多的工作。
保存全尺寸照片?
keywords:full-size
如果你給它一個(gè)文件來保存沟于,Android Camera應(yīng)用程序會(huì)保存全屏照片咳胃。你必須提供一個(gè)完全合格的文件名稱,以便相機(jī)應(yīng)用程序保存照片旷太。
通常展懈,用戶用設(shè)備攝像頭拍攝的任何照片都應(yīng)該保存在設(shè)備的公共外部存儲(chǔ)中,以便所有應(yīng)用程序都能訪問這些照片供璧。共享照片的適當(dāng)目錄由getExternalStoragePublicDirectory()提供存崖,并帶有DIRECTORY_PICTURES參數(shù)。由于此方法提供的目錄在所有應(yīng)用程序之間共享睡毒,因此對(duì)其進(jìn)行讀寫需要分別具有READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE權(quán)限来惧。寫入權(quán)限隱式地允許讀取,所以如果您需要寫入外部存儲(chǔ)演顾,那么您只需要請(qǐng)求一個(gè)權(quán)限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
但是供搀,如果希望照片只對(duì)應(yīng)用程序私有,可以使用getExternalFilesDir()提供的目錄钠至。在Android 4.3和更低的版本中葛虐,寫入這個(gè)目錄也需要WRITE_EXTERNAL_STORAGE權(quán)限。從Android 4.4開始棉钧,不再需要權(quán)限屿脐,因?yàn)槠渌麘?yīng)用程序無法訪問該目錄,所以您可以通過添加maxSdkVersion屬性,只在Android的較低版本上請(qǐng)求權(quán)限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"android:maxSdkVersion="18"/>
注意:在用戶卸載應(yīng)用程序時(shí)摄悯,將刪除保存在getExternalFilesDir()或getFilesDir()所提供目錄中的文件赞季。
確定文件的目錄后,需要?jiǎng)?chuàng)建一個(gè)抗沖突的文件名奢驯。您可能還希望將路徑保存到成員變量中以供以后使用申钩。這里有一個(gè)方法的示例解決方案,該方法使用日期-時(shí)間戳為新照片返回唯一的文件名:
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
? ? // Create an image file name
? ? String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
? ? String imageFileName = "JPEG_" + timeStamp + "_";
? ? File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
? ? File image = File.createTempFile(
? ? ? ? imageFileName,? /* prefix */
? ? ? ? ".jpg",? ? ? ? /* suffix */
? ? ? ? storageDir? ? ? /* directory */
? ? );
? ? // Save a file: path for use with ACTION_VIEW intents
? ? mCurrentPhotoPath = image.getAbsolutePath();
? ? return image;
}
使用此方法為照片創(chuàng)建文件瘪阁,您現(xiàn)在可以創(chuàng)建和調(diào)用這樣的意圖:
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
? ? Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
? ? // Ensure that there's a camera activity to handle the intent
? ? if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
? ? ? ? // Create the File where the photo should go
? ? ? ? File photoFile = null;
? ? ? ? try {
? ? ? ? ? ? photoFile = createImageFile();
? ? ? ? } catch (IOException ex) {
? ? ? ? ? ? // Error occurred while creating the File
? ? ? ? ? ? ...
? ? ? ? }
? ? ? ? // Continue only if the File was successfully created
? ? ? ? if (photoFile != null) {
? ? ? ? ? ? Uri photoURI = FileProvider.getUriForFile(this,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "com.example.android.fileprovider",
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? photoFile);
? ? ? ? ? ? takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
? ? ? ? ? ? startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
? ? ? ? }
? ? }
}
注意:getUriForFile(context ,string,file)會(huì)返回內(nèi)容:// URI,我們使用?getUriForFile時(shí)會(huì)導(dǎo)致異常撒遣。對(duì)于最近針對(duì)Android 7.0 (API級(jí)別24)和更高的應(yīng)用程序,跨包邊界傳遞文件:// / URI會(huì)導(dǎo)致FileUriExposedException管跺。因此义黎,我們現(xiàn)在提供了一種使用FileProvider存儲(chǔ)圖像的更通用的方法。
現(xiàn)在豁跑,您需要配置FileProvider廉涕。在您的應(yīng)用程序清單中,向應(yīng)用程序添加一個(gè)提供者:
<application>
<provider
? ? ? ? android:name="android.support.v4.content.FileProvider"
? ? ? ? android:authorities="com.example.android.fileprovider"
? ? ? ? android:exported="false"
? ? ? ? android:grantUriPermissions="true">
<meta-data
? ? ? ? ? ? android:name="android.support.FILE_PROVIDER_PATHS"
? ? ? ? ? ? android:resource="@xml/file_paths"/>
</provider>
</application>
路徑組件對(duì)應(yīng)于getExternalFilesDir()在調(diào)用Environment.DIRECTORY_PICTURES時(shí)返回的路徑艇拍。確保您將com.example.package.name替換為應(yīng)用程序的實(shí)際包名狐蜕。此外,還要檢查FileProvider的文檔卸夕,以獲得除external-path之外可以使用的路徑說明符的詳細(xì)描述层释。
將照片添加到一個(gè)畫廊
當(dāng)您通過一個(gè)意圖創(chuàng)建一個(gè)照片時(shí),您應(yīng)該知道您的圖像位于何處快集,因?yàn)槟紫戎付嗽诤翁幈4嫠备帷?duì)于其他人來說,可能讓你的照片可訪問的最簡(jiǎn)單的方法是從系統(tǒng)的媒體提供商(相冊(cè))那里訪問它个初。
注意:如果您將照片保存到getExternalFilesDir()提供的目錄中乖寒,媒體掃描器無法訪問這些文件,因?yàn)樗鼈兪菓?yīng)用程序的私有文件勃黍。
下面的示例方法演示了如何調(diào)用系統(tǒng)的媒體掃描器將照片添加到媒體提供者的數(shù)據(jù)庫中宵统,使其在Android Gallery應(yīng)用程序和其他應(yīng)用程序中可用。
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);
}
解碼一個(gè)按比例縮小的圖片
在內(nèi)存有限的情況下覆获,管理多個(gè)全尺寸圖像是很困難的。如果您發(fā)現(xiàn)您的應(yīng)用程序在僅僅顯示了幾個(gè)映像之后就耗盡了內(nèi)存瓢省,那么可以通過將JPEG擴(kuò)展到一個(gè)內(nèi)存數(shù)組中弄息,使其與目標(biāo)視圖的大小相匹配,從而大大減少使用的動(dòng)態(tài)堆的數(shù)量勤婚。下面的示例方法演示了這種技術(shù)摹量。
private void setPic() {// Get the dimensions of the Viewint targetW = mImageView.getWidth();int targetH = mImageView.getHeight();
// Get the dimensions of the bitmapBitmapFactory.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 imageint 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);}