欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现长按图片保存至相册功能

程序员文章站 2023-12-16 20:53:22
前言:前面写了一篇reactnative的学习笔记,说reactnative的android框架中有很多福利,确实是的,也说到了我们app中的一个把图片保存到相册的功能,好...

前言:前面写了一篇reactnative的学习笔记,说reactnative的android框架中有很多福利,确实是的,也说到了我们app中的一个把图片保存到相册的功能,好吧,还是准备写一篇博客,就当笔记了~

先上几张app的图片:

一进app就是一个进度条加载图片(我待会也会说一下进度条view跟怎么监听图片加载过程):

Android实现长按图片保存至相册功能

图片加载完毕后:

Android实现长按图片保存至相册功能

长按图片进入相册可以看到我们保存的图片:

Android实现长按图片保存至相册功能

监听图片加载的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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: