Android 圓形頭像 相簿和拍照裁剪選取

路過火車發表於2019-01-07

知識點:

  1. 在官方7.0的以上的系統中,嘗試傳遞 file://URI可能會觸發FileUriExposedException
  2. google官方FileProvider
  3. 呼叫系統裁剪的過程中涉及到兩個 Uri 物件:inputUri 和 outputUri
  4. 零時賦權
    (1)Context.grantUriPermission(package, Uri, mode_flags)Context.grantUriPermission(package, Uri, mode_flags)
    (2)Intent.setFlags()
  5. 自動建立Android/data/你的包名/files檔案
   this.getExternalFilesDir(null).getAbsolutePath()
  1. file 轉化 content://uri
  String mTempPhotoPath = this.getExternalFilesDir(null).getAbsolutePath() + File.separator + "photo.jpeg";
  File file=new File(mTempPhotoPath);
  Uri uri=FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getApplicationContext().getPackageName() + ".fileprovider", file);
  //(重點)第三個引數為要共享的檔案,並且這個檔案一定位於path 檔案中新增的子目錄裡面。

檔案的絕對路徑與第二步指定的檔案目錄保持一致:(假設包名為com.ysx.fileproviderserver)。如上邊的程式碼的檔案的絕對路徑為/data/data/com.ysx.fileproviderserver/files/text/hello.txt,對應paths中的內容為:。getUriForFile()的第二個引數是authority,與manifest檔案中宣告的authorities保持一致。這時候,我們得到的URI的串為:content://com.ysx.fileproviderserver.fileprovider/my_files/hello.txt。

  1. 在paths節點內部支援以下幾個子節點,分別為:
	<root-path/> 代表裝置的根目錄new File("/");
	<files-path/> 代表context.getFilesDir()
	<cache-path/> 代表context.getCacheDir()
	<external-path/> 代表Environment.getExternalStorageDirectory()
	<external-files-path>代表context.getExternalFilesDirs()
	<external-cache-path>代表getExternalCacheDirs()
  1. FileProvider基本使用流程
    (1)定義一個 FileProvider
    (2)指定共享目錄
    (3)為檔案生成有效的 Content URI
    (4)申請臨時的讀寫許可權
    (5)傳送 Content URI 至其他的 App

主要程式碼

package com.example.administrator.popupwindow;

import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.databinding.DataBinderMapper;
import android.databinding.DataBindingUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

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


import de.hdodenhof.circleimageview.CircleImageView;

import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;


public class MainActivity extends BaseActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";
    private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
    Uri imageUri_crop = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap
    private CircleImageView circleImageView;
    private String mTempPhotoPath;
    private String mTempPhotoPath2;
    private Uri imageUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        circleImageView = (CircleImageView) findViewById(R.id.profile_image);
        circleImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                showTypeDialog();
            }
        });
    }

    @Override
    public void onClick(View view) {
    }

    private void showTypeDialog() {
        //顯示對話方塊
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setIcon(R.mipmap.ic_launcher);
        builder.setTitle("選擇頭像");
        final String[] items = {"相簿", "相機"};
        builder.setCancelable(true);
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                Toast.makeText(getApplicationContext(), "You clicked " + items[i], Toast.LENGTH_SHORT).show();
                if (i == 0) {
                    Intent intent1 = new Intent(Intent.ACTION_PICK, null);
                    //開啟檔案
                    intent1.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
                    startActivityForResult(intent1, 1);
                    dialogInterface.dismiss();
                }
                if (i == 1) {
                    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {   //許可權還沒有授予,需要在這裡寫申請許可權的程式碼
                        // 第二個引數是一個字串陣列,裡面是你需要申請的許可權 可以設定申請多個許可權
                        // 最後一個引數是標誌你這次申請的許可權,該常量在onRequestPermissionsResult中使用到
                        Log.e(TAG, "onClick: " + "未授權");
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                2);
                    } else { //許可權已經被授予,在這裡直接寫要執行的相應方法即可
                        takePhoto();
                    }
                    dialogInterface.dismiss();
                }
            }
        });
        final AlertDialog dialog = builder.create();
        dialog.show();
    }

    private void takePhoto() {
        // 跳轉到系統的拍照介面
        Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 指定照片儲存位置為sd卡本目錄下
        // 這裡設定為固定名字 這樣就只會只有一張temp圖 如果要所有中間圖片都儲存可以通過時間或者加其他東西設定圖片的名稱
        // File.separator為系統自帶的分隔符 是一個固定的常量
        mTempPhotoPath = this.getExternalFilesDir(null).getAbsolutePath() + File.separator + "photo1.jpeg";//android/data 自動建立目錄
        mTempPhotoPath2 = Environment.getExternalStorageDirectory() + File.separator + "photo2.jpeg";
        Log.e(TAG, "takePhoto: " + mTempPhotoPath);
        Log.e(TAG, "takePhoto2: " + mTempPhotoPath2);
        // 獲取圖片所在位置的Uri路徑
        imageUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getApplicationContext().getPackageName() + ".fileprovider", new File(mTempPhotoPath2));
        Log.e(TAG, "takePhoto3: " + imageUri);
        //下面這句指定呼叫相機拍照後的照片儲存的路徑,如果沒有這句程式碼,則不儲存照片
        intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

//        intentToTakePhoto.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
//        intentToTakePhoto.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
        startActivityForResult(intentToTakePhoto, 2);
    }

    public void cropPhoto(Uri uri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        // aspectX aspectY 是寬高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // outputX outputY 是裁剪圖片寬高
        intent.putExtra("outputX", 250);
        intent.putExtra("outputY", 250);
        intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri_crop);
        intent.putExtra("return-data", false);
        startActivityForResult(intent, 3);
    }

    private Bitmap decodeUriAsBitmap(Uri uri) {
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return bitmap;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    Uri uri = data.getData();
                    cropPhoto(uri);
                }
                break;
            case 2:
                if (resultCode == RESULT_OK) {
                    if (imageUri != null) {
                        Log.e(TAG, "onActivityResult: " + imageUri.toString());
                        cropPhoto(imageUri);
                    }
                }
                break;
            case 3:
                if (imageUri_crop != null) {
                    Bitmap bitmap = decodeUriAsBitmap(imageUri_crop);//decode bitmap
                    circleImageView.setImageBitmap(bitmap);
                }
                break;
        }
    }
}



完整Demo下載,AS直接執行

相關文章