Android-圖片的選擇,裁剪,壓縮,適配高版本

趁着國慶有時間我們來聊一聊最常用的選取用戶圖片一系列的功能,go!

效果展示 效果展示鏈接

1.圖片的選取

拍照

我們之前設置拍照保存的文件地址的Uri,都是直接Intent.putExtra(MediaStore.EXTRA_OUTPUT,文件保存的Uri路徑),但是7.0之後,對用戶權限提高了保護,之前那種方式行不通了,所以我們要做7.0的判斷,用FileProvider獲取設置保存的文件Uri,然後放到Intent.putExtra(MediaStore.EXTRA_OUTPUT,文件保存的Uri路徑)中,代碼如下:

 //相機拍照的一個標識,後面用
                TAKEPAHTO = 1;
                // 啓動系統相機
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                //7.0以下設置保存圖片的地址
                Uri norTakePhotoSaveAdr;
                // 判斷7.0android系統
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    //臨時添加一個拍照權限
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    //  //通過FileProvider獲取保存圖片file的uri(先創建file,然後獲取File的Uri)
                    takePhotoSaveAdr = FileProvider.getUriForFile(MainActivity.this,
                            "com.hxzk.bj.photodemo", new File(Environment.getExternalStorageDirectory(), "savephoto.jpg"));
                    //MediaStore.EXTRA_OUTPUT-此設置需要一個保存視頻的路徑和文件名的Uri
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, takePhotoSaveAdr);
                } else {
                    norTakePhotoSaveAdr = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "savephoto.jpg"));
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, norTakePhotoSaveAdr);
                }
                //PHOTO_TAKEPHOTO,相機的一個請求碼,返回時要用
                startActivityForResult(intent, PHOTO_TAKEPHOTO);

相冊

相比較拍照,相冊要簡單一點,代碼中都有註釋,直接看:

  //拍照的一個表示
                TAKEPAHTO = 0;
                //調用系統圖庫,選擇圖片
                //Intent.ACTION_PICK 意思是選擇數據,其具體表達有:
                // Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                //intent.setType("image/*"); 獲取本地圖片
                // intent.setType("video/*");  獲取本地視頻
                //intent.setType("audio/*")  獲取本地音樂

                // Intent intent = new Intent(Intent.ACTION_PICK);
                //  intent.setType(ContactsContract.Contacts.CONTENT_TYPE); //獲取聯繫人
                // startActivityForResult(intent, PICK_CONTACT);

                //第二種寫法
                Intent intent = new Intent(Intent.ACTION_PICK, null);
                //其中External爲sdcard下的多媒體文件,Internal爲system下的多媒體文件。
                //使用INTERNAL_CONTENT_URI只能顯示存儲在內部的照片
                intent.setDataAndType(
                        MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
                //返回結果和標識
                startActivityForResult(intent, PHOTO_PHOTOALBUM);

.拍照或選取圖片的返回結果

不論是拍照還是選取圖片,我們都要在Activity的onActivityResult中去獲取返回結果,然後進行下一步操作。這裏提一下,我們在跳轉相冊或拍照都有一個requestCode,如圖: startActivityForResult(intent, PHOTO_PHOTOALBUM); 這個,我們全局定義了三個,分別是拍照,相冊,裁剪的標識:

 //三個常量全局標識
    //圖庫
    private static final int PHOTO_PHOTOALBUM = 0;
    //拍照
    private static final int PHOTO_TAKEPHOTO = 1;
    //裁剪
    private static final int PHOTO_PHOTOCLIP = 2;

在onActivityResult中就要用來區分了:

image.png

沒啥說的!

2. 圖片的裁剪和壓縮

圖片的裁剪

圖片的裁剪我們主要看一下starPhotoZoom()這個裁剪方法,代碼如下:

 public void startPhotoZoom(Uri uri) {
        Log.e("uri=====", "" + uri);
        //com.android.camera.action.CROP,這個action是調用系統自帶的圖片裁切功能
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");//裁剪的圖片uri和圖片類型
        intent.putExtra("crop", "true");//設置允許裁剪,如果不設置,就會跳過裁剪的過程,還可以設置putExtra("crop", "circle")
        intent.putExtra("aspectX", 1);//裁剪框的 X 方向的比例,需要爲整數
        intent.putExtra("aspectY", 1);//裁剪框的 Y 方向的比例,需要爲整數
        intent.putExtra("outputX", 60);//返回數據的時候的X像素大小。
        intent.putExtra("outputY", 60);//返回數據的時候的Y像素大小。
        //uritempFile爲Uri類變量,實例化uritempFile
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (TAKEPAHTO == 1) {//如果是7.0的拍照
                //開啓臨時訪問的讀和寫權限
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                //針對7.0以上的操作
                intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, uri));
                uriClipUri = uri;
            } else {//如果是7.0的相冊
                //設置裁剪的圖片地址Uri
                uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg");
            }

        } else {
            uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg");
        }
        Log.e("uriClipUri=====", "" + uriClipUri);
        //Android 對Intent中所包含數據的大小是有限制的,一般不能超過 1M,否則會使用縮略圖 ,所以我們要指定輸出裁剪的圖片路徑
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uriClipUri);
        intent.putExtra("return-data", false);//是否將數據保留在Bitmap中返回
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//輸出格式,一般設爲Bitmap格式及圖片類型
        intent.putExtra("noFaceDetection", true);//人臉識別功能
        startActivityForResult(intent, PHOTO_PHOTOCLIP);//裁剪完成的標識

    }

