知识点:
- 在官方7.0的以上的系统中,尝试传递 file://URI可能会触发FileUriExposedException
- google官方FileProvider
- 调用系统裁剪的过程中涉及到两个 Uri 对象:inputUri 和 outputUri
- 零时赋权
(1)Context.grantUriPermission(package, Uri, mode_flags)Context.grantUriPermission(package, Uri, mode_flags)
(2)Intent.setFlags() - 自动创建Android/data/你的包名/files文件
this.getExternalFilesDir(null).getAbsolutePath()
- 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。
- 在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()
- 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;
}
}
}