android 拍照无法生成图片,Android生成二維碼--拍照或從相冊選取圖片

拍照或從相冊選擇圖片是我們日常開發中經常使用到的,可以說是必須掌握的東西。上一篇我介紹了如何生成自定義二維碼《Android生成自定義二維碼》,其中logo和代替黑色色塊的圖片都是寫死的,所以現在我們就來實現拍照或者從相冊選取圖片這個功能。

先看效果圖:

327ae71d6882cdf1c0ca8490c8f99eb6.png    

33fb4f2d939dc229a066c07302297e71.png    

05dd39e700e1f8e713e8043150045ca7.png

拍照

1.啟動相機程序

拍照可以直接啟動系統的相機程序,代碼如下

Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, TAKE_PHOTO);

這里我們利用一個隱式Intent來啟動相機程序,其中action類型:android.media.action.IMAGE_CAPTURE 表示啟動相機應用並請求一張圖片。創建了Intent對象,還需指定圖片的保存路徑,調用Intent的putExtra()方法並傳入保存路徑即可,最后調用startActivityForResult啟動活動,重寫onActivityResult()方法就能得到返回值。

2.指定保存路徑

上面的intent中指定了保存路徑,也就是代碼中的imageUri。首先需要創建一個File對象用來存放圖片,並使用getExternalCacheDir()方法將圖片放入SD卡當前應用包名下的緩存目錄中。

接着需要將File對象轉換成Uri對象,需要進行版本判斷,7.0以下直接調用Uri的fromFile方法,得到的是圖片本地真實路徑。7.0以上直接使用真實路徑會被認為不安全,會拋出異常,所以需要使用特殊的內容提供器FileProvider,它是ContentProvider的一個子類,作用便是將受限的Uri轉換為可共享的Uri。

既然使用內容提供器,當然需要進行注冊了,AndroidManifest.xml中加入:

meta-data標簽中的內容是用來添加一個共享目錄的,引用了一個resource資源,所以需要在 res/xml 目錄下新建這個 xml 文件,用於存放應用需要共享的目錄文件。

接下來在代碼中調用FileProvider的getUriForFile()方法生成Uri對象,需要傳入三個參數,第一個參數是上下文,第二個參數便是 Manifest 文件中注冊 FileProvider 時設置的 authorities 屬性值,第三個參數為要共享的文件,也就是之前創建的File對象。

完整代碼

/*** 拍照*/

private voidtakePhoto() {//創建File對象,用於存儲拍照后的圖片

File outputImage = new File(getExternalCacheDir(), "output_image.jpg");try{if(outputImage.exists()) { outputImage.delete(); } outputImage.createNewFile(); }catch(IOException e) { e.printStackTrace(); }if (Build.VERSION.SDK_INT < 24) { imageUri=Uri.fromFile(outputImage); }else{ imageUri= FileProvider.getUriForFile(MainActivity.this, "com.example.xch.generateqrcode.fileprovider", outputImage); }//啟動相機程序

Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, TAKE_PHOTO); }

獲取拍照結果:

@Overrideprotected void onActivityResult(int requestCode, intresultCode, Intent data) {switch(requestCode) {caseTAKE_PHOTO:if (resultCode ==RESULT_OK) {try{//讀取拍照結果

logoBitmap =BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); }catch(Exception e) { e.printStackTrace(); } }break;default:break; } }

讀取拍照結果利用ContentResolver的openInputStream()方法,並傳入剛才圖片的保存路徑即可獲取圖片字節流,再利用BitmapFactory的decodeStream()方法轉為Bitmap格式,如果擔心OOM,可先進行壓縮。

最后別忘了在AndroidManifest.xml中加入權限

從相冊選擇圖片

打開相冊

同樣用Intent,action類型為android.intent.action.GET_CONTENT ,並給intent設置type為圖片,如下:

Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, CHOOSE_PHOTO);

獲取結果

在回調onActivityResult即可對返回結果進行處理

