說(shuō)明:由于在測(cè)試的時(shí)候使用的是Android 7.1的機(jī)器拧烦,所以郭霖大神書中的代碼會(huì)出現(xiàn)以下問(wèn)題虹曙,
(1)APP安裝在機(jī)器上實(shí)測(cè)會(huì)出現(xiàn)調(diào)用相機(jī)無(wú)法打開的現(xiàn)象;
(2)APP調(diào)用裁剪工具的時(shí)候會(huì)有Toast提示“無(wú)法加載此圖片”束莫。
以下是對(duì)郭霖大神在書中代碼的詳細(xì)講解以及適配唤锉,從而保證一切功能正常執(zhí)行。
Step 1. 新建一個(gè)activity_main.xml布局椅贱,這個(gè)沒(méi)什么問(wèn)題懂算。
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:id="@+id/take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照"/>
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
Step 2. 新建MainActivity,以下是郭霖大神書中原來(lái)的代碼庇麦。代碼中比較難以理解的地方注釋都寫的很詳細(xì)计技。
public class MainActivity extends AppCompatActivity {
public static final int TAKE_PHOTO = 1;
public static final int CROP_PHOTO = 2;
private Button takePhoto;
private ImageView imageView;
private Uri imageUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
takePhoto = (Button)findViewById(R.id.take_photo);
imageView = (ImageView)findViewById(R.id.image_view);
takePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
File outputImage = new File(Environment.getExternalStorageDirectory(),
"maybe.jpg");
if(outputImage.exists()){
outputImage.delete();
}
try {
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//將File對(duì)象轉(zhuǎn)換成Uri對(duì)象,因?yàn)閁ri對(duì)象標(biāo)識(shí)著maybe.jpg這張圖片的唯一地址山橄。
//由于Intent能傳遞的數(shù)據(jù)空間有限垮媒,所以需要轉(zhuǎn)化成Uri
//大圖片用Uri,小圖片用Bitmap
//以下代碼解決了Android 7.0 打開相機(jī)崩潰的問(wèn)題
if(Build.VERSION.SDK_INT > 24){
imageUri = FileProvider.getUriForFile(MainActivity.this,
"com.example.android.choosepicdemo.fileprovider",outputImage);
}else{
imageUri = Uri.fromFile(outputImage);
}
//Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
//第一個(gè)參數(shù):一個(gè)Intent對(duì)象
//第二個(gè)參數(shù):如果> = 0,當(dāng)Activity結(jié)束時(shí)requestCode將歸還在onActivityResult()中,
//以便確定返回的數(shù)據(jù)是從哪個(gè)Activity中返回
startActivityForResult(intent,TAKE_PHOTO);
}
});
}
/*第一個(gè)參數(shù):這個(gè)整數(shù)requestCode提供給onActivityResult,是以便確認(rèn)返回的數(shù)據(jù)是從哪個(gè)Activity返回的驾胆。
這個(gè)requestCode和startActivityForResult中的requestCode相對(duì)應(yīng)涣澡。
第二個(gè)參數(shù):這整數(shù)resultCode是由子Activity通過(guò)其setResult()方法返回。
第三個(gè)參數(shù):一個(gè)Intent對(duì)象丧诺,帶有返回的數(shù)據(jù)入桂。*/
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case TAKE_PHOTO:
if(resultCode == RESULT_OK){
Intent intent = new Intent("com.android.camera.action.CROP");
//以下兩行代碼適配Android 7.0 解決了無(wú)法加載圖片的問(wèn)題
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(imageUri,"image/*");
intent.putExtra("scale",true);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent,CROP_PHOTO);
}
break;
case CROP_PHOTO:
if(resultCode == RESULT_OK){
// 因?yàn)閕mageUri是Uri類型的,需要轉(zhuǎn)換才能被decodeStream使用
// 使用getContentResolver()
// 因?yàn)樵贏ndroid系統(tǒng)里面驳阎,數(shù)據(jù)庫(kù)是私有的抗愁。
// 一般情況下外部應(yīng)用程序是沒(méi)有權(quán)限讀取其他應(yīng)用程序的數(shù)據(jù)。
// 如果你想公開你自己的數(shù)據(jù)呵晚,你有兩個(gè)選擇:
// 你可以創(chuàng)建你自己的內(nèi)容提供器(一個(gè)ContentProvider子類)或者
// 你可以給已有的提供器添加數(shù)據(jù)-如果存在一個(gè)控制同樣類型數(shù)據(jù)的內(nèi)容提供器且你擁有寫的權(quán)限蜘腌。
// 外界的程序通過(guò)ContentResolver接口可以訪問(wèn)ContentProvider提供的數(shù)據(jù),
// 在Activity當(dāng)中通過(guò)getContentResolver()可以得到當(dāng)前應(yīng)用的 ContentResolver實(shí)例
try {
//解析成Bitmap
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
需要注意的是以下代碼是增加了的內(nèi)容:
//以下代碼解決了Android 7.0 打開相機(jī)崩潰的問(wèn)題
if(Build.VERSION.SDK_INT > 24){
imageUri = FileProvider.getUriForFile(MainActivity.this,
"com.example.android.choosepicdemo.fileprovider",outputImage);
}else{
imageUri = Uri.fromFile(outputImage);
}
如果不這么處理的話饵隙,會(huì)導(dǎo)致在調(diào)用相機(jī)獲取 Uri 的時(shí)候發(fā)生崩潰撮珠。
原因很明顯,file:// 不被允許作為一個(gè)附加的 Uri 的意圖金矛,否則會(huì)拋出 FileUriExposedException 芯急。其實(shí)背后有一個(gè)很好的理由勺届,如果文件路徑被發(fā)送到目標(biāo)應(yīng)用程序(相機(jī)應(yīng)用程序在這種情況下),文件將完全訪問(wèn)通過(guò)相機(jī)應(yīng)用程序的過(guò)程娶耍,而不僅僅只有發(fā)起者能收到免姿。但讓我們考慮一下,實(shí)際上是由我們的應(yīng)用程序去啟動(dòng)攝像頭拍照榕酒,并保存作為我們的應(yīng)用程序的代表文件胚膊。因此,該文件的訪問(wèn)權(quán)限應(yīng)該是我們的應(yīng)用程序而不是攝像頭應(yīng)用程序本身想鹰。這就是為什么現(xiàn)在 file:// 在 targetSdkVersion 24 中要求每一位開發(fā)者都去完成這個(gè)任務(wù)紊婉。參考博文:http://www.sohu.com/a/136282885_659256
所以需要采用FileProvider來(lái)處理。
具體的步驟如下:
- 首先在AndroidManifest.xml中聲明provider杖挣。這里要注意android:authorities要是你的包名一致肩榕,其他固定。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:authorities="com.example.android.choosepicdemo.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>
- 在res文件夾下創(chuàng)建xml文件夾惩妇,并創(chuàng)建provide_paths.xml(該名稱應(yīng)與上述android:resource里一致),maybe為圖片保存時(shí)的名稱株汉,根據(jù)你自己命名而定。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name = "maybe" path = "."/>
</paths>
- 修改java代碼如下
//以下代碼解決了Android 7.0 打開相機(jī)崩潰的問(wèn)題
if(Build.VERSION.SDK_INT > 24){
imageUri = FileProvider.getUriForFile(MainActivity.this,
"com.example.android.choosepicdemo.fileprovider",outputImage);
}else{
imageUri = Uri.fromFile(outputImage);
}
到這里就解決了在android 7.0以上平臺(tái)上調(diào)用攝像頭崩潰的問(wèn)題歌殃。
Step 3. 解決調(diào)用裁剪工具時(shí)提示“無(wú)法加載圖片”的問(wèn)題
很簡(jiǎn)單乔妈,在MainActivity.java中加上兩行代碼就搞定。
//以下兩行代碼適配Android 7.0 解決了無(wú)法加載圖片的問(wèn)題
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
以上氓皱。
點(diǎn)擊此處下載源碼路召。