Android获取照片、裁剪图片、压缩图片
前言
在做上一个项目时深深受到了图片上传的苦恼。图片上传主要分为两个部分,首先要获取图片,而获取图片可以分为从文件获取或者拍照获取。第二个部分才是上传图片,两个部分都是走了不少弯路。由于android系统的碎片化比较严重,我们可能出现在第一台机子上能获取图片,但是换一个机子就不能获取图片的问题,并且在android6.0,7.0之后也要做一定的适配,这样对于开发者来说,无疑很蛋疼。由于也是初学者,很多东西没有考虑到,适配起来也是有点难度的。
这几天也是从github上找到了一个库(地址在这takephoto),经过简单的学习之后,发现用起来还是蛮简单的,并且在不同机型之间都能达到同样的效果。更重要的是可以根据不同配置达到不同的效果
接下来看下用法
获取图片
1) 获取takephoto对象
一) 通过继承的方式
继承takephotoactivity、takephotofragmentactivity、takephotofragment三者之一。
通过gettakephoto()获取takephoto实例进行相关操作。
重写以下方法获取结果
void takesuccess(tresult result); void takefail(tresult result,string msg); void takecancel();
这种方法使用起来虽然简单,但是感觉定制性不高,必须继承指定的activity,而 有时我们已经封装好了baseactivity,不想再改了。有时候通过继承无法满足实际项目的需求。
二) 通过组装的方式去使用
实现takephoto.takeresultlistener,invokelistener接口。
在 oncreate,onactivityresult,onsaveinstancestate方法中调用takephoto对用的方法。
重写onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults),添加如下代码。
@override public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) { super.onrequestpermissionsresult(requestcode, permissions, grantresults); //以下代码为处理android6.0、7.0动态权限所需 tpermissiontype type=permissionmanager.onrequestpermissionsresult(requestcode,permissions,grantresults); permissionmanager.handlepermissionsresult(this,type,invokeparam,this); }
重写tpermissiontype invoke(invokeparam invokeparam)方法,添加如下代码:
@override public tpermissiontype invoke(invokeparam invokeparam) { tpermissiontype type=permissionmanager.checkpermission(tcontextwrap.of(this),invokeparam.getmethod()); if(tpermissiontype.wait.equals(type)){ this.invokeparam=invokeparam; } return type; }
添加如下代码获取takephoto实例:
/** * 获取takephoto实例 * @return */ public takephoto gettakephoto(){ if (takephoto==null){ takephoto= (takephoto) takephotoinvocationhandler.of(this).bind(new takephotoimpl(this,this)); } return takephoto; }
2)自定义ui
不仅可以对于参数自定义,也可以对于ui的自定义,比如自定义相册,自定义toolbar, 自定义状态栏,自定义提示文字,自定义裁切工具(需要使用自带的takephoto裁剪才行)。
3)通过takephoto对象获取图片
支持从相册获取,也支持拍照,相关api
* 从相机获取图片并裁剪 * @param outputuri 图片裁剪之后保存的路径 * @param options 裁剪配置 */ void onpickfromcapturewithcrop(uri outputuri, cropoptions options); /** * 从相册中获取图片并裁剪 * @param outputuri 图片裁剪之后保存的路径 * @param options 裁剪配置 */ void onpickfromgallerywithcrop(uri outputuri, cropoptions options); /** * 从文件中获取图片并裁剪 * @param outputuri 图片裁剪之后保存的路径 * @param options 裁剪配置 */ void onpickfromdocumentswithcrop(uri outputuri, cropoptions options); /** * 图片多选,并裁切 * @param limit 最多选择图片张数的限制 * @param options 裁剪配置 * */ void onpickmultiplewithcrop(int limit, cropoptions options);
4)裁剪配置
cropoptions 用于裁剪的配置类,可以对图片的裁剪比例,最大输出大小,以及是否使用takephoto自带的裁剪工具进行裁剪等,进行个性化配置。
压缩图片 onenablecompress(compressconfig config,boolean showcompressdialog)
指定压缩工具 takephoto里面自带压缩算法,也可以通过第三方的luban进行压缩
对于takephoto的二次封装
封装是对第二种方法的封装,主要参考了第一种的思想封装的。
关于takephoto的库代码全部封装到一个takephotoutil工具类中,看代码:
public class takephotoutil implements takephoto.takeresultlistener, invokelistener { private static final string tag = takephotoutil.class.getname(); private takephoto takephoto; private invokeparam invokeparam; private activity activity; private fragment fragment; public takephotoutil(activity activity){ this.activity = activity; } public takephotoutil(fragment fragment){ this.fragment = fragment; } /** * 获取takephoto实例 * @return */ public takephoto gettakephoto(){ if (takephoto==null){ takephoto= (takephoto) takephotoinvocationhandler.of(this).bind(new takephotoimpl(activity,this)); } return takephoto; } public void oncreate(bundle savedinstancestate){ gettakephoto().oncreate(savedinstancestate); } public void onsaveinstancestate(bundle outstate){ gettakephoto().onsaveinstancestate(outstate); } public void onactivityresult(int requestcode, int resultcode, intent data){ gettakephoto().onactivityresult(requestcode, resultcode, data); } public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) { permissionmanager.tpermissiontype type=permissionmanager.onrequestpermissionsresult(requestcode,permissions,grantresults); permissionmanager.handlepermissionsresult(activity,type,invokeparam,this); } /** * * @param result */ @override public void takesuccess(tresult result) { if(listener != null){ listener.takesuccess(result); } // deletecachepic(); } @override public void takefail(tresult result, string msg) { if(listener != null){ listener.takefail(result, msg); } // deletecachepic(); } @override public void takecancel() { if(listener != null){ listener.takecancel(); } } public void deletecachepic(){ file file=new file(environment.getexternalstoragedirectory(), "/takephoto/"); if(!file.exists()) return; file[] files = file.listfiles(); for (file f: files) { f.delete(); } } public interface takephotolistener{ void takesuccess(tresult result); void takefail(tresult result, string msg); void takecancel(); } public takephotolistener listener; public void settakephotolistener(simpletakephotolistener listener){ this.listener = listener; } public static class simpletakephotolistener implements takephotolistener{ @override public void takesuccess(tresult result) { } @override public void takefail(tresult result, string msg) { } @override public void takecancel() { } } @override public permissionmanager.tpermissiontype invoke(invokeparam invokeparam) { permissionmanager.tpermissiontype type=permissionmanager.checkpermission(tcontextwrap.of(activity),invokeparam.getmethod()); if(permissionmanager.tpermissiontype.wait.equals(type)){ this.invokeparam=invokeparam; } return type; } /** * * @param select_type */ public void takephoto(select_type select_type, simpletakephotolistener listener){ takephoto(select_type, null, listener); } public void takephoto(select_type select_type, photoconfigoptions cropoptions, simpletakephotolistener listener){ if (takephoto == null){ toast.maketext(activity, "请先开启照片功能", toast.length_short).show(); return; } settakephotolistener(listener); if(cropoptions == null){ cropoptions = new photoconfigoptions(); } cropoptions.configcompress(); //压缩配置 cropoptions.configtakephoto(); //拍照配置 file file=new file(environment.getexternalstoragedirectory(), "/takephoto/"+system.currenttimemillis() + ".jpg"); if (!file.getparentfile().exists())file.getparentfile().mkdirs(); uri imageuri = uri.fromfile(file); switch (select_type){ case pick_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; case pick_by_take: //拍照获取 if(cropoptions.crop == true){ takephoto.onpickfromcapturewithcrop(imageuri, cropoptions.getcropoptions()); }else { takephoto.onpickfromcapture(imageuri); } break; default: break; } } /** * 图片的裁剪配置选项内部类 */ public class photoconfigoptions{ //裁剪配置 private boolean crop = true; //是否裁剪 private boolean withwoncrop = true; //是否采用自带的裁剪工具,默认选取第三方的裁剪工具 private boolean cropsize = true; //尺寸还是比例 //压缩配置 private boolean useowncompresstool = true; //使用自带的压缩工具 private boolean iscompress = true; //是否压缩 private boolean showprogressbar = true; //显示压缩进度条 // private private int maxsize = 102400; //选择图片配置 private boolean useowngallery = true; //选择使用自带的相册 private boolean choosefromfile = false; //从文件获取图片 private int limit = 1; //选择最多图片的配置,选择多张图片会自动切换到takephoto自带相册 //其它配置 private boolean savepic = true; //选择完之后是否保存图片 private boolean correcttool = false; //纠正拍照的照片旋转角度 private int height = 800; private int width = 800; /** * 裁剪相关配置 * @return */ public cropoptions getcropoptions(){ if(crop == false) return null; cropoptions.builder builder = new cropoptions.builder(); if(cropsize){ builder.setoutputx(width).setoutputy(height); }else { builder.setaspectx(width).setaspecty(height); } builder.setwithowncrop(withwoncrop); //默认采用第三方配置 return builder.create(); } /** * 图片压缩相关配置 */ public void configcompress(){ if(iscompress == false) { takephoto.onenablecompress(null, false); return; } compressconfig config; if(useowncompresstool){ config = new compressconfig.builder() .setmaxsize(maxsize) .setmaxpixel(width>height?width:height) .enablereserveraw(savepic) .create(); }else { lubanoptions options = new lubanoptions.builder() .setmaxheight(height) .setmaxwidth(maxsize) .create(); config = compressconfig.ofluban(options); config.enablereserveraw(savepic); } takephoto.onenablecompress(config, showprogressbar); } public void configtakephoto(){ takephotooptions.builder builder = new takephotooptions.builder(); if(useowngallery){ builder.setwithowngallery(true); } if(correcttool){ builder.setcorrectimage(true); } takephoto.settakephotooptions(builder.create()); } public void setcrop(boolean crop) { this.crop = crop; } public void setwithwoncrop(boolean withwoncrop) { this.withwoncrop = withwoncrop; } public void setcropsize(boolean cropsize) { this.cropsize = cropsize; } public void setuseowncompresstool(boolean useowncompresstool) { this.useowncompresstool = useowncompresstool; } public void setcompress(boolean compress) { iscompress = compress; } public void setshowprogressbar(boolean showprogressbar) { this.showprogressbar = showprogressbar; } public void setmaxsize(int maxsize) { this.maxsize = maxsize; } public void setuseowngallery(boolean useowngallery) { this.useowngallery = useowngallery; } public void setchoosefromfile(boolean choosefromfile) { this.choosefromfile = choosefromfile; } public void setlimit(int limit) { this.limit = limit; } public void setsavepic(boolean savepic) { this.savepic = savepic; } public void setcorrecttool(boolean correcttool) { this.correcttool = correcttool; } public void setheight(int height) { this.height = height; } public void setwidth(int width) { this.width = width; } } /** * 照片获取方式, 从相册获取或拍照处理 */ public enum select_type{ pick_by_select, pick_by_take } }
封装了一个basetakephotoactivity,里面的代码如下:
protected takephotoutil takephotoutil; @override protected void oncreate(@nullable bundle savedinstancestate) { takephotoutil = new takephotoutil(this); if(usetakephoto()){ takephotoutil.oncreate(savedinstancestate); } super.oncreate(savedinstancestate); } @override protected void onsaveinstancestate(bundle outstate) { if(usetakephoto()){ takephotoutil.onsaveinstancestate(outstate); } super.onsaveinstancestate(outstate); } @override protected void onactivityresult(int requestcode, int resultcode, intent data) { if(usetakephoto()){ takephotoutil.onactivityresult(requestcode, resultcode, data); } super.onactivityresult(requestcode, resultcode, data); } @override public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) { if(usetakephoto()){ takephotoutil.onrequestpermissionsresult(requestcode, permissions, grantresults); } super.onrequestpermissionsresult(requestcode, permissions, grantresults); } protected boolean usetakephoto(){ return false; }
其他对于业务的封装,可以再封装一个baseactivity,继承自basetakephotoactivity,这样就可以不影响baseactivity的使用,如果我们在主activity中使用获取图片的功能需要两步
1)开启takephoto功能
@override protected boolean usetakephoto() { return true; }
2 ) 获取图片
takephotoutil.takephoto(takephotoutil.select_type.pick_by_take, new takephotoutil.simpletakephotolistener(){ @override public void takesuccess(tresult result) { string s = result.getimage().getcompresspath(); bitmap bitmap = bitmapfactory.decodefile(s); iv.setimagebitmap(bitmap); } });
takephoto()的第一个参数是一个枚举类型的参数,分别为从相册获取和拍照获取,第二个参数为获取成功失败监听,有三个回调,由于有些回调不是必须的,所以对listener做了一个适配,只需要回调想要的方法即可,获取成功之后就可以通过tresult封装的参数获取想要的图片以及图片地址。对于获取到的图片地址就可以做一些上传处理。
图片上传
可以借助okhttp3实现上传功能
multipartbody.builder builder = new multipartbody.builder().settype(multipartbody.form); requestbody requestbody = requestbody.create(mediatype.parse(multipart_form_data), file); multipartbody.part part = multipartbody.part.createformdata("dir", file.getname(), requestbody); builder.addpart(part); request.builder builder1 = new request.builder().url(url).post(builder.build()); request request = builder1.build(); httputils.client.newcall(request).enqueue(new callback() { @override public void onfailure(call call, ioexception e) { } @override public void onresponse(call call, response response) throws ioexception { if(response.issuccessful()){ final string s = response.body().string(); ((activity)context).runonuithread(new runnable() { @override public void run() { } }); } } });
大致代码如上
最后
由于当时没有找到这个库,于是跑去问公司另一个做android的,看了下他封装的代码,确实也是值得学习的,他的代码也是适配到了android7.0,贴下它的代码,方便以后学习:
public class camerautil { private static final int request_capture_camera = 1221; private static final int request_crop = 1222; private static final int request_open_album = 1223; private static final string tag = "camera"; private static uri mcacheuri; private camerautil() { } @requirespermission(allof = {manifest.permission.read_external_storage,manifest.permission.write_external_storage, manifest.permission.camera}) public static void getimagefromcamera(activity activity) { 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 public static void getimagefromcamera(fragment fragment) { if (checkexternalstoragestate(fragment.getcontext())) { fragment.startactivityforresult(getimagefromcamera(fragment.getcontext()), request_capture_camera); } } private static intent getimagefromcamera(context context) { intent getimagebycamera = new intent(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); return getimagebycamera; } private static boolean checkexternalstoragestate(context context) { if (textutils.equals(environment.getexternalstoragestate(), environment.media_mounted)) { return true; } toast.maketext(context.getapplicationcontext(), "请确认已经插入sd卡", toast.length_long).show(); return false; } @suppresswarnings("resultofmethodcallignored") public static file getcachephotofile() { file file = new file(environment.getexternalstoragedirectory(), "/lenso/cache/cameratakephoto" + system.currenttimemillis() + ".jpg"); if (!file.getparentfile().exists()) file.getparentfile().mkdirs(); return file; } private static uri getcachephotouri(context context) { return fileprovider.geturiforfile(context, getauthority(context), getcachephotofile()); } private static uri getcachephotouri(context context, file file) { return fileprovider.geturiforfile(context, getauthority(context), file); } public static void onactivityresult(activity activity, int requestcode, int resultcode, intent data, onactivityresultlistener listener) { onactivityresult(activity, null, requestcode, resultcode, data, listener); } /** * getcachephotofile().getparentfile().getabsolutepath() * @param dir * @return */ public static boolean deletedir(file dir) { if (dir != null && dir.isdirectory()) { string[] children = dir.list(); for (int i = 0; i < children.length; i++) { boolean success = deletedir(new file(dir, children[i])); if (!success) { return false; } } } return dir.delete(); } public static file savebitmap(bitmap bitmap) { file file = getcachephotofile(); if (bitmap == null || bitmap.isrecycled()) return file; fileoutputstream outputstream = null; try { outputstream = new fileoutputstream(file); bitmap.compress(bitmap.compressformat.jpeg, 100, outputstream); } catch (filenotfoundexception e) { e.printstacktrace(); } finally { if (outputstream != null) try { outputstream.close(); } catch (ioexception e) { e.printstacktrace(); } bitmap.recycle(); } return file; } public static void copy(file file, file point) { if (!file.exists()) return; if (!point.getparentfile().exists()) point.getparentfile().mkdirs(); bufferedinputstream inputstream = null; bufferedoutputstream outputstream = null; try { inputstream = new bufferedinputstream(new fileinputstream(file)); outputstream = new bufferedoutputstream(new fileoutputstream(point)); byte[] buff = new byte[1024 * 1024 * 2]; int len; while ((len = inputstream.read(buff)) != -1) { outputstream.write(buff, 0, len); outputstream.flush(); } } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { closestream(inputstream); closestream(outputstream); } } private static void closestream(closeable closeable) { if (closeable != null) try { closeable.close(); } catch (ioexception e) { e.printstacktrace(); } } public static void onactivityresult(activity activity, cropoption crop, int requestcode, int resultcode, intent data, onactivityresultlistener listener) { if (resultcode == activity.result_canceled) return; uri uri; switch (requestcode) { case request_open_album: uri = data.getdata(); if (uri != null) { mcacheuri = getcachephotouri(activity); copy(new file(getrealfilepath(activity, uri)), new file(getrealfilepath(activity, mcacheuri))); } else { bitmap bitmap = data.getparcelableextra("data"); file file = savebitmap(bitmap); mcacheuri = getcachephotouri(activity, file); } case request_capture_camera: uri = mcacheuri; if (listener != null) { listener.requestcapturecamera(getrealfilepath(activity, uri), null); } if (crop == null) return; crop.setsource(uri); intent intent = crop.create(); granturipermission(activity, intent, crop.getoutput()); activity.startactivityforresult(intent, request_crop); break; case request_crop: if (listener != null && data != null) { listener.requestcrop(getrealfilepath(activity, mcacheuri), (bitmap) data.getparcelableextra("data")); } break; } } @requirespermission(allof = {manifest.permission.read_external_storage,manifest.permission.write_external_storage}) public static void getimagefromalbum(activity activity) { intent intent = new intent(intent.action_pick); intent.settype("image/*");//相片类型 activity.startactivityforresult(intent, request_open_album); } @requirespermission(allof = {manifest.permission.read_external_storage,manifest.permission.write_external_storage}) @deprecated public static void getimagefromalbum(fragment fragment) { intent intent = new intent(intent.action_pick); intent.settype("image/*");//相片类型 fragment.startactivityforresult(intent, request_open_album); } public interface onactivityresultlistener { void requestcapturecamera(string path, bitmap bitmap); void requestcrop(string path, bitmap bitmap); } /** * try to return the absolute file path from the given uri * * @param context context * @param uri uri * @return the file path or null */ public static string getrealfilepath(final context context, final uri uri) { if (null == uri) return null; string path = uri.tostring(); if (path.startswith("content://" + getauthority(context) + "/rc_external_path")) { return path.replace("content://" + getauthority(context) + "/rc_external_path", environment.getexternalstoragedirectory().getabsolutepath()); } final string scheme = uri.getscheme(); string data = null; if (scheme == null) data = uri.getpath(); else if (contentresolver.scheme_file.equals(scheme)) { data = uri.getpath(); } else if (contentresolver.scheme_content.equals(scheme)) { cursor cursor = context.getcontentresolver().query(uri, new string[]{mediastore.images.imagecolumns.data}, null, null, null); if (null != cursor) { if (cursor.movetofirst()) { int index = cursor.getcolumnindex(mediastore.images.imagecolumns.data); if (index > -1) { data = cursor.getstring(index); } } cursor.close(); } } return data; } private static string getauthority(context context) { return context.getpackagename() + ".fileprovider"; } public static class cropoption { private int aspectx=1;//x比例 private int aspecty=1;//y比例 private boolean returndata = false;//是返回bitmap,否返回uri private string outputformat;//输出流保存格式jpg png ... private int outputx=200;//返回的bitmap宽 private int outputy=200;//返回的bitmap高 private uri output;//输出流保存路径 private uri source;//需要截图的图片uri private boolean nofacedetection = true;//是否关闭人脸识别功能 // get和set方法省略 private intent create() { if (source == null) throw new nullpointerexception("没有设置图片uri"); intent intent = new intent("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); } return intent; } } private static void granturipermission(context context, intent intent, uri uri) { list<resolveinfo> resinfolist = context.getpackagemanager().queryintentactivities(intent, packagemanager.match_default_only); for (resolveinfo resolveinfo : resinfolist) { string packagename = resolveinfo.activityinfo.packagename; context.granturipermission(packagename, uri, intent.flag_grant_write_uri_permission | intent.flag_grant_read_uri_permission); } } } //xml文件部分 <?xml version="1.0" encoding="utf-8"?> <resources > <paths> <external-path path="" name="rc_external_path" /> </paths> </resources> //清单文件注册部分 <provider android:name="android.support.v4.content.fileprovider" android:authorities="com.lenso.fileprovider" android:exported="false" android:granturipermissions="true"> <meta-data android:name="android.support.file_provider_paths" android:resource="@xml/file_path" /> </provider>
也封装了从本地获取,以及拍照获取的相关功能,可以值得学习,毕竟不少坑。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: C#虚函数用法实例分析