前言:前面写了一篇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;
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);
}
}
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{ 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;
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
public abstract class GuardedAsyncTask
extends AsyncTask {
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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。



