Android實現(xiàn)長按圖片保存至相冊功能
前言:前面寫了一篇reactnative的學(xué)習(xí)筆記,說reactnative的Android框架中有很多福利,確實是的,也說到了我們app中的一個把圖片保存到相冊的功能,好吧,還是準(zhǔn)備寫一篇博客,就當(dāng)筆記了~
先上幾張app的圖片:
一進app就是一個進度條加載圖片(我待會也會說一下進度條view跟怎么監(jiān)聽圖片加載過程):

圖片加載完畢后:

長按圖片進入相冊可以看到我們保存的圖片:

監(jiān)聽圖片加載的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());
}
}
實現(xiàn)起來還是很簡單的,就是動態(tài)改變兩端弧的起點和終點,通過屬性動畫不斷的在(0-360)循環(huán),代碼應(yīng)該還算比較清晰哈!~~~~
圖片加載用了一個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","內(nèi)存卡不存在");
return;
}
Log.e("TAG","開始保存圖片至內(nèi)存卡~~");
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","圖片已經(jīng)保存至內(nèi)存卡~~");
}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();
}
}
}
加載完畢后把圖片存放在了內(nèi)存卡中(當(dāng)然,這是我隨便寫的一個圖片加載,大家換成自己的加載框架哈,):
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","內(nèi)存卡不存在");
return;
}
Log.e("TAG","開始保存圖片至內(nèi)存卡~~");
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","圖片已經(jīng)保存至內(nèi)存卡~~");
}catch (Exception e){
e.printStackTrace();
}finally {
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}.start();
}
這里我們是需要把圖片保存到內(nèi)存卡中,所以考慮到了android 6.0的運行時權(quán)限,所以小伙伴們也一定要判斷哦,我在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,結(jié)果android原生又生疏了,小伙伴們?nèi)绻蚕裎乙粯拥脑?,一定要常練?xí)哦,兩個東西都是需要常敲的那種,不然又忘記了?。?!
最后附上demo的git鏈接:
https://github.com/913453448/CamerRoll
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android圖片添加水印圖片并把圖片保存到文件存儲的實現(xiàn)代碼
- Android人臉識別Demo豎屏YUV方向調(diào)整和圖片保存(分享)
- Android長按imageview把圖片保存到本地的實例代碼
- android中Glide實現(xiàn)加載圖片保存至本地并加載回調(diào)監(jiān)聽
- Android 實現(xiàn)WebView點擊圖片查看大圖列表及圖片保存功能
- Android 自定義View手寫簽名并保存圖片功能
- Android使用webView長按保存下載網(wǎng)絡(luò)圖片
- Android保存多張圖片到本地的實現(xiàn)方法
- Android開發(fā)實現(xiàn)的保存圖片到相冊功能示例
- Android開發(fā)實現(xiàn)保存圖片到手機相冊功能
- Android 圖片保存到相冊不顯示的解決方案(兼容Android 10及更高版本)
相關(guān)文章
Android Studio下添加assets目錄的實現(xiàn)方法
下面小編就為大家?guī)硪黄狝ndroid Studio下添加assets目錄的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03
Android PopWindow 設(shè)置背景亮度的實例
這篇文章主要介紹了Android PopWindow 設(shè)置背景亮度的實例的相關(guān)資料,這里提供實現(xiàn)方法,希望能幫助有所需要的朋友,需要的朋友可以參考下2017-08-08
Kotlin 協(xié)程 supervisorScope {} 運行崩潰解決方法
看過很多?supervisorScope {}?文檔的使用,我照抄一摸一樣的代碼,運行就崩潰,最后找到了解決方法,應(yīng)該是kotlin版本更新做過改動,當(dāng)前我使用的是?androidx.core:core-ktx:1.9.0,本文給大家介紹Kotlin 協(xié)程 supervisorScope {} 運行崩潰解決方法,感興趣的朋友一起看看吧2024-01-01
Kotlin語言中CompileSdkVersion與targetSdkVersion的區(qū)別淺析
這篇文章主要介紹了Kotlin語言中CompileSdkVersion和targetSdkVersion有什么區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-02-02
Android開發(fā)-之環(huán)境的搭建(圖文詳解)
這篇文章主要介紹了Android開發(fā)-之環(huán)境的搭建(圖文詳解),具有一定的參考價值,有興趣的可以了解一下。2016-11-11