@Overrideprotected void onActivityResult(int requestCode, intresultCode, Intent data) {switch(requestCode) {caseCHOOSE_PHOTO:if (resultCode ==RESULT_OK) {//判斷手機系統版本號

if (Build.VERSION.SDK_INT >= 19) {//4.4及以上系統使用這個方法處理圖片

handleImageOnKitKat(data); }else{//4.4以下系統使用這個方法處理圖片

handleImageBeforeKitKat(data); } }break;default:break; } }

由於Android 4.4開始,不再返回圖片真實的Uri,而是一個封裝過的Uri,因此需要分別進行處理。Android 4.4之前直接調用getImagePath()方法通過Uri和selection就可獲取真實路徑,如下

/*** 4.4版本以前,直接獲取真實路徑 *@paramdata*/

private voidhandleImageBeforeKitKat(Intent data) { Uri uri=data.getData(); String imagePath= getImagePath(uri, null);//顯示圖片

displayImage(imagePath); }privateString getImagePath(Uri uri, String selection) { String path= null;//通過Uri和selection來獲取真實的圖片路徑

Cursor cursor = getContentResolver().query(uri, null, selection, null, null);if (cursor != null) {if(cursor.moveToFirst()) { path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); }returnpath; }

Android 4.4之后,需要解析封裝過的Uri,如果Uri是document類型,則通過document id處理,如果是content類型的Uri,則使用普通方式處理,如果是file類型的Uri,直接獲取圖片路徑即可。獲取到路徑后即可顯示,如下

private voidhandleImageOnKitKat(Intent data) { String imagePath= null; Uri uri=data.getData();if (DocumentsContract.isDocumentUri(this, uri)) {//如果是document類型的Uri,則通過document id處理

String docId =DocumentsContract.getDocumentId(uri);if ("com.android.providers.media.documents".equals(uri.getAuthority())) { String id= docId.split(":")[1]; //解析出數字格式的id

String selection = MediaStore.Images.Media._ID + "=" +id; imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); }else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri= ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); imagePath= getImagePath(contentUri, null); } }else if ("content".equalsIgnoreCase(uri.getScheme())) {//如果是content類型的Uri,則使用普通方式處理

imagePath = getImagePath(uri, null); }else if ("file".equalsIgnoreCase(uri.getScheme())) {//如果是file類型的Uri,直接獲取圖片路徑即可

imagePath =uri.getPath(); } displayImage(imagePath);//根據圖片路徑顯示圖片

}

顯示圖片

/*** 顯示圖片 *@paramimagePath 圖片路徑*/

private voiddisplayImage(String imagePath) {if (imagePath != null) { logoBitmap=BitmapFactory.decodeFile(imagePath);//顯示圖片

picture_logo.setImageBitmap(logoBitmap); }else{ Toast.makeText(this, "獲取圖片失敗", Toast.LENGTH_SHORT).show(); } }

動態權限申請

由於涉及了敏感權限 WRITE_EXTERNAL_STORAGE ,所以需要進行權限的動態申請,如下

//動態權限申請

if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); }else{//打開相冊

openAlbum(); }

@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {switch(requestCode) {case 1:if (grantResults.length > 0 && grantResults[0] ==PackageManager.PERMISSION_GRANTED) {//打開相冊

openAlbum(); }else{ Toast.makeText(this, "你拒絕了權限申請,可能無法打開相冊喲", Toast.LENGTH_SHORT).show(); }break;default: } }

上面的代碼就是動態申請權限的流程,首先判斷用戶是不是已經給我們權限授權了,使用ContextCompat.checkSelfPermission()方法,第一個參數是Context,第二個參數是具體的權限名稱,如果等於PackageManager.PERMISSION_GRANTED表明已授權,不等於就是沒有授權。

如果已授權就直接做后面的操作,如果沒有授權,需要調用ActivityCompat.requestPermissions()方法申請授權,第一個參數是當前Activity實例,第二個參數是權限數組,第三個是請求碼。

用戶的選擇將會回調到onRequestPermissionsResult()方法中,授權結果封裝在grantResults參數中,如果被授權,則打開相冊,否則提示用戶未打開權限無法使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值