Android获取照片、裁剪图片、压缩图片
前言
在做上一个项目时深深受到了图片上传的苦恼。图片上传主要分为两个部分,首先要获取图片,而获取图片可以分为从文件获取或者拍照获取。第二个部分才是上传图片,两个部分都是走了不少弯路。由于Android系统的碎片化比较严重,我们可能出现在第一台机子上能获取图片,但是换一个机子就不能获取图片的问题,并且在Android6.0,7.0之后也要做一定的适配,这样对于开发者来说,无疑很蛋疼。由于也是初学者,很多东西没有考虑到,适配起来也是有点难度的。
这几天也是从github上找到了一个库(地址在这TakePhoto),经过简单的学习之后,发现用起来还是蛮简单的,并且在不同机型之间都能达到同样的效果。更重要的是可以根据不同配置达到不同的效果
接下来看下用法
获取图片
1)获取TakePhoto对象
一)通过继承的方式
继承TakePhotoActivity、TakePhotoFragmentActivity、TakePhotoFragment三者之一。
通过getTakePhoto()获取TakePhoto实例进行相关操作。
重写以下方法获取结果
voidtakeSuccess(TResultresult); voidtakeFail(TResultresult,Stringmsg); voidtakeCancel();
这种方法使用起来虽然简单,但是感觉定制性不高,必须继承指定的Activity,而有时我们已经封装好了BaseActivity,不想再改了。有时候通过继承无法满足实际项目的需求。
二)通过组装的方式去使用
实现TakePhoto.TakeResultListener,InvokeListener接口。
在onCreate,onActivityResult,onSaveInstanceState方法中调用TakePhoto对用的方法。
重写onRequestPermissionsResult(intrequestCode,String[]permissions,int[]grantResults),添加如下代码。
@Override
publicvoidonRequestPermissionsResult(intrequestCode,String[]permissions,int[]grantResults){
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
//以下代码为处理Android6.0、7.0动态权限所需
TPermissionTypetype=PermissionManager.onRequestPermissionsResult(requestCode,permissions,grantResults);
PermissionManager.handlePermissionsResult(this,type,invokeParam,this);
}
重写TPermissionTypeinvoke(InvokeParaminvokeParam)方法,添加如下代码:
@Override
publicTPermissionTypeinvoke(InvokeParaminvokeParam){
TPermissionTypetype=PermissionManager.checkPermission(TContextWrap.of(this),invokeParam.getMethod());
if(TPermissionType.WAIT.equals(type)){
this.invokeParam=invokeParam;
}
returntype;
}
添加如下代码获取TakePhoto实例:
/**
*获取TakePhoto实例
*@return
*/
publicTakePhotogetTakePhoto(){
if(takePhoto==null){
takePhoto=(TakePhoto)TakePhotoInvocationHandler.of(this).bind(newTakePhotoImpl(this,this));
}
returntakePhoto;
}
2)自定义UI
不仅可以对于参数自定义,也可以对于UI的自定义,比如自定义相册,自定义Toolbar,自定义状态栏,自定义提示文字,自定义裁切工具(需要使用自带的TakePhoto裁剪才行)。
3)通过TakePhoto对象获取图片
支持从相册获取,也支持拍照,相关Api
*从相机获取图片并裁剪 *@paramoutPutUri图片裁剪之后保存的路径 *@paramoptions裁剪配置 */ voidonPickFromCaptureWithCrop(UrioutPutUri,CropOptionsoptions); /** *从相册中获取图片并裁剪 *@paramoutPutUri图片裁剪之后保存的路径 *@paramoptions裁剪配置 */ voidonPickFromGalleryWithCrop(UrioutPutUri,CropOptionsoptions); /** *从文件中获取图片并裁剪 *@paramoutPutUri图片裁剪之后保存的路径 *@paramoptions裁剪配置 */ voidonPickFromDocumentsWithCrop(UrioutPutUri,CropOptionsoptions); /** *图片多选,并裁切 *@paramlimit最多选择图片张数的限制 *@paramoptions裁剪配置 **/ voidonPickMultipleWithCrop(intlimit,CropOptionsoptions);
4)裁剪配置
CropOptions用于裁剪的配置类,可以对图片的裁剪比例,最大输出大小,以及是否使用TakePhoto自带的裁剪工具进行裁剪等,进行个性化配置。
压缩图片onEnableCompress(CompressConfigconfig,booleanshowCompressDialog)
指定压缩工具takePhoto里面自带压缩算法,也可以通过第三方的Luban进行压缩
对于TakePhoto的二次封装
封装是对第二种方法的封装,主要参考了第一种的思想封装的。
关于TakePhoto的库代码全部封装到一个TakePhotoUtil工具类中,看代码:
publicclassTakePhotoUtilimplementsTakePhoto.TakeResultListener,InvokeListener{
privatestaticfinalStringTAG=TakePhotoUtil.class.getName();
privateTakePhototakePhoto;
privateInvokeParaminvokeParam;
privateActivityactivity;
privateFragmentfragment;
publicTakePhotoUtil(Activityactivity){
this.activity=activity;
}
publicTakePhotoUtil(Fragmentfragment){
this.fragment=fragment;
}
/**
*获取TakePhoto实例
*@return
*/
publicTakePhotogetTakePhoto(){
if(takePhoto==null){
takePhoto=(TakePhoto)TakePhotoInvocationHandler.of(this).bind(newTakePhotoImpl(activity,this));
}
returntakePhoto;
}
publicvoidonCreate(BundlesavedInstanceState){
getTakePhoto().onCreate(savedInstanceState);
}
publicvoidonSaveInstanceState(BundleoutState){
getTakePhoto().onSaveInstanceState(outState);
}
publicvoidonActivityResult(intrequestCode,intresultCode,Intentdata){
getTakePhoto().onActivityResult(requestCode,resultCode,data);
}
publicvoidonRequestPermissionsResult(intrequestCode,String[]permissions,int[]grantResults){
PermissionManager.TPermissionTypetype=PermissionManager.onRequestPermissionsResult(requestCode,permissions,grantResults);
PermissionManager.handlePermissionsResult(activity,type,invokeParam,this);
}
/**
*
*@paramresult
*/
@Override
publicvoidtakeSuccess(TResultresult){
if(listener!=null){
listener.takeSuccess(result);
}
//deleteCachePic();
}
@Override
publicvoidtakeFail(TResultresult,Stringmsg){
if(listener!=null){
listener.takeFail(result,msg);
}
//deleteCachePic();
}
@Override
publicvoidtakeCancel(){
if(listener!=null){
listener.takeCancel();
}
}
publicvoiddeleteCachePic(){
Filefile=newFile(Environment.getExternalStorageDirectory(),"/takephoto/");
if(!file.exists())return;
File[]files=file.listFiles();
for(Filef:files){
f.delete();
}
}
publicinterfaceTakePhotoListener{
voidtakeSuccess(TResultresult);
voidtakeFail(TResultresult,Stringmsg);
voidtakeCancel();
}
publicTakePhotoListenerlistener;
publicvoidsetTakePhotoListener(SimpleTakePhotoListenerlistener){
this.listener=listener;
}
publicstaticclassSimpleTakePhotoListenerimplementsTakePhotoListener{
@Override
publicvoidtakeSuccess(TResultresult){
}
@Override
publicvoidtakeFail(TResultresult,Stringmsg){
}
@Override
publicvoidtakeCancel(){
}
}
@Override
publicPermissionManager.TPermissionTypeinvoke(InvokeParaminvokeParam){
PermissionManager.TPermissionTypetype=PermissionManager.checkPermission(TContextWrap.of(activity),invokeParam.getMethod());
if(PermissionManager.TPermissionType.WAIT.equals(type)){
this.invokeParam=invokeParam;
}
returntype;
}
/**
*
*@paramselect_type
*/
publicvoidtakePhoto(Select_typeselect_type,SimpleTakePhotoListenerlistener){
takePhoto(select_type,null,listener);
}
publicvoidtakePhoto(Select_typeselect_type,PhotoConfigOptionscropOptions,SimpleTakePhotoListenerlistener){
if(takePhoto==null){
Toast.makeText(activity,"请先开启照片功能",Toast.LENGTH_SHORT).show();
return;
}
setTakePhotoListener(listener);
if(cropOptions==null){
cropOptions=newPhotoConfigOptions();
}
cropOptions.configCompress();//压缩配置
cropOptions.configTakePhoto();//拍照配置
Filefile=newFile(Environment.getExternalStorageDirectory(),"/takephoto/"+System.currentTimeMillis()+".jpg");
if(!file.getParentFile().exists())file.getParentFile().mkdirs();
UriimageUri=Uri.fromFile(file);
switch(select_type){
casePICK_BY_SELECT://从相册获取
if(cropOptions.limit>1){
if(cropOptions.crop==true){
takePhoto.onPickMultipleWithCrop(cropOptions.limit,cropOptions.getCropOptions());
}else{
takePhoto.onPickMultiple(cropOptions.limit);
}
}
if(cropOptions.chooseFromFile){
if(cropOptions.crop==true){
takePhoto.onPickFromDocumentsWithCrop(imageUri,cropOptions.getCropOptions());
}else{
takePhoto.onPickFromDocuments();
}
}else{
if(cropOptions.crop==true){
takePhoto.onPickFromGalleryWithCrop(imageUri,cropOptions.getCropOptions());
}else{
takePhoto.onPickFromGallery();
}
}
break;
casePICK_BY_TAKE://拍照获取
if(cropOptions.crop==true){
takePhoto.onPickFromCaptureWithCrop(imageUri,cropOptions.getCropOptions());
}else{
takePhoto.onPickFromCapture(imageUri);
}
break;
default:
break;
}
}
/**
*图片的裁剪配置选项内部类
*/
publicclassPhotoConfigOptions{
//裁剪配置
privatebooleancrop=true;//是否裁剪
privatebooleanwithWonCrop=true;//是否采用自带的裁剪工具,默认选取第三方的裁剪工具
privatebooleancropSize=true;//尺寸还是比例
//压缩配置
privatebooleanuseOwnCompressTool=true;//使用自带的压缩工具
privatebooleanisCompress=true;//是否压缩
privatebooleanshowProgressBar=true;//显示压缩进度条
//private
privateintmaxSize=102400;
//选择图片配置
privatebooleanuseOwnGallery=true;//选择使用自带的相册
privatebooleanchooseFromFile=false;//从文件获取图片
privateintlimit=1;//选择最多图片的配置,选择多张图片会自动切换到TakePhoto自带相册
//其它配置
privatebooleansavePic=true;//选择完之后是否保存图片
privatebooleancorrectTool=false;//纠正拍照的照片旋转角度
privateintheight=800;
privateintwidth=800;
/**
*裁剪相关配置
*@return
*/
publicCropOptionsgetCropOptions(){
if(crop==false)returnnull;
CropOptions.Builderbuilder=newCropOptions.Builder();
if(cropSize){
builder.setOutputX(width).setOutputY(height);
}else{
builder.setAspectX(width).setAspectY(height);
}
builder.setWithOwnCrop(withWonCrop);//默认采用第三方配置
returnbuilder.create();
}
/**
*图片压缩相关配置
*/
publicvoidconfigCompress(){
if(isCompress==false){
takePhoto.onEnableCompress(null,false);
return;
}
CompressConfigconfig;
if(useOwnCompressTool){
config=newCompressConfig.Builder()
.setMaxSize(maxSize)
.setMaxPixel(width>height?width:height)
.enableReserveRaw(savePic)
.create();
}else{
LubanOptionsoptions=newLubanOptions.Builder()
.setMaxHeight(height)
.setMaxWidth(maxSize)
.create();
config=CompressConfig.ofLuban(options);
config.enableReserveRaw(savePic);
}
takePhoto.onEnableCompress(config,showProgressBar);
}
publicvoidconfigTakePhoto(){
TakePhotoOptions.Builderbuilder=newTakePhotoOptions.Builder();
if(useOwnGallery){
builder.setWithOwnGallery(true);
}
if(correctTool){
builder.setCorrectImage(true);
}
takePhoto.setTakePhotoOptions(builder.create());
}
publicvoidsetCrop(booleancrop){
this.crop=crop;
}
publicvoidsetWithWonCrop(booleanwithWonCrop){
this.withWonCrop=withWonCrop;
}
publicvoidsetCropSize(booleancropSize){
this.cropSize=cropSize;
}
publicvoidsetUseOwnCompressTool(booleanuseOwnCompressTool){
this.useOwnCompressTool=useOwnCompressTool;
}
publicvoidsetCompress(booleancompress){
isCompress=compress;
}
publicvoidsetShowProgressBar(booleanshowProgressBar){
this.showProgressBar=showProgressBar;
}
publicvoidsetMaxSize(intmaxSize){
this.maxSize=maxSize;
}
publicvoidsetUseOwnGallery(booleanuseOwnGallery){
this.useOwnGallery=useOwnGallery;
}
publicvoidsetChooseFromFile(booleanchooseFromFile){
this.chooseFromFile=chooseFromFile;
}
publicvoidsetLimit(intlimit){
this.limit=limit;
}
publicvoidsetSavePic(booleansavePic){
this.savePic=savePic;
}
publicvoidsetCorrectTool(booleancorrectTool){
this.correctTool=correctTool;
}
publicvoidsetHeight(intheight){
this.height=height;
}
publicvoidsetWidth(intwidth){
this.width=width;
}
}
/**
*照片获取方式,从相册获取或拍照处理
*/
publicenumSelect_type{
PICK_BY_SELECT,PICK_BY_TAKE
}
}
封装了一个BaseTakePhotoActivity,里面的代码如下:
protectedTakePhotoUtiltakePhotoUtil;
@Override
protectedvoidonCreate(@NullableBundlesavedInstanceState){
takePhotoUtil=newTakePhotoUtil(this);
if(useTakePhoto()){
takePhotoUtil.onCreate(savedInstanceState);
}
super.onCreate(savedInstanceState);
}
@Override
protectedvoidonSaveInstanceState(BundleoutState){
if(useTakePhoto()){
takePhotoUtil.onSaveInstanceState(outState);
}
super.onSaveInstanceState(outState);
}
@Override
protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){
if(useTakePhoto()){
takePhotoUtil.onActivityResult(requestCode,resultCode,data);
}
super.onActivityResult(requestCode,resultCode,data);
}
@Override
publicvoidonRequestPermissionsResult(intrequestCode,String[]permissions,int[]grantResults){
if(useTakePhoto()){
takePhotoUtil.onRequestPermissionsResult(requestCode,permissions,grantResults);
}
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
}
protectedbooleanuseTakePhoto(){
returnfalse;
}
其他对于业务的封装,可以再封装一个BaseActivity,继承自BaseTakePhotoActivity,这样就可以不影响BaseActivity的使用,如果我们在主Activity中使用获取图片的功能需要两步
1)开启TakePhoto功能
@Override
protectedbooleanuseTakePhoto(){
returntrue;
}
2)获取图片
takePhotoUtil.takePhoto(TakePhotoUtil.Select_type.PICK_BY_TAKE,newTakePhotoUtil.SimpleTakePhotoListener(){
@Override
publicvoidtakeSuccess(TResultresult){
Strings=result.getImage().getCompressPath();
Bitmapbitmap=BitmapFactory.decodeFile(s);
iv.setImageBitmap(bitmap);
}
});
takePhoto()的第一个参数是一个枚举类型的参数,分别为从相册获取和拍照获取,第二个参数为获取成功失败监听,有三个回调,由于有些回调不是必须的,所以对Listener做了一个适配,只需要回调想要的方法即可,获取成功之后就可以通过TResult封装的参数获取想要的图片以及图片地址。对于获取到的图片地址就可以做一些上传处理。
图片上传
可以借助okhttp3实现上传功能
MultipartBody.Builderbuilder=newMultipartBody.Builder().setType(MultipartBody.FORM);
RequestBodyrequestBody=RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA),file);
MultipartBody.Partpart=MultipartBody.Part.createFormData("dir",file.getName(),requestBody);
builder.addPart(part);
Request.Builderbuilder1=newRequest.Builder().url(url).post(builder.build());
Requestrequest=builder1.build();
HttpUtils.client.newCall(request).enqueue(newCallback(){
@Override
publicvoidonFailure(Callcall,IOExceptione){
}
@Override
publicvoidonResponse(Callcall,Responseresponse)throwsIOException{
if(response.isSuccessful()){
finalStrings=response.body().string();
((Activity)context).runOnUiThread(newRunnable(){
@Override
publicvoidrun(){
}
});
}
}
});
大致代码如上
最后
由于当时没有找到这个库,于是跑去问公司另一个做Android的,看了下他封装的代码,确实也是值得学习的,他的代码也是适配到了Android7.0,贴下它的代码,方便以后学习:
publicclassCameraUtil{
privatestaticfinalintREQUEST_CAPTURE_CAMERA=1221;
privatestaticfinalintREQUEST_CROP=1222;
privatestaticfinalintREQUEST_OPEN_ALBUM=1223;
privatestaticfinalStringTAG="Camera";
privatestaticUrimCacheUri;
privateCameraUtil(){
}
@RequiresPermission(allOf={Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA})
publicstaticvoidgetImageFromCamera(Activityactivity){
if(checkExternalStorageState(activity)){
activity.startActivityForResult(getImageFromCamera(activity.getApplicationContext()),REQUEST_CAPTURE_CAMERA);
}
}
@RequiresPermission(allOf={Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA})
@Deprecated
publicstaticvoidgetImageFromCamera(Fragmentfragment){
if(checkExternalStorageState(fragment.getContext())){
fragment.startActivityForResult(getImageFromCamera(fragment.getContext()),REQUEST_CAPTURE_CAMERA);
}
}
privatestaticIntentgetImageFromCamera(Contextcontext){
IntentgetImageByCamera=newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
mCacheUri=getCachePhotoUri(context.getApplicationContext());
getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT,mCacheUri);
getImageByCamera.putExtra("outputFormat",Bitmap.CompressFormat.JPEG.toString());
grantUriPermission(context,getImageByCamera,mCacheUri);
returngetImageByCamera;
}
privatestaticbooleancheckExternalStorageState(Contextcontext){
if(TextUtils.equals(Environment.getExternalStorageState(),Environment.MEDIA_MOUNTED)){
returntrue;
}
Toast.makeText(context.getApplicationContext(),"请确认已经插入SD卡",Toast.LENGTH_LONG).show();
returnfalse;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
publicstaticFilegetCachePhotoFile(){
Filefile=newFile(Environment.getExternalStorageDirectory(),"/lenso/cache/CameraTakePhoto"+System.currentTimeMillis()+".jpg");
if(!file.getParentFile().exists())file.getParentFile().mkdirs();
returnfile;
}
privatestaticUrigetCachePhotoUri(Contextcontext){
returnFileProvider.getUriForFile(context,getAuthority(context),getCachePhotoFile());
}
privatestaticUrigetCachePhotoUri(Contextcontext,Filefile){
returnFileProvider.getUriForFile(context,getAuthority(context),file);
}
publicstaticvoidonActivityResult(Activityactivity,intrequestCode,intresultCode,Intentdata,OnActivityResultListenerlistener){
onActivityResult(activity,null,requestCode,resultCode,data,listener);
}
/**
*getCachePhotoFile().getParentFile().getAbsolutePath()
*@paramdir
*@return
*/
publicstaticbooleandeleteDir(Filedir){
if(dir!=null&&dir.isDirectory()){
String[]children=dir.list();
for(inti=0;i-1){
data=cursor.getString(index);
}
}
cursor.close();
}
}
returndata;
}
privatestaticStringgetAuthority(Contextcontext){
returncontext.getPackageName()+".FileProvider";
}
publicstaticclassCropOption{
privateintaspectX=1;//x比例
privateintaspectY=1;//y比例
privatebooleanreturnData=false;//是返回bitmap,否返回uri
privateStringoutputFormat;//输出流保存格式JPGPNG...
privateintoutputX=200;//返回的bitmap宽
privateintoutputY=200;//返回的bitmap高
privateUrioutput;//输出流保存路径
privateUrisource;//需要截图的图片uri
privatebooleannoFaceDetection=true;//是否关闭人脸识别功能
//get和set方法省略
privateIntentcreate(){
if(source==null)
thrownewNullPointerException("没有设置图片uri");
Intentintent=newIntent("com.android.camera.action.CROP");
intent.setDataAndType(source,"image/*");
intent.putExtra("crop","true");
if(aspectX>0)
intent.putExtra("aspectX",aspectX);
if(aspectY>0)
intent.putExtra("aspectY",aspectY);
if(outputX>0)
intent.putExtra("outputX",outputX);
if(outputY>0)
intent.putExtra("outputY",outputY);
intent.putExtra("return-data",returnData);
if(!returnData){
output=output==null?source:output;
outputFormat=outputFormat==null?Bitmap.CompressFormat.JPEG.toString():outputFormat;
intent.putExtra(MediaStore.EXTRA_OUTPUT,output);
intent.putExtra("outputFormat",outputFormat);
intent.setType("image/*");
intent.putExtra("noFaceDetection",noFaceDetection);
}
returnintent;
}
}
privatestaticvoidgrantUriPermission(Contextcontext,Intentintent,Uriuri){
ListresInfoList=context.getPackageManager().queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
for(ResolveInforesolveInfo:resInfoList){
StringpackageName=resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName,uri,Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
}
//xml文件部分
//清单文件注册部分
也封装了从本地获取,以及拍照获取的相关功能,可以值得学习,毕竟不少坑。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。