1. 程式人生 > >Android獲取圖片並裁剪

Android獲取圖片並裁剪

手機獲取圖片兩種方式

  • 通過相機拍照
  • 從系統圖庫中選取

基本流程

無論是選擇拍照還是選擇現成的圖片進行裁剪,都要分為以下三個步驟:

S1. 呼叫系統的拍照介面或者相簿的管理介面,由於我們要對該介面的操作結果進行監聽,因此我們在啟動Activity的時候使用startActivityForResult(Intent intent, int requestCode)函式。假設我們在介面A1中用startActivityForResult的方式啟動介面A2,那麼當A2關閉之後,會自動呼叫A1onActivityResult函式(因此我們需要重寫該函式)

S2.onActivityResult(int requestCode, int resultCode, Intent data)

函式中,data中包含被關閉的介面(A2)傳遞過來的資料,所以我們需要的”被選擇的圖片“或者”拍照生成的圖片“資訊就包含在這個裡面,此時我們就可以在這個操作的基礎之上接著進行圖片的裁剪操作,同樣是呼叫系統的介面,同樣是使用startActivityForResult的方式

S3. 最後還是一樣,在onActivityResult中我們可以得到圖片裁剪介面回傳過來的intent的物件,裡面就包含了裁剪過的圖片的資料,我們獲取資料,接下來的操作就自定義了

還好系統給我們提供了很多現成的操作介面,所以功能實現起來並不太複雜

準備工作

在使用startActivityForResult

需要傳入一個名為requestCode的引數,在onActivityResult函式中我們還會獲得一個名為resultCode的引數,這個具體可以這麼來解釋:

對於一個介面A1,我們可以通過startActivityForResult的方式來啟動很多個介面A2A3…,那麼在onActivityResult函式中就會有一個疑問了:這個函式到底是因為我們啟動的A2A3…哪個介面被關閉而呼叫的呢?

所以這時,就是requestCode發揮作用的時候了,通過它來區分不同的啟動介面,讓我們知道到底是A2A3…中的哪個介面關閉後傳遞資料過來,這樣我們就不會產生歧義了。

resultCode顧名思義就能知道是介面的執行結果程式碼,就比如在選擇圖片時,正常的流程當然是使用者點選了一個圖片然後返回,可是如果使用者按下了鍵盤上的Back鍵,這時候我們怎麼能做出正確的相應操作?只有通過這個引數,它可能的取值有Activity.RESULT_OK

Activity.RESULT_CANCELED等等,這樣就能幫助我們做出正確的判斷了。

所以就我們第一步就是先要區分一下拍照介面和相簿選擇介面,我們在類中定義:

final int REQUEST_CAPTURE = 1;  //表示拍照介面
final int REQUEST_SELECT  = 2;  //表示相簿選擇介面
final int REQUEST_CROP    = 3;  //表示圖片裁剪介面

由拍照獲取圖片

進入拍照介面很簡單,程式碼如下:

Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUEST_CAPTURE);

從相簿選擇圖片

Intent intent = new Intent();
//"image/*"表示通配所有圖片型別的檔案,包含"image/png"、"image/jpeg"等
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, REQUEST_SELECT);

上述程式碼中為什麼不是
intent.setAction(Intent.ACTION_PICK)

圖片裁剪

這一步是建立在前面的圖片獲取基礎之上,我們需要重寫Activity中的onActivityResult函式,程式碼如下:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    //操作失敗,使用者沒有拍照成功或者沒有選擇圖片時
    if (Activity.RESULT_OK != resultCode) {
        return ;
    }

    switch (requestCode) {

    //拍照
    case REQUEST_CAPTURE:
    //從相簿中選擇
    case REQUEST_SELECT: {
        //啟動裁剪介面
        Intent intent = new Intent("com.android.camera.action.CROP");  
        intent.putExtra("crop", "true");
        intent.putExtra("scale", true);
        intent.putExtra("noFaceDetection", true);
        //裁剪圖片的長寬比
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        //裁剪圖片的輸出大小
        intent.putExtra("outputX", 512);
        intent.putExtra("outputY", 512);
        //這裡我們不把圖片以Bitmap形式進行返回,因為資料量過大
        //我們將裁剪後生成的圖片儲存到本地
        intent.putExtra("return-data", false);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("裁剪輸出檔案路徑"));
        //設定需要裁剪的圖片資料
        intent.setDataAndType(data.getData(), "image/*");  
        startActivityForResult(intent, REQUEST_CROP);
    } break;

    //裁剪圖片結果
    case REQUEST_CROP: {
        // TODO what you wanna do...
        // 裁剪生成的圖片在上述程式碼中的"裁剪輸出檔案路徑"位置
    } break;

    default:
        break;
    }
}

可能出現的問題

到這裡似乎看起來就大功告成了,然而部分手機的執行結果可能事與願違,如小米4,在執行拍照返回之後進行裁剪,發現漆黑一片,沒有圖片,究其原因,是在上述程式碼中null == data.getData()導致的。

解決辦法如下:

//在啟動拍照介面之前新增
//就相當於我們要把拍照生成的圖片儲存到一個我們自定義的路徑中
//在要進行裁剪操作,我們再把這個自定義的路徑以Uri的形式傳遞過去以便找到
File img = new File("拍照圖片臨時儲存路徑");
if (img.exists()) {
    img.delete();
}
try {
    img.createNewFile();
} catch (IOException e) {
    e.printStackTrace();
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(img));

同時將上述onActivityResult函式中的:

intent.setDataAndType(data.getData(), "image/*");

修改為:

intent.setDataAndType(Uri.fromFile(new File("拍照圖片臨時儲存路徑"), "image/*");  

用這種方式的好處就相當於我們不用在資訊交換中攜帶大量的原始資料,因為這樣造成的系統開銷太大,作為替代,我們只使用一個指向該資料的地址(Android中使用的是Uri,原理一樣),這樣操作起來也就更加有效率(相當於程式設計中的傳值與傳地址的區別)。

PS:大圖裁剪同樣用的是上述方法,都把生成的資料先儲存到本地之後再通過Uri來傳遞

執行結果

這裡寫圖片描述