1. 程式人生 > 其它 >Android學習——從相簿中選擇照片

Android學習——從相簿中選擇照片

雖然呼叫攝像頭拍照既方便又快捷,但我們並不是每次都需要去當場拍一張照片的。因為每個人的手機相簿裡應該都會存有許許多多張照片,直接從相簿裡選取一張現有的照片會比開啟相機拍一張照片更加常用。一個優秀的應用程式應該將這兩種選擇方式都提供給使用者,由使用者來決定使用哪一種。下面我們就來看一下,如何才能實現從相簿中選擇照片的功能。

還是在CameraAlbumTest專案的基礎上進行修改,編輯activity_main.xml檔案,在佈局中新增一個按鈕用於從相簿中選擇照片,程式碼如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/take_photo" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Take Photo" /> <ImageView android:id
="@+id/picture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> <Button android:id="@+id/choose_from_album" android:layout_width="match_parent" android:layout_height
="wrap_content" android:text="Choose from Album" /> </LinearLayout>
package com.example.cameraalbumtest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;

import android.Manifest;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    public static final int TAKE_PHOTO=1;//拍照
    public static final int CHOOSE_PHOTO=2;//從相簿取照片

    private ImageView picture;

    private Uri imageUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //獲取例項
        Button takePhoto=(Button) findViewById(R.id.take_photo);
        picture =(ImageView) findViewById(R.id.picture);

        takePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //建立file物件,用於儲存拍照後的圖片
                File outputImage=new File(getExternalCacheDir(),"output_image.jpg");//把圖片進行命名
                //呼叫getExternalCacheDir()可以得到手機SD卡的應用關聯快取目錄
                //所謂的應用關聯快取目錄,就是指SD卡中專門用於存放當前應用快取資料的位置
                //具體的路徑是/sdcard/Android/data/<package name>/cache

                //因為從Android 6.0系統開始,讀寫SD卡被列為了危險許可權,
                // 如果將圖片存放在SD卡的任何其他目錄,都要進行執行時許可權處理才行,而使用應用關聯目錄則可以跳過這一步。
                try {
                    if (outputImage.exists()) {//如果已經存在了圖片,則刪掉,
                        outputImage.delete();
                    }
                    outputImage.createNewFile();//將圖片放入
                }catch (IOException e){
                    e.printStackTrace();
                }

                //獲取Uri物件
                //這個Uri物件標識著output_image.jpg這張圖片的本地真實路徑。
                if (Build.VERSION.SDK_INT >= 24) {
                    //呼叫FileProvider的getUriForFile() 方法將File 物件轉換成一個封裝過的Uri物件
                    imageUri = FileProvider.getUriForFile(MainActivity.this,"com.example.cameraalbumtest.fileprovider", outputImage);
                    //FileProvider則是一種特殊的內容提供器,它使用了和內容提供器類似的機制來對資料進行保護,
                    // 可以選擇性地將封裝過的Uri共享給外部,從而提高了應用的安全性。
                    //第一個引數要求傳入Context 物件
                    //第二個引數可以是任意唯一的字串 (需要在AndroidManifest.xml中宣告)
                    //第三個引數則是我們剛剛建立的File 物件
                } else {//若系統的版本低於Android7.0,則呼叫下面的方法將File物件轉換為Uri物件
                    imageUri = Uri.fromFile(outputImage);
                }


                //啟動相機程式
                Intent intent= new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);//指定圖片的輸出地址
                startActivityForResult(intent,TAKE_PHOTO);//呼叫startActivityForResult() 來啟動活動。
            }
        });

        //從相簿中取圖片
        Button chooseFromAlbum=(Button) findViewById(R.id.choose_from_album);
        chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {//定義該按鈕點選事件
                //申請一個執行時許可權處理
                //許可權WRITE_EXTERNAL_STORAGE表示同時授予程式對SD卡讀和寫的能力。
                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方法來獲取圖片
                    openAlbum();
                }

            }
        });

    }

    //定義openAlbum()方法來獲取圖片
    private void openAlbum(){
        Intent intent =new Intent("android.intent.action.GET_CONTENT");//構建一個intent物件,並將它的action指定
        intent.setType("image/*");
        startActivityForResult(intent, CHOOSE_PHOTO);//開啟相簿程式,選擇照片
        //給第二個引數傳入的值變成了CHOOSE_PHOTO
        // 這樣當從相簿選擇完圖片回到onActivityResult() 方法時
        // 就會進入CHOOSE_PHOTO 的case 來處理圖片。
    }

    @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,"You denied the permission",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }


    //使用startActivityForResult() 來啟動活動的,
    // 因此拍完照後會有結果返回到onActivityResult() 方法中。
    //在此函式中顯示影象
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case TAKE_PHOTO://拍照
                if (resultCode == RESULT_OK) {//如果拍照成功
                    try {
                        // 將拍攝的照片顯示出來
                        //可以呼叫BitmapFactory的decodeStream() 方法將output_image.jpg這張照片解析成Bitmap 物件
                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                        picture.setImageBitmap(bitmap);//將Bitmap物件,設定到ImageView中顯示出來。
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case CHOOSE_PHOTO://開啟相簿
                if (resultCode == RESULT_OK){
                    //判斷手機的系統的版本號
                    if (Build.VERSION.SDK_INT>=19){
                        //4.4及以上系統使用這個方法處理圖片
                        handleImageOnKitKat(data);
                    } else {
                        // 4.4以下系統使用這個方法處理圖片
                        handleImageBeforeKitKat(data);
                    }
                }
            default:
                break;
        }
    }

    //因為Android系統從4.4版本開始,選取相簿中的圖片不再返回圖片真實的Uri了,而是一個封裝過的Uri
    // 因此如果是4.4版本以上的手機就需要對這個Uri進行解析才行。
    @TargetApi(19)
    private void handleImageOnKitKat(Intent data) {//用於解析Android4.4版本以上的封裝過的Uri
        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())) {
                //如果Uri的authority是media格式的話,document id 還需要再進行一次解析
                //要通過字串分割的方式取出後半部分才能得到真正的數字id
                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); // 根據圖片路徑顯示圖片
    }

    //它的Uri是沒有封裝過的,不需要任何解析
    private void handleImageBeforeKitKat(Intent data) {
        Uri uri = data.getData();
        String imagePath = getImagePath(uri, null);//直接將Uri傳入到getImagePath() 方法當中就能獲取到圖片的真實路徑了
        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;
    }


    //將圖片顯示到介面上
    private void displayImage(String imagePath) {
        if (imagePath != null) {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            picture.setImageBitmap(bitmap);
        } else {
            Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
        }
    }
}