首页 > 系统 > Android > 正文

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

2019-12-12 03:32:47
字体:
来源:转载
供稿:网友

前言:前面写了一篇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

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表