咔嚓
簡(jiǎn)評(píng):官方文檔也可能會(huì)有不足窃肠,多踩坑包个,多分享。
作者在學(xué)習(xí) Google 官方的 Android 拍照教程 - Take Photos Simply 時(shí)冤留,遇到了一些問(wèn)題碧囊,感覺(jué)官方教程只寫了 90%树灶。為此,作者寫了這篇文章糯而,把完整的流程和一些要注意的地方都寫了下來(lái)天通,讓你不用再去查 StackOverflow 或者 Github Gist。
1.創(chuàng)建圖片文件
File imageFile; // use this to keep a reference to the file you create so that we can access it from onActivityResult()
private void createImageFile() throws IOException {
String imageFileName = "image" + System.currentTimeMillis() + "_"; // give it a unique filename
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
// use this if you want android to automatically save it into the device's image gallery:
// File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
imageFile = File.createTempFile(imageFileName, ".jpg", storageDir);
}
- 確保每個(gè)文件名都獨(dú)一無(wú)二熄驼,所以這里用到了 System.currentTimeMillis()像寒。
- 注意代碼中注釋掉的那兩行,用它們會(huì)將拍的照片存放在手機(jī)相冊(cè)中瓜贾,在 Android 6.0 (API level 23) 以上诺祸,你需要?jiǎng)討B(tài)申請(qǐng)權(quán)限,可以考慮用 Permiso 庫(kù)來(lái)簡(jiǎn)化你的工作阐虚。
2.聲明權(quán)限
如果打算將照片存放在應(yīng)用的私有目錄中序臂,對(duì)于 Android 4.3 及以下的系統(tǒng)需要 WRITE_EXTERNAL_STORAGE 權(quán)限。
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="22" />
<!--- If you want to save to the public image directory (the image gallery), you need to do this:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
--->
...
</manifest>
- 要求了 WRITE_EXTERNAL_STORAGE 權(quán)限的同時(shí)实束,也自然有了 READ_EXTERNAL_STORAGE 權(quán)限奥秆。
- 如果是想把照片保存在手機(jī)相冊(cè)目錄下,所有版本的系統(tǒng)都需要 WRITE_EXTERNAL_STORAGE 權(quán)限咸灿。
- 官方文檔中的 maxSdkVersion 為 18构订,可能會(huì)導(dǎo)致這個(gè)問(wèn)題:stackoverflow
3.配置 FileProvider
在 AndroidManifest.xml 中增加 FileProvider:
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="{your.app.package}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
...
</application>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
4.創(chuàng)建和開(kāi)始 Intent
當(dāng)創(chuàng)建 Intent 時(shí),判斷當(dāng)前設(shè)備是否有攝像頭并且能接受這個(gè) Intent避矢。此外悼瘾,需要額外添加 flag 來(lái)授權(quán)相機(jī)能寫入我們提供的文件。
public void startCameraIntent() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
try {
imageFile = createImageFile();
} catch (IOException e) {
// file wasn't created
}
if (imageFile != null) {
Uri imageUri = FileProvider.getUriForFile(
ExampleActivity.this,
BuildConfig.APPLICATION_ID + ".fileprovider",
imageFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
// This is important. Without it, you may get Security Exceptions.
// Google fails to mention this in their docs...
// Taken from: https://github.com/commonsguy/cw-omnibus/blob/master/Camera/FileProvider/app/src/main/java/com/commonsware/android/camcon/MainActivity.java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ClipData clip = ClipData.newUri(getContentResolver(), "A photo", imageUri);
intent.setClipData(clip);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
else {
List<ResolveInfo> resInfoList =
getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, imageUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
startActivityForResult(intent, REQUEST_CODE_CAMERA);
}
else {
// device doesn't have camera
}
}
- 注意其中的 BuildConfig.APPLICATION_ID + “.fileprovider” 是和 AndroidManifest.xml 中 provider 的 android:authorities 值一致的审胸。
5.獲取結(jié)果
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { // note that data will be null
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_CAMERA) {
// imageFile will have the photo in it so do whatever you want with it
}
}
}
日?qǐng)?bào)延伸閱讀:
歡迎關(guān)注:
- 知乎專欄「極光日?qǐng)?bào)」砂沛,每天為 Makers 導(dǎo)讀三篇優(yōu)質(zhì)英文文章烫扼。
- 網(wǎng)易云電臺(tái)「極光日?qǐng)?bào)**」,上下班路上為你讀報(bào)碍庵。