1. 程式人生 > >Android開啟相簿vs拍照獲取圖片的原理實現

Android開啟相簿vs拍照獲取圖片的原理實現

前言:這幾天在做使用者登陸註冊的邏輯時,遇到了要修改使用者的頭像問題的解決。在此把實現的原理以及實現過程中遇到的問題分享個大家...留下些許腳印

在手機的app裡我們常常可用看到在個人中心頁面有修改頭像的功能 : 你可以選擇拍照或從相簿 選取圖片來源。

1,拍照or從相簿選擇都是通過intent 的startActivityForResulr(intent,requestCode)方法來啟動,在onActivityResutl()回撥方法中獲取圖片儲存路徑。

2.拍照實現原理:當用戶選擇拍照時其可正常拍照或自拍方式來獲取它想要的圖片。這種方式是把拍下來的照片儲存在SD卡里,通過呼叫系統相機建立一個Intent物件為其所在一個開啟相機的action。然後在onActivityResutl()方法裡獲取到剛剛儲存的圖片的SD卡路徑。然後跟即此圖片路徑你可以獲取到他的bimmap或Picasso,Universal Image Loader.....等其他圖片處理框架來設定到我們的ImageView控制元件上即可顯示

3.從相簿選擇:此方式是通過Intent來開啟系統相機。首先實列化一個Intent物件變設定其開啟相機的action,呼叫startActivityForResulr(intent,requestCode)方法即可開啟相簿。然後在onActivityResutl()方法裡通過getContentReslover()內容解析者來獲取圖片儲存的cursor遊標然後解析遊標獲取圖片的路徑,拿到圖片路徑後你就可以根據圖片路徑設定圖片的顯示啦。

所遇問題:

(1),在拍照時因為要把圖片儲存在SD卡里所以要手動用程式碼判斷手機裡是否掛載了SD卡。程式碼如下:

 //首先判斷手機是否插入SD卡
String state = Environment.getExternalStorageState();//獲取外部儲存裝置的儲存狀態
if (state.equals(Environment.MEDIA_MOUNTED)){

	//開啟系統相機的相關程式碼....

  }else{
	Toast.makeText(getActivity(), "請確保已插入SD卡", Toast.LENGTH_SHORT).show();
  }
(2),當用戶的手機系統為 android 6.0或6.0以上的系統時,需要用程式碼的方式動態的判斷使用者是否添加了讀寫SD卡的許可權【拍照把圖片寫入SD卡,從相簿選擇相當於從SD裡讀取資料】,即便你在清單檔案裡添加了但如果你不用程式碼動態方式提示使用者授權此許可權,那頭像就讀取不到。而在6.0以下的系統裡只需在AndroidMainfest.xml檔案裡新增即可。由此可見Google在不斷的加強系統對安裝應用的許可權管理。這也是出於一種安全性的考慮。

程式碼如下:

//6.0系統對許可權加強裡管理。so 使用程式碼方式來實現 設定許可權[6.0以下系統只需在清單檔案裡新增]
        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //申請WRITE_EXTERNAL_STORAGE許可權
            this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},22);//許可權,請求碼
        }

新增後可以看到此提示資訊:

                                                     

4.重寫activity的onRequestPermissionsResult()方法監聽使用者對許可權的選擇,在授予許可權的if分支裡進行拍照或開啟相簿的業務操作

 @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 使用者授予了此許可權
            } else {
                // 使用者拒絕了此許可權
            }
        }
    }

