1. 程式人生 > >關於android6.0的拍照調不起裁剪程式,並提示“檔案無效,無法載入”

關於android6.0的拍照調不起裁剪程式,並提示“檔案無效,無法載入”

在這裡統一的記錄一下:

第一點,有一些手機沒有sd卡(說是這樣),就是那種getExternalStorage()得不到的情況,這種情況下當然是無法把拍的照片或者相簿取出來的存進這個路徑的,這是一個坑,我是這麼解決的:

//App一些資訊的儲存資料夾路徑
private static final String appDirectoryPath = File.separator+"mnt"+File.separator+"sdcard"+File.separator+"HousingEstate";
定義一個自己的路徑,前面的/mnt/sdcard好像是所有手機(我測試過的)都能找到這個路徑,然後後面加上名字,之後建立目錄,作為所有的該app的圖片所在目錄,注意的是,我這裡是在繼承自Application類(程式啟動時載入)的一個工具類裡定義的,這樣只要程式一啟動我就能得到它,不過,我原來是在這個類的
@Override
public void onCreate() { super.onCreate(); }

裡面建立目錄的,本來是可行的,可是放在android6.0上之後也不行了說是找不到file,於是就放在需要他的時候現去建立吧,我在這裡是把它放在個人資料展示頁建立的,因為這裡要用到上傳頭像。

File rootDirectory = new File(TheApplication.getAppRootDirectoryPath());
if (rootDirectory.exists()){
    if (rootDirectory.isDirectory())
        rootDirectory.delete();
} rootDirectory.mkdirs(); file = new File(rootDirectory.getPath(),"portrait.jpg");

第二點,在6.0以下所有測試的手機都完美執行上傳頭像功能(相簿方式和相機方式都行),可到了android6.0之後,大坑來了!

首先,許可權問題,具體可以參考我的另一篇部落格點選開啟連結

本想著許可權也配好了,一切都ok了,沒想到啊沒想到,神奇的事情發生了,首先,相簿選取方式上傳圖片一點毛病都沒有,就是拍照方式,能調起拍照程式,可是點選確認的時候卻提示“檔案無效,無法載入”,我以為又是許可權問題,檢查了好多遍,也查閱了好多遍,用到的許可權都配置了,可就是不行,現在一想,許可權沒配置導致的後果是能調起拍照,但是點選確認鍵無效,和這個情況也不符啊。然後我以為是路徑錯誤,各種跑斷點,這一跑不要緊,結果發現,只要是跑斷點結束後就能調起裁剪程式了,EXM?這是什麼鬼?還有延遲?然後後來我又試了試在照完之後等待一會(具體多長時間並不確定),然後就好了,問了同事,發現onActivityForResult中的返回intent(也就是data)為空,於是推斷會不會是這個原因,結果檢視資料後發現,如果你調起拍照程式的時候沒有寫putExtra(MediaStore.EXTRA_OUTPUT, imageUri);這一句,那麼他不會返回空,而是返回一個縮圖的地址(並不明白),如果指定了輸出路徑則返回的data就是null,所以和這個沒關係。於是我斷定肯定這中間有什麼地方耗時了,在還沒有準備好所需要的一切資源時就去調裁剪程式了,這時他肯定找不到資源啊,然後就各種嘗試,最後發現了問題所在,先看一下調起相簿和相機的程式碼:

fromGallery.setText("相簿");
fromGallery.setOnClickListener(new View.OnClickListener() {
                    @Override
public void onClick(View v) {
                        //如果選擇的是相簿
try {
                            if (file.exists()) {
                                file.delete();
}
                            file.createNewFile();
} catch (IOException e) {
                            e.printStackTrace();
}
                        imageUri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, GALLERY_REQUEST_CODE);
mAlertDialog.dismiss();
}
                });
TextView fromCamera = (TextView) view.findViewById(R.id.cancle);
fromCamera.setText("相機");
fromCamera.setOnClickListener(new View.OnClickListener() {
                    @Override
public void onClick(View v) {
                        //如果選擇的是拍照(不能執行註釋程式碼,否則會出現6.0調不出裁剪程式,並且無錯誤資訊,只提示檔案無效,無法載入”)
//                        try {
//                            if (file.exists()) {
//                                file.delete();
//                            }
//                            file.createNewFile();
//
//                        } catch (IOException e) {
//                            e.printStackTrace();
//                        }
imageUri = Uri.fromFile(file);
//                        Log.d("AAAAA", "Uri: "+imageUri.toString());
Intent camera = new Intent("android.media.action.IMAGE_CAPTURE");
camera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(camera, CAMERA_REQUEST_CODE);
mAlertDialog.dismiss();
}
                });
對於拍照的異常,我已經在註釋裡寫了,我想的是,file.delete()或者file.createNewFile()方法是耗時的,所以在還沒有建立好的時候就去存放所拍的照片就會出錯,而之所以相簿的沒事,是因為調起相簿根本不需要指定輸出路徑,只是裁剪的時候會用到,等到了那個時候,中間這段時間足以讓檔案刪除和建立工作都做好了,所以不會出錯。總之,我發現,try-catch語句裡面的程式碼根本可以不需要用,即便是相簿,他不需要先去建立檔案,指定輸出路徑後,他自己輸出的時候會自動建立的,你只需要給他指定路徑就好了。

再附上接收並調起裁剪程式的程式碼:

Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(imageUri, "image/*");
// 裁剪框的比例,11
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// 裁剪後輸出圖片的尺寸大小,如果不設定這個可能ImageView顯示不完整導致顯示黑色的
intent.putExtra("outputX", 800);
intent.putExtra("outputY", 600);
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CROP_REQUEST_CODE);
最後在onActivityForResult方法中顯示裁剪後的圖片:
String path = file.getPath();
bitmap = BitmapFactory.decodeFile(path);
mPortrait.setImageBitmap(bitmap);
整個流程的file只需要在Activity初始化的時候例項化一次即可,並不需要建立和多次例項化!

最後,提一下怎麼建立自定義效果的AlertDialog:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
final AlertDialog mAlertDialog = builder.create();
mAlertDialog.show();
//你自己定義的AlertDialog的內容佈局
View view = View.inflate(this, R.layout.alert_dialog, null);
TextView title = (TextView) view.findViewById(R.id.title);
title.setText("你自己的標題");
TextView content = (TextView) view.findViewById(R.id.content);
content.setText("你自己的提示框資訊");
TextView fromGallery = (TextView) view.findViewById(R.id.confirm);
fromGallery.setText("按鈕1");
fromGallery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//do something.
}
});
TextView fromCamera = (TextView) view.findViewById(R.id.cancle);
fromCamera.setText("按鈕1");
fromCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//do something.
}
});
mAlertDialog.getWindow().setContentView(view);
//寫成new ColorDrawable(0x00000000)的形式而不是null,如果是null有的手機會是黑色背景
mAlertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
//2/3等於0了!!!
// Log.d("SD", "onClick: " + 2 / 3 * TheApplication.screenWidth + "||" + 1 / 3 * TheApplication.screenHeight);
Log.d("SD", "onClick: " + (int) ((float) 2 / 3) * TheApplication.screenWidth + "||" + (int) (float) 1 / 3 * TheApplication.screenHeight);
//這裡指定成螢幕寬度的3分之2,高度的3分之1
mAlertDialog.getWindow().setLayout((int) (((float) 2 / 3) * TheApplication.screenWidth), (int) ((float) 1 / 3 * TheApplication.screenHeight));
//對於getWindow的修改都應該放在show之後!!