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){ List resInfoList=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文件部分 //清单文件注册部分
也封装了从本地获取,以及拍照获取的相关功能,可以值得学习,毕竟不少坑。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。