5,最後帖上完整的程式碼:

 
    //彈出對話方塊讓使用者選擇頭像來源(拍照vs相簿)
    private void showPhotoSelectDialog(){
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        View view = UIUtils.inflater(R.layout.zdy_dailog);
        builder.setView(view);
        TextView paizhaoText = (TextView)view.findViewById(R.id.zdy_text1);
        TextView fromxzheText = (TextView)view.findViewById(R.id.zdy_text2);
        final AlertDialog theDialog = builder.create();
        paizhaoText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                theDialog.dismiss();
                fromPaizhao();
            }
        });
        fromxzheText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                theDialog.dismiss();
                fromXianche();
            }
        });
        theDialog.show();
    }

    private String image_from_sd_paizhao_or_xianche__path;//圖片sd路徑
    private boolean isuploadImage;//標記使用者是否上傳圖片
    private String path; //sd卡路徑

    /**
     * 注意:
     * 1.拍照和開啟相簿Android6.0系統後要動態授予應用寫和讀SD卡的許可權,否則拍照失敗/從相簿獲取圖片失敗或應用崩潰
     * 2.需重寫onRequestPermissionsResult方法監聽使用者對許可權的授予情況(拒絕vs允許),在允許的方法裡執行相應的拍照或開啟相簿功能
     * 3.經測試,在允許了其中一個許可權後下次再點選就無需申請了
     */
    private static int WRITE_SD_CODE = 1;
    private static int READ_SD_CODE = 2;
    @TargetApi(Build.VERSION_CODES.M)
    private void fromPaizhao() {
        //6.0以上系統(拍照時)動態申請寫sd卡許可權
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},WRITE_SD_CODE);//許可權,請求碼
        }else {
            paizhao();
            Log.v("TAG", "拍照.....");
        }
    }
    @TargetApi(Build.VERSION_CODES.M)
    private void fromXianche() {
        //6.0以上系統(開啟相簿時)動態申請讀sd卡許可權
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            this.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},READ_SD_CODE);
        }else {
            xianche();
            Log.v("TAG", "相簿....");
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        Log.v("TAG", "許可權requestCode:" + requestCode);
        if (requestCode == WRITE_SD_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                paizhao();
                Log.v("TAG", "授予許可權");
            }else {
                Log.v("TAG", "拒絕許可權");
            }
        }else if(requestCode==READ_SD_CODE){
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                xianche();
                Log.v("TAG", "授予許可權");
            }else {
                Log.v("TAG", "拒絕許可權");
            }
        }
    }
    /**
     *  解決android7.0拍照應用掛掉問題:http://www.cnblogs.com/netcorner/p/6542373.html
     * 1.安卓7.0遇到 android.os.FileUriExposedException: file:///storage/emulated.. exposed beyond app through Intent.getData()
     * 2.注意:4..2.2的系統拍照要用Uri.fromFile(file)(oppo手機測試結果),否則在拍照完成點選OK確認後在onActivityResult()方法裡執行的是放棄拍照的if分支
     * 故要做系統版本判斷
     */
    private void paizhao(){
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        path = Environment.getExternalStorageDirectory().getPath() + "/";
        //將當前的拍照時間作為圖片的檔名稱
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
        String filename = simpleDateFormat.format(new Date()) + ".jpg";
        image_from_sd_paizhao_or_xianche__path = path + filename;
        File file = new File(image_from_sd_paizhao_or_xianche__path);
        //******************************************************************
        Uri photoURI;
        //解決三星7.x或其他7.x系列的手機拍照失敗或應用崩潰的bug.解決4.2.2(oppo)/4.x系統拍照執行不到設定顯示圖片的bug
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N){ //7.0以下的系統用 Uri.fromFile(file)
            photoURI = Uri.fromFile(file);
        }else {                                            //7.0以上的系統用下面這種方案
            photoURI = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider",file);
        }
        //******************************************************************
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);//將圖片檔案轉化為一個uri傳入
        startActivityForResult(intent, 100);
    }
    //開啟相簿
    private void xianche(){
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, 200);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case 100://拍照
                if (resultCode==this.RESULT_OK){
                    if(image_from_sd_paizhao_or_xianche__path!=null) {
                        //UIL框架載入本地sd卡圖片路徑為 String imageFilePath = "file://" + image_from_sd_paizhao;
                        //Picssao用file來封裝檔案
                        File file = new File(image_from_sd_paizhao_or_xianche__path);
                        Picasso.with(this).load(file).into(circleImageView);
                        isuploadImage = true;
                        Log.v("TAG", "拍照獲取的圖片sd卡路徑:" + image_from_sd_paizhao_or_xianche__path);
                    }
                }else{
                    ToastUtils.show("放棄拍照");
                }
                break;
            case 200://從相簿
                if(resultCode==this.RESULT_OK) {
                    //內容解析者來操作內容提供最對資料的4方法
                    if (data!=null) {
                        Uri uri = data.getData();
                        if (uri!=null) {
                            Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                            //選擇的就只是一張圖片,所以cursor只有一條記錄
                            if (cursor != null) {
                                if (cursor.moveToFirst()) {
                                    image_from_sd_paizhao_or_xianche__path = cursor.getString(cursor.getColumnIndex("_data"));//獲取相簿路徑欄位
                                    File file = new File(image_from_sd_paizhao_or_xianche__path);
                                    Picasso.with(this).load(file).into(circleImageView);
                                    isuploadImage = true;
                                    Log.v("TAG", "開啟相簿獲取的圖片sd卡路徑:" + image_from_sd_paizhao_or_xianche__path);
                                }
                            }
                        }
                    }
                }else{
                    ToastUtils.show("放棄從相簿選擇");
                }
                break;
        }
    }

6,當手機系統為Android7.0. 開啟相機拍照時應用會掛掉,報android.os.FileUriExposedException: file:///storage/emulated.. exposed beyond app through Intent.getData()