Android生成二維碼--拍照或從相冊選取圖片

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

先看效果圖:html

      

      

 

 

拍照

1.啓動相機程序 

   拍照能夠直接啓動系統的相機程序,代碼以下android

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()方法就能獲得返回值。git

 

2.指定保存路徑github

    上面的intent中指定了保存路徑,也就是代碼中的imageUri。首先須要建立一個File對象用來存放圖片,並使用getExternalCacheDir()方法將圖片放入SD卡當前應用包名下的緩存目錄中。
    接着須要將File對象轉換成Uri對象,須要進行版本判斷,7.0如下直接調用Uri的fromFile方法,獲得的是圖片本地真實路徑。7.0以上直接使用真實路徑會被認爲不安全,會拋出異常,因此須要使用特殊的內容提供器FileProvider,它是ContentProvider的一個子類,做用即是將受限的Uri轉換爲可共享的Uri。
    既然使用內容提供器,固然須要進行註冊了,AndroidManifest.xml中加入:數組

<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.xch.generateqrcode.fileprovider" android:exported="false" android:grantUriPermissions="true">
    <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>

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

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!--path設爲空值表示將整個sd卡共享-->
    <external-path name="my_images" path="" />
</paths>

    接下來在代碼中調用FileProvider的getUriForFile()方法生成Uri對象,須要傳入三個參數,第一個參數是上下文,第二個參數即是 Manifest 文件中註冊 FileProvider 時設置的 authorities 屬性值,第三個參數爲要共享的文件,也就是以前建立的File對象。安全

 

   完整代碼ide

/** * 拍照 */
private void takePhoto() { // 建立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); }

 

獲取拍照結果:ui

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case TAKE_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,可先進行壓縮。this

 

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

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

從相冊選擇圖片

  • 打開相冊 
    一樣用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便可對返回結果進行處理
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case CHOOSE_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版本之前,直接獲取真實路徑 * @param data */
private void handleImageBeforeKitKat(Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null); //顯示圖片
 displayImage(imagePath); } private String 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(); } return path; }

 

    Android 4.4以後,須要解析封裝過的Uri,若是Uri是document類型,則經過document id處理,若是是content類型的Uri,則使用普通方式處理,若是是file類型的Uri,直接獲取圖片路徑便可。獲取到路徑後便可顯示,以下

private void handleImageOnKitKat(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); // 根據圖片路徑顯示圖片
}

 

顯示圖片

/** * 顯示圖片 * @param imagePath 圖片路徑 */
private void displayImage(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(); }
@Override public 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參數中,若是被受權,則打開相冊,不然提示用戶未打開權限沒法使用。


    源碼已更新至GitHub,地址:https://github.com/yangxch/GenerateQRCode

相關文章
相關標籤/搜索