Android实现长按图片保存至相册功能
程序员文章站
2023-12-16 20:53:22
前言:前面写了一篇reactnative的学习笔记,说reactnative的android框架中有很多福利,确实是的,也说到了我们app中的一个把图片保存到相册的功能,好...
前言:前面写了一篇reactnative的学习笔记,说reactnative的android框架中有很多福利,确实是的,也说到了我们app中的一个把图片保存到相册的功能,好吧,还是准备写一篇博客,就当笔记了~
先上几张app的图片:
一进app就是一个进度条加载图片(我待会也会说一下进度条view跟怎么监听图片加载过程):
图片加载完毕后:
长按图片进入相册可以看到我们保存的图片:
监听图片加载的loaddingview源码(不是很难,我就直接贴代码了):
package com.leo.camerroll; import android.animation.valueanimator; import android.content.context; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.graphics.rectf; import android.util.attributeset; import android.util.typedvalue; import android.view.view; import android.view.animation.animation; import android.view.animation.linearinterpolator; import android.widget.progressbar; /** * created by leo on 17/1/22. */ public class loadingview extends progressbar { private final int default_radius = dp2px(15); private final int default_reach_color = 0xffffffff; private final int default_unreach_color = 0x88000000; private final long anim_duration = 1000; private final string base_text = "00%"; private boolean isstop; private int mradius = default_radius; private int mstrokewidth; private paint reachpaint; private paint unreachpaint; private paint textpaint; private paint bgpaint; private int mstartangle=0; private float msweepangle=360*0.382f; private valueanimator anim; public loadingview(context context) { this(context, null); } public loadingview(context context, attributeset attrs) { this(context, attrs, 0); } public loadingview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); initview(); } private void initview() { reachpaint = new paint(paint.anti_alias_flag | paint.dither_flag); reachpaint.setstrokecap(paint.cap.round); reachpaint.setstyle(paint.style.stroke); unreachpaint = new paint(reachpaint); reachpaint.setcolor(default_reach_color); unreachpaint.setcolor(default_unreach_color); textpaint = new paint(paint.anti_alias_flag | paint.dither_flag); textpaint.setstyle(paint.style.stroke); textpaint.setcolor(color.white); textpaint.setfakeboldtext(true); bgpaint=new paint(paint.anti_alias_flag|paint.dither_flag); bgpaint.setstrokecap(paint.cap.round); bgpaint.setcolor(color.argb(44,0,0,0)); setmax(100); } @override protected synchronized void onmeasure(int widthmeasurespec, int heightmeasurespec) { int defwidth = resolvesize(getsuggestedminimumwidth(), widthmeasurespec); int defheight = resolvesize(getsuggestedminimumheight(), heightmeasurespec); int expectsize = math.min(defheight, defwidth); if (expectsize <= 0) { expectsize = mradius * 2; } else { mradius = expectsize / 2; } mstrokewidth = mradius / 5; reachpaint.setstrokewidth(mstrokewidth); unreachpaint.setstrokewidth(mstrokewidth); setmeasureddimension(expectsize, expectsize); float textsize=0; while(true){ textsize+=0.1; textpaint.settextsize(textsize); if(textpaint.measuretext(base_text,0,base_text.length())>=mradius){ break; } } } @override protected synchronized void ondraw(canvas canvas) { if(isstop){ setvisibility(view.gone); return; } //drawbackground transparent canvas.drawcircle(getwidth()/2,getwidth()/2,mradius-mstrokewidth,bgpaint); //draw reach drawprogressreach(canvas); //draw progress text drawprogresstext(canvas); } private void drawprogresstext(canvas canvas) { string text=string.valueof((int)(getprogress()*1.0f/getmax()*100))+"%"; int centerx=getwidth()/2; int centery=getwidth()/2; int basex= (int) (centerx-textpaint.measuretext(text,0,text.length())/2); int basey= (int) (centery-(textpaint.getfontmetrics().ascent+textpaint.getfontmetrics().descent)/2); canvas.drawtext(text,basex,basey,textpaint); } private void drawprogressreach(canvas canvas) { canvas.drawarc(new rectf(0 + mstrokewidth / 2, 0 + mstrokewidth / 2, mradius * 2 - mstrokewidth / 2, mradius * 2 - mstrokewidth / 2), mstartangle, msweepangle, false, reachpaint); //drawonreach canvas.drawarc(new rectf(0 + mstrokewidth / 2, 0 + mstrokewidth / 2, mradius * 2 - mstrokewidth / 2, mradius * 2 - mstrokewidth / 2), mstartangle+msweepangle, 360-msweepangle,false, unreachpaint); } @override protected void onattachedtowindow() { super.onattachedtowindow(); if(anim==null){ anim=valueanimator.ofint(0,360); anim.setinterpolator(new linearinterpolator()); anim.setduration(anim_duration); anim.setrepeatcount(animation.infinite); anim.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { if(animation!=null&&animation.getanimatedvalue()!=null){ int startangle= (int) animation.getanimatedvalue(); mstartangle=startangle; postinvalidate(); } } }); }else{ anim.cancel(); anim.removeallupdatelisteners(); } anim.start(); } public void loadcompleted() { isstop=true; if(anim!=null){ anim.cancel(); anim.removeallupdatelisteners(); this.setvisibility(view.gone); } } /** * @param size * @return px */ private int dp2px(int size) { return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, size, getcontext().getresources().getdisplaymetrics()); } }
实现起来还是很简单的,就是动态改变两端弧的起点和终点,通过属性动画不断的在(0-360)循环,代码应该还算比较清晰哈!~~~~
图片加载用了一个asynctask:
private class downimagetask extends asynctask<string, long, bitmap> { private imageview imageview; private long contentlength; public downimagetask(imageview imageview) { this.imageview = imageview; } @override protected bitmap doinbackground(string... params) { bitmap bitmap = null; bufferedinputstream bis = null; bytearrayoutputstream bos = null; try { file filedir=new file(getapplication().getexternalcachedir(),"images"); if(filedir==null||!filedir.isdirectory()){ filedir.mkdir(); } file file=new file(filedir.getabsolutepath()+"/"+params[0].hashcode()+".png"); if(file!=null&&file.length()>0){ return bitmap=bitmapfactory.decodefile(file.getabsolutepath()); } bos=new bytearrayoutputstream(); byte[] buffer = new byte[512]; long total=0; int len ; url url = new url(params[0]); httpurlconnection conn = (httpurlconnection) url.openconnection(); this.contentlength = conn.getcontentlength(); bis = new bufferedinputstream(conn.getinputstream()); while ((len = bis.read(buffer)) != -1) { total+=len; publishprogress(total); thread.sleep(100); bos.write(buffer, 0, len); bos.flush(); } bitmap= bitmapfactory.decodebytearray(bos.tobytearray(),0,bos.tobytearray().length); savebitmaptodisk(bos,params[0]); } catch (exception e) { e.printstacktrace(); } finally { try { if (bis != null) { bis.close(); } if (bos != null) { bos.close(); } } catch (ioexception e) { e.printstacktrace(); } } return bitmap; } private void savebitmaptodisk(final bytearrayoutputstream baos, final string url) { new thread(){ @override public void run() {bufferedoutputstream bos=null; try{ if(!environment.media_mounted.equals(environment.getexternalstoragestate())){ log.e("tag","内存卡不存在"); return; } log.e("tag","开始保存图片至内存卡~~"); byte[] bytes = baos.tobytearray(); file filedir=new file(getapplication().getexternalcachedir(),"images"); if(filedir==null||!filedir.isdirectory()){ filedir.mkdir(); } file file=new file(filedir.getabsolutepath()+"/"+url.hashcode()+".png"); file.createnewfile(); bos=new bufferedoutputstream(new fileoutputstream(file)); bos.write(bytes); bos.flush(); log.e("tag","图片已经保存至内存卡~~"); }catch (exception e){ e.printstacktrace(); }finally { if(bos!=null){ try { bos.close(); } catch (ioexception e) { e.printstacktrace(); } } } } }.start(); } @override protected void onprogressupdate(long... values) { mloadingview.setprogress((int) ((values[0].longvalue() * 1.0f / contentlength) * 100)); } @override protected void onpostexecute(bitmap bitmap) { if (imageview != null && bitmap != null) { imageview.setimagebitmap(bitmap); mloadingview.loadcompleted(); } } }
加载完毕后把图片存放在了内存卡中(当然,这是我随便写的一个图片加载,大家换成自己的加载框架哈,):
private void savebitmaptodisk(final bytearrayoutputstream baos, final string url) { new thread(){ @override public void run() {bufferedoutputstream bos=null; try{ if(!environment.media_mounted.equals(environment.getexternalstoragestate())){ log.e("tag","内存卡不存在"); return; } log.e("tag","开始保存图片至内存卡~~"); byte[] bytes = baos.tobytearray(); file filedir=new file(getapplication().getexternalcachedir(),"images"); if(filedir==null||!filedir.isdirectory()){ filedir.mkdir(); } file file=new file(filedir.getabsolutepath()+"/"+url.hashcode()+".png"); file.createnewfile(); bos=new bufferedoutputstream(new fileoutputstream(file)); bos.write(bytes); bos.flush(); log.e("tag","图片已经保存至内存卡~~"); }catch (exception e){ e.printstacktrace(); }finally { if(bos!=null){ try { bos.close(); } catch (ioexception e) { e.printstacktrace(); } } } } }.start(); }
这里我们是需要把图片保存到内存卡中,所以考虑到了android 6.0的运行时权限,所以小伙伴们也一定要判断哦,我在oncreate的时候就判断了:
if (build.version.sdk_int >= build.version_codes.m) { if (contextcompat.checkselfpermission(this, manifest.permission.write_external_storage) != packagemanager.permission_granted) { requestalertwindowpermission(); } }
private static final int request_code = 1; private void requestalertwindowpermission() { activitycompat.requestpermissions(this,new string[]{manifest.permission.write_external_storage},request_code); }
下面就是讲长按图片保存至相册了:
mimageview.setonlongclicklistener(new view.onlongclicklistener(){ @override public boolean onlongclick(view v) { if(mimageview.getdrawable() instanceof bitmapdrawable){ toast.maketext(getapplicationcontext(),"长按保存图片至相册",toast.length_short).show(); file filedir=new file(getapplication().getexternalcachedir(),"images"); file file=new file(filedir.getabsolutepath()+"/"+image_url.hashcode()+".png"); if(file!=null&&file.length()>0){ camerarollmanager rollmanager=new camerarollmanager(mainactivity.this, uri.parse(file.getabsolutepath())); rollmanager.execute(); } } return false; } });
camerarollmanager是我直接copy的reactnatvie中的android模块的代码:
camerarollmanager.java
package com.leo.camerroll.camera; import android.content.context; import android.content.intent; import android.media.mediascannerconnection; import android.net.uri; import android.os.environment; import android.os.handler; import android.os.looper; import android.os.message; import android.widget.toast; import java.io.file; import java.io.fileinputstream; import java.io.fileoutputstream; import java.io.ioexception; import java.nio.channels.filechannel; /** * created by leo on 17/1/22. */ public class camerarollmanager extends guardedasynctask{ private static context mcontext; private final uri muri; private static handler handler=new handler(looper.getmainlooper()){ @override public void handlemessage(message msg) { toast.maketext(mcontext,"保存成功!",toast.length_short).show(); intent intent = new intent(); intent.settype("image/*"); intent.setaction(intent.action_get_content); mcontext.startactivity(intent); } }; public camerarollmanager(context context, uri uri) { super(context); mcontext = context; muri = uri; } @override protected void doinbackgroundguarded(object[] params) { file source = new file(muri.getpath()); filechannel input = null, output = null; try { file exportdir = environment.getexternalstoragepublicdirectory(environment.directory_pictures); exportdir.mkdirs(); if (!exportdir.isdirectory()) { return; } file dest = new file(exportdir, source.getname()); int n = 0; string fullsourcename = source.getname(); string sourcename, sourceext; if (fullsourcename.indexof('.') >= 0) { sourcename = fullsourcename.substring(0, fullsourcename.lastindexof('.')); sourceext = fullsourcename.substring(fullsourcename.lastindexof('.')); } else { sourcename = fullsourcename; sourceext = ""; } while (!dest.createnewfile()) { dest = new file(exportdir, sourcename + "_" + (n++) + sourceext); } input = new fileinputstream(source).getchannel(); output = new fileoutputstream(dest).getchannel(); output.transferfrom(input, 0, input.size()); input.close(); output.close(); mediascannerconnection.scanfile( mcontext, new string[]{dest.getabsolutepath()}, null, new mediascannerconnection.onscancompletedlistener() { @override public void onscancompleted(string path, uri uri) { handler.sendemptymessage(0); } }); } catch (ioexception e) { } finally { if (input != null && input.isopen()) { try { input.close(); } catch (ioexception e) { } } if (output != null && output.isopen()) { try { output.close(); } catch (ioexception e) { } } } } }
guardedasynctask.java:
package com.leo.camerroll.camera; import android.content.context; import android.os.asynctask; /** * created by leo on 17/1/22. */ public abstract class guardedasynctask <params, progress> extends asynctask<params, progress, void> { private final context mreactcontext; protected guardedasynctask(context reactcontext) { mreactcontext = reactcontext; } @override protected final void doinbackground(params... params) { try { doinbackgroundguarded(params); } catch (runtimeexception e) { } return null; } protected abstract void doinbackgroundguarded(params... params); }
好啦!!! 看着简单哈,花了我一个上午的时间,还是自己不熟练的原因额,感觉高了一段时间rn,结果android原生又生疏了,小伙伴们如果也像我一样的话,一定要常练习哦,两个东西都是需要常敲的那种,不然又忘记了!!!
最后附上demo的git链接:
https://github.com/913453448/camerroll
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。