圖片的壓縮

我直接上方法吧:

    /**
     * 圖片壓縮的方法
     */
    public void compressPhto(File mFile){
//        BitmapFactory這個類就提供了多個解析方法(decodeResource、decodeStream、decodeFile等)用於創建Bitmap。
//        比如如果圖片來源於網絡,就可以使用decodeStream方法;
//        如果是sd卡里面的圖片,就可以選擇decodeFile方法;
//        如果是資源文件裏面的圖片,就可以使用decodeResource方法等
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 獲取當前圖片的邊界大小
        int outHeight = options.outHeight;
        int outWidth = options.outWidth;
        String outMimeType = options.outMimeType;
        options.inJustDecodeBounds = false;
        //inSampleSize的作用就是可以把圖片的長短縮小inSampleSize倍,所佔內存縮小inSampleSize的平方
        options.inSampleSize = caculateSampleSize(options, 10, 10);
        Bitmap bitmap = BitmapFactory.decodeFile(mFile.getPath(),options);
        ivUserPhoto.setImageBitmap(bitmap);
    }

    /**
     * 計算出所需要壓縮的大小
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    private int caculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int sampleSize = 1;
        int picWidth = options.outWidth;
        int picHeight = options.outHeight;
        if (picWidth > reqWidth || picHeight > reqHeight) {
            int halfPicWidth = picWidth / 2;
            int halfPicHeight = picHeight / 2;
            while (halfPicWidth / sampleSize > reqWidth || halfPicHeight / sampleSize > reqHeight) {
                sampleSize *= 2;
            }
        }
        return sampleSize;
    }

3.圖片的適配

7.0適配問題

不知道,大家注意裁剪方法裏的這一段代碼沒?

image.png

還有拍照的這一段代碼:

image.png

爲什麼要這麼寫呢? 原來7.0一下版本我們,直接調用相機獲取的圖片地址是: file:///storage/emulated/0/temp.jpg的文件 然而7.0之後就變成: content://........文件,使用 content://代替了 file:/// 這是因爲:Android 爲了提高私有文件的安全性,從 7.0 開始對外傳遞file://類型的uri會觸發FileUriExposedException。因此,在分享私有文件時必須使用FileProvider。 那麼如果在使用之前的方法就會報錯,我們要給程序在manifest文件中加入FileProvider:

image.png

我們看看provider_paths這個文件中都有啥?

image.png

這注解寫的,不會,就轉行吧。

裁剪完成

直接加載圖片顯示

image.png

4.圖片的上傳

上傳圖片 但還有中情況是我們要上傳加載的圖片,我也給大家提供了方法:

Bitmap photoBitmap;
File file;

/**
 * 上傳圖片
 */
public void upDateFile() {
    try {
        //裁剪後的圖像轉成BitMap
        photoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uriClipUri));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    //創建路徑
    String path = Environment.getExternalStorageDirectory()
            .getPath() + "/Pic";
    //獲取外部儲存目錄
    file = new File(path);
    //創建新目錄, 創建此抽象路徑名指定的目錄,包括創建必需但不存在的父目錄。
    file.mkdirs();
    //以當前時間重新命名文件
    long time = System.currentTimeMillis();
    //生成新的文件
    file = new File(file.toString() + "/" + time+ ".png");
    //創建輸出流
    OutputStream out = null;
    try {
        out = new FileOutputStream(file.getPath());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    //壓縮文件,返回結果,true說明成功了
   boolean bCompress = photoBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
}

4.其他知識點

  • 點擊用戶頭像彈窗不是我們常用的PupUpWindow,而是自定義的BottomSheetDialog(待優化).
  • 用戶頭像被我做成了圓角,這個也是自定義ImageView,大家可以下載查看源碼。

暫時這麼多,以後補充! Github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章