SubsamplingScaleImageView用于加载Android中长图,大图的展示,它是一个subsampling-scale-image-view库。
第一步:加入依赖
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
第二步:使用
它不同于imageview可以使用Glide加载图片,需要通过FileTarget作为桥梁。
Glide.with(mContext).downloadOnly().load(imageInfos.get(position)).addListener(new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e,Object model,Target target,
boolean isFirstResource) {
return true;
}
@Override
public boolean onResourceReady(File resource,Object model,Target target,DataSource dataSource,
boolean isFirstResource) {
holder.photoView.setVisibility(View.GONE);
holder.dynamicDetialPreviewBg.setVisibility(View.VISIBLE);
holder.dynamicDetialPreviewImage.setVisibility(View.VISIBLE);
getSupportPresenter().loadSuccess(mContext, resource,holder.dynamicDetialPreviewImage,holder.dynamicDetialPreviewBg);
return true;
}
}).into(new FileTarget() {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
}
});
第三步,使用到的几个重要方法及工具类
方法:
public void loadSuccess(Context context,File resource,SubsamplingScaleImageView imageView,RelativeLayout progressBar) {
String imagePath = resource.getAbsolutePath();
imageView.setVisibility(View.VISIBLE);
setImageSpec(context,imagePath, imageView);
imageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_USE_EXIF);
ImageSource imageSource =ImageSource.uri(Uri.fromFile(new File(imagePath)));
if (ImageUtil.isBmpImageWithMime(imagePath)) {
imageSource.tilingDisabled();
}
imageView.setImage(imageSource);
imageView.setOnImageEventListener(new SubsamplingScaleImageView.OnImageEventListener() {
@Override
public void onReady() {
progressBar.setVisibility(View.GONE);
}
@Override
public void onImageLoaded() {
}
@Override
public void onPreviewLoadError(Exception e) {
}
@Override
public void onImageLoadError(Exception e) {
}
@Override
public void onTileLoadError(Exception e) {
}
@Override
public void onPreviewReleased() {
}
});
}
private void setImageSpec(Context context,String imagePath,SubsamplingScaleImageView imageView) {
boolean isLongImage =ImageUtil.isLongImage(context, imagePath);
if (isLongImage) {
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_START);
imageView.setMinScale(ImageUtil.getLongImageMinScale(context, imagePath));
imageView.setMaxScale(ImageUtil.getLongImageMaxScale(context, imagePath));
imageView.setDoubleTapZoomScale(ImageUtil.getLongImageMaxScale(context, imagePath));
}else {
boolean isSmallImage =ImageUtil.isSmallImage(context, imagePath);
boolean isWideImage =ImageUtil.isWideImage(context, imagePath);
if (isWideImage) {
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
imageView.setMinScale(ImagePreview.getInstance().getMinScale());
imageView.setMaxScale(ImagePreview.getInstance().getMaxScale());
imageView.setDoubleTapZoomScale(ImageUtil.getWideImageDoubleScale(activity, imagePath));
}else if (isSmallImage) {
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM);
imageView.setMinScale(ImageUtil.getSmallImageMinScale(activity, imagePath));
imageView.setMaxScale(ImageUtil.getSmallImageMaxScale(activity, imagePath));
imageView.setDoubleTapZoomScale(ImageUtil.getSmallImageMaxScale(activity, imagePath));
}else {
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
imageView.setMinScale(ImagePreview.getInstance().getMinScale());
imageView.setMaxScale(ImagePreview.getInstance().getMaxScale());
imageView.setDoubleTapZoomScale(ImagePreview.getInstance().getMediumScale());
}
}
}
工具类:FileTarget
package com.astro90.android.app.dynamicpreview;
import android.graphics.drawable.Drawable;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.target.SizeReadyCallback;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import java.io.File;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class FileTarget implements Target {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
}
@Override
public void onResourceReady(@NonNull File resource,@Nullable Transition transition) {
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
@Override
public void getSize(@NonNull SizeReadyCallback cb) {
cb.onSizeReady(Target.SIZE_ORIGINAL,Target.SIZE_ORIGINAL);
}
@Override
public void removeCallback(@NonNull SizeReadyCallback cb) {
}
@Nullable
@Override
public Request getRequest() {
return null;
}
@Override
public void setRequest(@Nullable Request request) {
}
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
}
ImagePreview
package com.astro90.android.app.dynamicpreview;
public class ImagePreview {
private float minScale =1.0f;// 最小缩放倍数
private float mediumScale =3.0f;// 中等缩放倍数
private float maxScale =5.0f;// 最大缩放倍数
public static ImagePreview getInstance() {
return InnerClass.instance;
}
@Deprecated
public ImagePreview setScaleLevel(int min,int medium,int max) {
if (max > medium && medium > min && min >0) {
this.minScale = min;
this.mediumScale = medium;
this.maxScale = max;
}else {
throw new IllegalArgumentException("max must greater to medium, medium must greater to min!");
}
return this;
}
public float getMinScale() {
return minScale;
}
public float getMediumScale() {
return mediumScale;
}
public float getMaxScale() {
return maxScale;
}
private static class InnerClass {
private static ImagePreview instance =new ImagePreview();
}
}
ImageUtil
package com.astro90.android.app.dynamicpreview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Environment;
import android.text.TextUtils;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class ImageUtil {
private static final String TAG ="ImageUtil";
public static Bitmap getImageBitmap(String srcPath,float maxWidth,float maxHeight) {
BitmapFactory.Options newOpts =new BitmapFactory.Options();
newOpts.inJustDecodeBounds =true;
Bitmap bitmap =BitmapFactory.decodeFile(srcPath,newOpts);
newOpts.inJustDecodeBounds =false;
int originalWidth =newOpts.outWidth;
int originalHeight =newOpts.outHeight;
float be =1;
if (originalWidth >originalHeight &&originalWidth > maxWidth) {
be =originalWidth / maxWidth;
}else if (originalWidth maxHeight) {
be =newOpts.outHeight / maxHeight;
}
if (be <=0) {
be =1;
}
newOpts.inSampleSize = (int) be;
newOpts.inPreferredConfig =Bitmap.Config.ARGB_8888;
newOpts.inDither =false;
newOpts.inPurgeable =true;
newOpts.inInputShareable =true;
if (bitmap !=null && !bitmap.isRecycled()) {
bitmap.recycle();
}
try {
bitmap =BitmapFactory.decodeFile(srcPath,newOpts);
}catch (OutOfMemoryError e) {
if (bitmap !=null && !bitmap.isRecycled()) {
bitmap.recycle();
}
Runtime.getRuntime().gc();
}catch (Exception e) {
Runtime.getRuntime().gc();
}
if (bitmap !=null) {
bitmap =rotateBitmapByDegree(bitmap,getBitmapDegree(srcPath));
}
return bitmap;
}
public static int getBitmapDegree(String path) {
int degree =0;
try {
ExifInterface exifInterface =new ExifInterface(path);
int orientation =
exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree =90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree =180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree =270;
break;
default:
degree =0;
break;
}
}catch (IOException e) {
e.printStackTrace();
}
return degree;
}
public static Bitmap rotateBitmapByDegree(Bitmap bm,int degree) {
Bitmap returnBm =null;
Matrix matrix =new Matrix();
matrix.postRotate(degree);
try {
returnBm =Bitmap.createBitmap(bm,0,0, bm.getWidth(), bm.getHeight(),matrix,true);
}catch (OutOfMemoryError e) {
e.printStackTrace();
}
if (returnBm ==null) {
returnBm = bm;
}
if (bm != returnBm) {
bm.recycle();
}
return returnBm;
}
public static Bitmap resizeImage(Bitmap origin,int newWidth,int newHeight) {
if (origin ==null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidth) /width;
float scaleHeight = ((float) newHeight) /height;
Matrix matrix =new Matrix();
matrix.postScale(scaleWidth,scaleHeight);
Bitmap newBM =Bitmap.createBitmap(origin,0,0,width,height,matrix,false);
if (!origin.isRecycled()) {
origin.recycle();
}
return newBM;
}
public static String saveBitmapBackPath(Bitmap bm)throws IOException {
String path =Environment.getExternalStorageDirectory() +"/ShareLongPicture/.temp/";
File targetDir =new File(path);
if (!targetDir.exists()) {
try {
targetDir.mkdirs();
}catch (Exception e) {
e.printStackTrace();
}
}
String fileName ="temp_LongPictureShare_" +System.currentTimeMillis() +".jpeg";
File savedFile =new File(path +fileName);
if (!savedFile.exists()) {
savedFile.createNewFile();
}
BufferedOutputStream bos =new BufferedOutputStream(new FileOutputStream(savedFile));
bm.compress(Bitmap.CompressFormat.JPEG,100,bos);
bos.flush();
bos.close();
return savedFile.getAbsolutePath();
}
public static int getOrientation(String imagePath) {
int degree =0;
try {
ExifInterface exifInterface =new ExifInterface(imagePath);
int orientation =
exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return 0;
}
}catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static int[]getWidthHeight(String imagePath) {
if (imagePath.isEmpty()) {
return new int[]{0,0};
}
BitmapFactory.Options options =new BitmapFactory.Options();
options.inJustDecodeBounds =true;
try {
Bitmap originBitmap =BitmapFactory.decodeFile(imagePath,options);
}catch (Exception e) {
e.printStackTrace();
}
// 使用第一种方式获取原始图片的宽高
int srcWidth =options.outWidth;
int srcHeight =options.outHeight;
// 使用第二种方式获取原始图片的宽高
if (srcHeight == -1 || srcWidth == -1) {
try {
ExifInterface exifInterface =new ExifInterface(imagePath);
srcHeight =
exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH,ExifInterface.ORIENTATION_NORMAL);
srcWidth =
exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH,ExifInterface.ORIENTATION_NORMAL);
}catch (IOException e) {
e.printStackTrace();
}
}
// 使用第三种方式获取原始图片的宽高
if (srcWidth <=0 || srcHeight <=0) {
Bitmap bitmap2 =BitmapFactory.decodeFile(imagePath);
if (bitmap2 !=null) {
srcWidth =bitmap2.getWidth();
srcHeight =bitmap2.getHeight();
try {
if (!bitmap2.isRecycled()) {
bitmap2.recycle();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
int orient =getOrientation(imagePath);
if (orient ==90 ||orient ==270) {
return new int[]{srcHeight, srcWidth};
}
return new int[]{srcWidth, srcHeight};
}
public static boolean isLongImage(Context context,String imagePath) {
int[]wh =getWidthHeight(imagePath);
float w =wh[0];
float h =wh[1];
float imageRatio = (h /w);
float phoneRatio =PhoneUtil.getPhoneRatio(context.getApplicationContext()) +0.1F;
boolean isLongImage = (w >0 &&h >0) && (h >w) && (imageRatio >=phoneRatio);
//Print.d(TAG, "isLongImage = " + isLongImage);
return isLongImage;
}
public static boolean isWideImage(Context context,String imagePath) {
int[]wh =getWidthHeight(imagePath);
float w =wh[0];
float h =wh[1];
float imageRatio = (w /h);
//float phoneRatio = PhoneUtil.getPhoneRatio(context.getApplicationContext()) + 0.1F;
boolean isWideImage = (w >0 &&h >0) && (w >h) && (imageRatio >=2);
//Print.d(TAG, "isWideImage = " + isWideImage);
return isWideImage;
}
public static boolean isSmallImage(Context context,String imagePath) {
int[]wh =getWidthHeight(imagePath);
boolean isSmallImage =wh[0]
//Print.d(TAG, "isSmallImage = " + isSmallImage);
return isSmallImage;
}
public static float getLongImageMinScale(Context context,String imagePath) {
int[]wh =getWidthHeight(imagePath);
float imageWid =wh[0];
float phoneWid =PhoneUtil.getPhoneWid(context.getApplicationContext());
return phoneWid /imageWid;
}
public static float getLongImageMaxScale(Context context,String imagePath) {
return getLongImageMinScale(context, imagePath) *2;
}
public static float getWideImageDoubleScale(Context context,String imagePath) {
int[]wh =getWidthHeight(imagePath);
float imageHei =wh[1];
float phoneHei =PhoneUtil.getPhoneHei(context.getApplicationContext());
return phoneHei /imageHei;
}
public static float getSmallImageMinScale(Context context,String imagePath) {
int[]wh =getWidthHeight(imagePath);
float imageWid =wh[0];
float phoneWid =PhoneUtil.getPhoneWid(context.getApplicationContext());
return phoneWid /imageWid;
}
public static float getSmallImageMaxScale(Context context,String imagePath) {
return getSmallImageMinScale(context, imagePath) *2;
}
public static Bitmap getImageBitmap(String srcPath,int degree) {
boolean isOOM =false;
BitmapFactory.Options newOpts =new BitmapFactory.Options();
newOpts.inJustDecodeBounds =true;
Bitmap bitmap =BitmapFactory.decodeFile(srcPath,newOpts);
newOpts.inJustDecodeBounds =false;
float be =1;
newOpts.inSampleSize = (int)be;
newOpts.inPreferredConfig =Bitmap.Config.RGB_565;
newOpts.inDither =false;
newOpts.inPurgeable =true;
newOpts.inInputShareable =true;
if (bitmap !=null && !bitmap.isRecycled()) {
bitmap.recycle();
}
try {
bitmap =BitmapFactory.decodeFile(srcPath,newOpts);
}catch (OutOfMemoryError e) {
isOOM =true;
if (bitmap !=null && !bitmap.isRecycled()) {
bitmap.recycle();
}
Runtime.getRuntime().gc();
}catch (Exception e) {
isOOM =true;
Runtime.getRuntime().gc();
}
if (isOOM) {
try {
bitmap =BitmapFactory.decodeFile(srcPath,newOpts);
}catch (Exception e) {
newOpts.inPreferredConfig =Bitmap.Config.RGB_565;
bitmap =BitmapFactory.decodeFile(srcPath,newOpts);
}
}
if (bitmap !=null) {
if (degree ==90) {
degree +=180;
}
bitmap =rotateBitmapByDegree(bitmap, degree);
int ttHeight = (1080 * bitmap.getHeight() / bitmap.getWidth());
if (bitmap.getWidth() >=1080) {
bitmap =zoomBitmap(bitmap,1080,ttHeight);
}
}
return bitmap;
}
private static Bitmap zoomBitmap(Bitmap bitmap,int width,int height) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix matrix =new Matrix();
float scaleWidth = ((float) width /w);
float scaleHeight = ((float) height /h);
matrix.postScale(scaleWidth,scaleHeight);
return Bitmap.createBitmap(bitmap,0,0,w,h,matrix,true);
}
public static String getImageTypeWithMime(String path) {
BitmapFactory.Options options =new BitmapFactory.Options();
options.inJustDecodeBounds =true;
BitmapFactory.decodeFile(path,options);
String type =options.outMimeType;
// ”image/png”、”image/jpeg”、”image/gif”
if (TextUtils.isEmpty(type)) {
type ="";
}else {
type = type.substring(6);
}
return type;
}
public static boolean isGifImageWithMime(String path) {
return "gif".equalsIgnoreCase(getImageTypeWithMime(path));
}
public static boolean isBmpImageWithMime(String path) {
return "bmp".equalsIgnoreCase(getImageTypeWithMime(path));
}
public static boolean isGifImageWithUrl(String url) {
return url.toLowerCase().endsWith("gif");
}
}
PhoneUtil
package com.astro90.android.app.dynamicpreview;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;
public class PhoneUtil {
public static int getPhoneWid(Context context) {
WindowManager windowManager =
(WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metric =new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metric);
return metric.widthPixels;
}
public static int getPhoneHei(Context context) {
WindowManager windowManager =
(WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metric =new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metric);
return metric.heightPixels;
}
public static float getPhoneRatio(Context context) {
return ((float)getPhoneHei(context)) / ((float)getPhoneWid(context));
}
public static int px2dp(Context context,float pxValue) {
final float scale = context.getApplicationContext().getResources().getDisplayMetrics().density;
return (int) (pxValue /scale +0.5f);
}
public static int dp2px(Context context,float dipValue) {
final float scale = context.getApplicationContext().getResources().getDisplayMetrics().density;
return (int) (dipValue *scale +0.5f);
}
}
第四步,清除Glide的缓存
在OnDestroy()中销毁
new Thread(new Runnable() {
@Override
public void run() {
Glide.get(mContext.getApplicationContext()).clearDiskCache();
}
}).start();