老夫逛遍数10篇blog,写了百行code,解了数千问题,万望一篇足以解决所有问题 !~ !
基于6.0动态权限的图片选取方式,包含拍照与相册,根据此文快速实现相关功能!
如果你的项目要适用于6.0,7.0版本的系统的话,就需要兼容6.0的动态权限,7.0的数据保护!
最终效果图:
操作步骤
步骤1 :AndroidMainfest
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.yongliu.takephoto.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
- android:name
建议统一设置为”android.support.v4.content.FileProvider” - android:authorities
表示授权列表,当有多个授权时,用分号隔开。名字填写你的应用包名 - android:exported
表示该内容提供器(ContentProvider)是否能被第三方程序组件使用。一般填false,为 true则会报安全异常 android:grantUriPermissions
这个是为是否对ContentProvider中的内容无访问权限的用户提供临时权限。true,表示授予URI 临时访问权限,false表示不授予< meta-data > 指定私有文件的路径
name:可统一设置为”android.support.FILE_PROVIDER_PATHS”
android:resource:资源文件的路径名称可自行取名创建
步骤2 :res - 创建xml文件夹 - 创建file_paths(需要和之前清单文件中的名称一致)
file_paths:
- 万能方式
//可统一设置path为null,默认放在sd卡最外层空间内
<external-path name="files_root" path=""/>
- 正确方式
<?xml version="1.0" encoding="utf-8"?>
<paths>
<-- PS: path的地址需要与拍照时候存储照片的地址一致,不然会报出参数异常!!!-->
<external-path
name="files_root"
path="pictures"/>
</paths>
拍照存储地址
- 万能方式再走一波 - 。-
<?xml version="1.0" encoding="utf-8"?>
<paths>
<-- PS: name随便起,能识别就好;path表示要共享文件的路径"."表示所有路径 -->
<external-path path="." name="external_storage_root" />
</paths>
- external-path 表示用来指定共享路径的
- name 随便取,只是一个标签,买个关子,下面会解释
- path 这个比较重要,如果不设置,则表示将整个 SD 卡进行共享,然后制定了,比如 path=”Pictrues”那么,就只共享 sd卡- 下的 Pictures 文件夹
- file-path 物理路径为Context.getFilesDir() + /files/*
- cache-path 物理路径为Context.getCacheDir() + /files/*
- external-path 物理路径为Environment.getExternalStorageDirectory() + /files/*
- external-files-path 物理路径为Context.getExternalFilesDir(String) + /files/*
- external-cache-path 物理路径为Context.getExternalCacheDir() + /files/*
步骤3 :AndroidMainfest 添加权限
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
步骤4:使用BottomSheetDialog弹出相机,相册选取框(先把布局copy到layout下,待会儿用)
Effect
bsd_new_order:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_take_photo"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="拍照"
android:textColor="#2981DD"
android:textSize="14sp"/>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#E4E4E4"/>
<TextView
android:id="@+id/tv_select_gallery"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="相册"
android:textColor="#2981DD"
android:textSize="14sp"/>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#E4E4E4"/>
<TextView
android:id="@+id/tv_cancel"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="取消"
android:textColor="#363636"
android:textSize="14sp"/>
</LinearLayout>
步骤5:因为demo是我从当前项目中找出来的,我们的请求都写在base中,调用比较方便,不过当前为了方便理解,我全部写在了一个类中~(如不想看此步骤,这直接跳到步骤6看全文)
- 创建接口
/**
* 读写SD卡权限申请后回调
**/
public interface OnCheckStoragePermission {
/**
* @param haspermission true 允许 false 拒绝
**/
void onCheckStoragePression(boolean haspermission);
}
/**
* 拍照权限申请后回调
**/
public interface OnCheckCameraPermission {
/**
* @param haspermission true 允许 false 拒绝
**/
void onCheckCameraPression(boolean haspermission);
}
- 权限判断
/**
* android6.0动态权限申请:SD卡读写权限
**/
public void checkStoragePression(OnCheckStoragePermission callback) {
mOnCheckStoragePermission = callback;
if (Build.VERSION.SDK_INT >= 23) {
int checkCamerapression = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (checkCamerapression != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS}, PERMISSIONS_WRITE_STORAGE);
return;
}
mOnCheckStoragePermission.onCheckStoragePression(true);
return;
}
mOnCheckStoragePermission.onCheckStoragePression(true);
}
/**
* android6.0动态权限申请:相机使用权限
**/
public void checkCameraPression(OnCheckCameraPermission callback) {
mOnCheckCameraPermission = callback;
if (Build.VERSION.SDK_INT >= 23) {
int checkCamerapression = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (checkCamerapression != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSIONS_CAMERA);
return;
}
mOnCheckCameraPermission.onCheckCameraPression(true);
return;
}
mOnCheckCameraPermission.onCheckCameraPression(true);
}
- 权限申请
/**
* 权限申请
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean permit = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
switch (requestCode) {
case PERMISSIONS_CAMERA:
if (mOnCheckCameraPermission != null) {
mOnCheckCameraPermission.onCheckCameraPression(permit);
}
break;
case PERMISSIONS_WRITE_STORAGE:
if (mOnCheckStoragePermission != null) {
mOnCheckStoragePermission.onCheckStoragePression(permit);
}
break;
default:
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- 调用方式
//随意的一个点击事件既可
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkCameraPression(new OnCheckCameraPermission() {
@Override
public void onCheckCameraPression(boolean haspermission) {
if (haspermission) {
checkStoragePression(new OnCheckStoragePermission() {
@Override
public void onCheckStoragePression(boolean hasStorePermission) {
if (hasStorePermission) {
//相机、相册弹框
showBottomDialog();
}
}
});
}
}
});
}
});
}
步骤6 :全文预览 > <(差不多可以copy搞定,附带demo地址)
Mainactivity :
package com.example.yongliu.takephoto;
import android.Manifest;
import android.content.ContentResolver;
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.os.Environment;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomSheetDialog;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class MainActivity extends AppCompatActivity {
protected OnCheckCameraPermission mOnCheckCameraPermission;
private OnCheckStoragePermission mOnCheckStoragePermission;
private File sdcardTempFile;
private TextView mBtn;
private ImageView mImg;
protected final int PERMISSIONS_WRITE_STORAGE = 1;
public final int PERMISSIONS_CAMERA = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = findViewById(R.id.btn);
mImg = findViewById(R.id.img);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkCameraPression(new OnCheckCameraPermission() {
@Override
public void onCheckCameraPression(boolean haspermission) {
if (haspermission) {
checkStoragePression(new OnCheckStoragePermission() {
@Override
public void onCheckStoragePression(boolean hasStorePermission) {
if (hasStorePermission) {
showBottomDialog();
}
}
});
}
}
});
}
});
}
/**
* 显示拍照/从相册选取的底部对话框
*/
public void showBottomDialog() {
final BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
View dialogView = LayoutInflater.from(this).inflate(R.layout.bsd_new_order, null);
dialogView.findViewById(R.id.tv_take_photo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bottomSheetDialog != null) {
bottomSheetDialog.dismiss();
}
takePhoto();
}
});
dialogView.findViewById(R.id.tv_select_gallery).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bottomSheetDialog != null) {
bottomSheetDialog.dismiss();
}
selectFromGallery();
}
});
dialogView.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bottomSheetDialog != null) {
bottomSheetDialog.dismiss();
}
}
});
bottomSheetDialog.setContentView(dialogView);
bottomSheetDialog.show();
}
/**
* 拍照
*/
private void takePhoto() {
String path = Environment.getExternalStorageDirectory().getPath() + "/myPhoto" + SystemClock.currentThreadTimeMillis() + ".jpg";
// String path1 = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + DeviceUtils.getPackageName(GarageApp.getAppContext()) + "/img_dow_"+SystemClock.currentThreadTimeMillis() + ".jpg";
sdcardTempFile = new File(path);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(MainActivity.this, "com.example.yongliu.takephoto.fileprovider", sdcardTempFile));
} else {
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(sdcardTempFile));
}
startActivityForResult(intent, 101);
}
/**
* 从相册选取照片
*/
private void selectFromGallery() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, 102);
}
/**
* android6.0动态权限申请:SD卡读写权限
**/
public void checkStoragePression(OnCheckStoragePermission callback) {
mOnCheckStoragePermission = callback;
if (Build.VERSION.SDK_INT >= 23) {
int checkCamerapression = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (checkCamerapression != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS}, PERMISSIONS_WRITE_STORAGE);
return;
}
mOnCheckStoragePermission.onCheckStoragePression(true);
return;
}
mOnCheckStoragePermission.onCheckStoragePression(true);
}
/**
* android6.0动态权限申请:相机使用权限
**/
public void checkCameraPression(OnCheckCameraPermission callback) {
mOnCheckCameraPermission = callback;
if (Build.VERSION.SDK_INT >= 23) {
int checkCamerapression = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (checkCamerapression != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSIONS_CAMERA);
return;
}
mOnCheckCameraPermission.onCheckCameraPression(true);
return;
}
mOnCheckCameraPermission.onCheckCameraPression(true);
}
/**
* 权限申请
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean permit = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
switch (requestCode) {
case PERMISSIONS_CAMERA:
if (mOnCheckCameraPermission != null) {
mOnCheckCameraPermission.onCheckCameraPression(permit);
}
break;
case PERMISSIONS_WRITE_STORAGE:
if (mOnCheckStoragePermission != null) {
mOnCheckStoragePermission.onCheckStoragePression(permit);
}
break;
default:
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* Uri 转 绝对路径
*/
public static String getFilePathFromContentUri(Uri selectedVideoUri, ContentResolver contentResolver) {
String filePath;
String[] filePathColumn = {MediaStore.MediaColumns.DATA};
Cursor cursor = contentResolver.query(selectedVideoUri, filePathColumn, null, null, null);
//也可用下面的方法拿到cursor
//Cursor cursor = this.context.managedQuery(selectedVideoUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
}
/**
* 读写SD卡权限申请后回调
**/
public interface OnCheckStoragePermission {
/**
* @param haspermission true 允许 false 拒绝
**/
void onCheckStoragePression(boolean haspermission);
}
/**
* 拍照权限申请后回调
**/
public interface OnCheckCameraPermission {
/**
* @param haspermission true 允许 false 拒绝
**/
void onCheckCameraPression(boolean haspermission);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
/**
* 根据路径获取数据
* */
case 101:
if (resultCode == RESULT_OK && sdcardTempFile.exists()) {
try {
//如果需要上传照片到服务器,上传方法写这里既可
FileInputStream inputStream = new FileInputStream(sdcardTempFile);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
mImg.setImageBitmap(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case 102:
ContentResolver contentResolver = getContentResolver();
if (data != null) {
//如果需要把选取的图片进行上传服务器,请把下面代码注释解开,因为这样 才能获取到文件URI转的文件路径~方便上传的时候使用
//Uri uri = data.getData();
//使用工具类获取绝对路径
//String path = getFilePathFromContentUri(uri, contentResolver);
//File sdcardTempFile = new File(path);
//if (resultCode == RESULT_OK && sdcardTempFile.exists()) {
if (resultCode == RESULT_OK) {
//显示在我们UI上
Bitmap bitmap = null;
try {
//如果需要上传照片到服务器,上传方法写这里既可
bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(data.getData()));
mImg.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
break;
default:
break;
}
}
}
MainActivity Xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.yongliu.takephoto.MainActivity">
<TextView
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="获取照片"
/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#363636"
/>
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
/>
</LinearLayout>
问题解决:
出现只要有关 permisssion字段的错误,很可能就是权限问题了
根据上面的步骤3 加入对应的权限吧,宁多勿少!宁可杀错不可放过 = = !
出现 FileUriExposedException 异常
用FileProvider来解决这一问题,配置方式在文章上方
回传图片用于ImageView展示时,无法充满屏幕
//为用于展示的ImageView添加scaleType属性
android:scaleType="centerCrop"
scaleType属性:
center 按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截取图片的居中部分显示
centerCrop 按比例扩大图片的size居中显示,使得图片长(宽)等于或大于View的长(宽)
centerInside 将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片长/宽等于或小于View的长/宽
fitCenter 把图片按比例扩大/缩小到View的宽度,居中显示
fitEnd 把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置
fitStart 把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置
fitXY 把图片不按比例扩大/缩小到View的大小显示
matrix 用矩阵来绘制,动态缩小放大图片来显示
每次点击只调用一次权限申请,如第一次点击申请相机权限,第二次点击申请文件存储权限,第三次点击触发相机,相册选取框
好好看看是不是有忘记重写 onRequestPermissionsResult 方法啦!
图片裁剪
方式一:
public void cropRawPhoto(Uri uri) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", output_X);
intent.putExtra("outputY", output_Y);
intent.putExtra("return-data", true);
startActivityForResult(intent, CODE_RESULT_REQUEST);
}
在拍照完成后调用裁剪时
cropRawPhoto(FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID +
".provider", mCurrentFile));
相册选图调用裁剪
cropRawPhoto(data.getData());
相机裁剪
/**
* 拍照之后,启动裁剪
* @param camerapath 路径
* @param imgname img 的名字
* @return
*/
@NonNull
private Intent CutForCamera(String camerapath,String imgname) {
try {
//设置裁剪之后的图片路径文件
File cutfile = new File(Environment.getExternalStorageDirectory().getPath(),
"cutcamera.png"); //随便命名一个
if (cutfile.exists()){ //如果已经存在,则先删除,这里应该是上传到服务器,然后再删除本地的,没服务器,只能这样了
cutfile.delete();
}
cutfile.createNewFile();
//初始化 uri
Uri imageUri = null; //返回来的 uri
Uri outputUri = null; //真实的 uri
Intent intent = new Intent("com.android.camera.action.CROP");
//拍照留下的图片
File camerafile = new File(camerapath,imgname);
if (Build.VERSION.SDK_INT >= 24) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
imageUri = FileProvider.getUriForFile(mActivity,
"com.rachel.studyapp.fileprovider",
camerafile);
} else {
imageUri = Uri.fromFile(camerafile);
}
outputUri = Uri.fromFile(cutfile);
//把这个 uri 提供出去,就可以解析成 bitmap了
mCutUri = outputUri;
// crop为true是设置在开启的intent中设置显示的view可以剪裁
intent.putExtra("crop",true);
// aspectX,aspectY 是宽高的比例,这里设置正方形
intent.putExtra("aspectX",1);
intent.putExtra("aspectY",1);
//设置要裁剪的宽高
intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200));
intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200));
intent.putExtra("scale",true);
//如果图片过大,会导致oom,这里设置为false
intent.putExtra("return-data",false);
if (imageUri != null) {
intent.setDataAndType(imageUri, "image/*");
}
if (outputUri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
}
intent.putExtra("noFaceDetection", true);
//压缩图片
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
return intent;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
相册裁剪
/**
* 图片裁剪
* @param uri
* @return
*/
@NonNull
private Intent CutForPhoto(Uri uri) {
try {
//直接裁剪
Intent intent = new Intent("com.android.camera.action.CROP");
//设置裁剪之后的图片路径文件
File cutfile = new File(Environment.getExternalStorageDirectory().getPath(),
"cutcamera.png"); //随便命名一个
if (cutfile.exists()){ //如果已经存在,则先删除,这里应该是上传到服务器,然后再删除本地的,没服务器,只能这样了
cutfile.delete();
}
cutfile.createNewFile();
//初始化 uri
Uri imageUri = uri; //返回来的 uri
Uri outputUri = null; //真实的 uri
Log.d(TAG, "CutForPhoto: "+cutfile);
outputUri = Uri.fromFile(cutfile);
mCutUri = outputUri;
Log.d(TAG, "mCameraUri: "+mCutUri);
// crop为true是设置在开启的intent中设置显示的view可以剪裁
intent.putExtra("crop",true);
// aspectX,aspectY 是宽高的比例,这里设置正方形
intent.putExtra("aspectX",1);
intent.putExtra("aspectY",1);
//设置要裁剪的宽高
intent.putExtra("outputX", ToolUtils.dip2px(mActivity,200)); //200dp
intent.putExtra("outputY",ToolUtils.dip2px(mActivity,200));
intent.putExtra("scale",true);
//如果图片过大,会导致oom,这里设置为false
intent.putExtra("return-data",false);
if (imageUri != null) {
intent.setDataAndType(imageUri, "image/*");
}
if (outputUri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
}
intent.putExtra("noFaceDetection", true);
//压缩图片
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
return intent;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
说了这么多,还有一个大家比较关心的问题就是:哪些已经上线的旧版本应用没有做 7.0 适配工作怎么办?关于这个问题,Google 已经提前帮我们想好解决方案啦。
还记得 6.0 运行时权限问题吗?如果你不想处理运行时权限事宜的话,只需要在 build.gradle 文件中将 targetSdkVersion 的值设为 23 以下即可。
同样的,只要 targetSdkVersion 值小于 24,File URI 的使用依旧可以出现在 7.0 及以上版本的设备中。不过需要注意的是,如前面所述,调用系统裁剪功能比较特殊,可能会出现一些问题。
虽然 Google 在每次发布新版 Android 系统时,都提供这种设置 targetSdkVersion 的方式兼容旧版本,但只是一种临时解决方案,并不推荐大家使用这种技巧绕开新版本的适配问题。要知道,新出现的 API 改变一定是在解决过去存在的系统问题,是一种进步的表现。遵循规范,是我们每个开发人员开发时都应铭记于心的格言。
扩展文章: