OLIS(Create Long Image Synthesis)
最近公司需求 处理多张图片合成并且加底部 合成

实现原理
读取本地绝对路径图片-> 转换成bitmap -> 然后绘制到画布上 -> 保存成文件 即可
直接上代码
LongPictureCreate
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.nanchen.compresshelper.CompressHelper;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import cc.shinichi.library.tool.ui.PhoneUtil;
public class LongPictureCreate extends View {
private final String TAG = "LongPictureCreate";
private Context context;
private Listener listener;
// 图片的url集合
private List<String> imageUrlList;
// 保存下载后的图片url和路径键值对的链表
private LinkedHashMap<String, String> localImagePathMap;
// 长图的宽度,默认为屏幕宽度
private int longPictureWidth;
// 长图两边的间距
private int picMargin;
// 被认定为长图的长宽比
private int maxSingleImageRatio = 3;
private Bitmap buttomBitmap;
public LongPictureCreate(Context context) {
super(context);
init(context);
}
public LongPictureCreate(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LongPictureCreate(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void removeListener() {
this.listener = null;
}
public void setListener(Listener listener) {
this.listener = listener;
}
private void init(Context context) {
this.context = context;
picMargin = 0;
longPictureWidth = PhoneUtil.getPhoneWid(context);
initView();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void initView() {
}
public void setData(List<String> imageList) {
this.imageUrlList = imageList;
if (this.imageUrlList == null) {
this.imageUrlList = new ArrayList<>();
}
if (localImagePathMap != null) {
localImagePathMap.clear();
} else {
localImagePathMap = new LinkedHashMap<>();
}
}
public void startDraw() {
// 需要先下载全部需要用到的图片(用户头像、图片等),下载完成后再进行长图的绘制操作
new Thread(() -> {
// 图片下载完成后,进行view的绘制
// 模拟保存图片url、路径的键值对
for (int i = 0; i < imageUrlList.size(); i++) {
localImagePathMap.put(imageUrlList.get(i), imageUrlList.get(i));
}
// 开始绘制view
draw();
}).start();
}
private int getAllImageHeight() {
int height = 0;
for (int i = 0; i < imageUrlList.size(); i++) {
int[] wh = ImageUtil.getWidthHeight(localImagePathMap.get(imageUrlList.get(i)));
int w = wh[0];
int h = wh[1];
wh[0] = (longPictureWidth - (picMargin) * 2);
wh[1] = (wh[0]) * h / w;
float imgRatio = h / w;
if (imgRatio > maxSingleImageRatio) {
wh[1] = wh[0] * maxSingleImageRatio;
Log.d(TAG, "getAllImageHeight w h > maxSingleImageRatio = " + Arrays.toString(wh));
}
height = height + wh[1];
}
height = height + imageUrlList.size();
return height;
}
private Bitmap getSingleBitmap(String path) {
int[] wh = ImageUtil.getWidthHeight(path);
final int w = wh[0];
final int h = wh[1];
wh[0] = (longPictureWidth - (picMargin) * 2);
wh[1] = (wh[0]) * h / w;
Bitmap bitmap = null;
try {
// 长图,只截取中间一部分
float imgRatio = h / w;
if (imgRatio > maxSingleImageRatio) {
wh[1] = wh[0] * maxSingleImageRatio;
Log.d(TAG, "getSingleBitmap w h > maxSingleImageRatio = " + Arrays.toString(wh));
}
bitmap = Glide.with(context).asBitmap().load(path).centerCrop().into(wh[0], wh[1]).get();
Log.d(TAG, "getSingleBitmap glide bitmap w h = " + bitmap.getWidth() + " , " + bitmap.getHeight());
return bitmap;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
private Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
if (bitmap == null) {
return null;
}
try {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
paint.setAntiAlias(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.BLACK);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
final Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawBitmap(bitmap, src, rect, paint);
Log.d(TAG, "getRoundedCornerBitmap w h = " + output.getWidth() + " × " + output.getHeight());
return output;
} catch (Exception e) {
e.printStackTrace();
return bitmap;
}
}
private int getAllTopHeightWithIndex(int index) {
int height = 0;
for (int i = 0; i < index + 1; i++) {
int[] wh = ImageUtil.getWidthHeight(localImagePathMap.get(imageUrlList.get(i)));
int w = wh[0];
int h = wh[1];
wh[0] = (longPictureWidth - (picMargin) * 2);
wh[1] = (wh[0]) * h / w;
float imgRatio = h / w;
if (imgRatio > maxSingleImageRatio) {
wh[1] = wh[0] * maxSingleImageRatio;
Log.d(TAG, "getAllImageHeight w h > maxSingleImageRatio = " + Arrays.toString(wh));
}
height = height + wh[1];
}
height = height ;
Log.d(TAG, "---getAllTopHeightWithIndex = " + height);
return height;
}
private void draw() {
// 计算出最终生成的长图的高度 = 上、中、图片总高度、下等个个部分加起来
int allBitmapHeight = 0;
Bitmap last = Bitmap.createScaledBitmap(buttomBitmap, longPictureWidth - 2*picMargin,buttomBitmap.getHeight(),false);
// 计算图片的总高度
if (imageUrlList != null & imageUrlList.size() > 0) {
allBitmapHeight = getAllImageHeight()+last.getHeight() ;
}else {
allBitmapHeight = last.getHeight() ;
}
// 创建空白画布
Bitmap.Config config = Bitmap.Config.ARGB_8888;
Bitmap bitmapAll;
try {
bitmapAll = Bitmap.createBitmap(longPictureWidth, allBitmapHeight, config);
} catch (Exception e) {
e.printStackTrace();
config = Bitmap.Config.RGB_565;
bitmapAll = Bitmap.createBitmap(longPictureWidth, allBitmapHeight, config);
}
Canvas canvas = new Canvas(bitmapAll);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setFilterBitmap(true);
int top = 0;
if (imageUrlList != null && imageUrlList.size() > 0) {
Bitmap bitmapTemp;
int imageRadius = 0;
for (int i = 0; i < imageUrlList.size(); i++) {
bitmapTemp = getSingleBitmap(localImagePathMap.get(imageUrlList.get(i)));
Bitmap roundBitmap = getRoundedCornerBitmap(bitmapTemp, imageRadius);
top = getAllTopHeightWithIndex(i - 1 );
if (roundBitmap != null) {
canvas.drawBitmap(roundBitmap, picMargin, top, paint);
}
}
}
top = getAllTopHeightWithIndex(imageUrlList.size() - 1 );
last.setDensity(bitmapAll.getDensity());
//getAllTopHeightWithIndex(imageUrlList.size()+1 )
canvas.drawBitmap(last, picMargin, top, paint);
// 生成最终的文件,并压缩大小,这里使用的是:implementation 'com.github.nanchen2251:CompressHelper:1.0.5'
try {
String path = ImageUtil.saveBitmapBackPath(bitmapAll);
//保存图片到本地
savePicLocal(path);
Log.d(TAG, "最终生成的长图路径为:" + path);
if (listener != null) {
listener.onSuccess(path);
}
} catch (IOException e) {
e.printStackTrace();
if (listener != null) {
listener.onFail();
}
}
}
private void savePicLocal(String path) {
float imageRatio = ImageUtil.getImageRatio(path);
// 最终压缩后的长图宽度
int finalCompressLongPictureWidth;
if (imageRatio >= 10) {
finalCompressLongPictureWidth = 750;
} else if (imageRatio >= 5 && imageRatio < 10) {
finalCompressLongPictureWidth = 900;
} else {
finalCompressLongPictureWidth = longPictureWidth;
}
String result;
// 由于长图一般比较大,所以压缩时应注意OOM的问题,这里并不处理OOM问题,请自行解决。
try {
result = new CompressHelper.Builder(context).setMaxWidth(finalCompressLongPictureWidth)
.setMaxHeight(Integer.MAX_VALUE) // 默认最大高度为960
.setQuality(80) // 默认压缩质量为80
.setFileName("长图_" + System.currentTimeMillis()) // 设置你需要修改的文件名
.setCompressFormat(Bitmap.CompressFormat.JPEG) // 设置默认压缩为jpg格式
.setDestinationDirectoryPath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()+ "/长图分享/")
.build()
.compressToFile(new File(path))
.getAbsolutePath();
} catch (OutOfMemoryError e) {
e.printStackTrace();
finalCompressLongPictureWidth = finalCompressLongPictureWidth / 2;
result = new CompressHelper.Builder(context).setMaxWidth(finalCompressLongPictureWidth)
.setMaxHeight(Integer.MAX_VALUE) // 默认最大高度为960
.setQuality(50) // 默认压缩质量为80
.setFileName("长图_" + System.currentTimeMillis()) // 设置你需要修改的文件名
.setCompressFormat(Bitmap.CompressFormat.JPEG) // 设置默认压缩为jpg格式
.setDestinationDirectoryPath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ "/长图分享/")
.build()
.compressToFile(new File(path))
.getAbsolutePath();
}
}
public static Bitmap resizeImage(Bitmap origin, int newWidthOrHeight) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) newWidthOrHeight) / width;
float newHeight = height * scaleWidth;
//float scaleHeight = ((float) newHeight) / height;
float offsetx = (newWidthOrHeight - width) / 2f;
float offsety = (newHeight - height) / 2f;
offsetx = newWidthOrHeight < width ? Math.abs(offsetx) : 0;
offsety = newWidthOrHeight < width ? Math.abs(offsety) : 0;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleWidth);//, width >> 1, height >> 1
Bitmap bitmap = Bitmap.createBitmap(origin, (int)offsetx, (int)offsety, newWidthOrHeight, (int)newHeight, matrix, false);
Bitmap result = Bitmap.createScaledBitmap(origin, newWidthOrHeight, (int)newHeight, false);
if (!origin.isRecycled()) {
origin.recycle();
}
return result;
}
public void setbuttomBitmap(Bitmap buttomBitmap) {
this.buttomBitmap = buttomBitmap;
}
public interface Listener {
/**
* 生成长图成功的回调
*
* @param path 长图路径
*/
void onSuccess(String path);
/**
* 生成长图失败的回调
*/
void onFail();
}
}
ImageUtil
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 {
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 < originalHeight && originalHeight > 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 int[] getWidthHeight(String imagePath) {
if (TextUtils.isEmpty(imagePath)) {
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 <= 0 || srcWidth <= 0) {
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();
}
}
}
return new int[] { srcWidth, srcHeight };
}
public static float getImageRatio(String imagePath) {
int[] wh = getWidthHeight(imagePath);
if (wh[0] > 0 && wh[1] > 0) {
return (float) Math.max(wh[0], wh[1]) / (float) Math.min(wh[0], wh[1]);
}
return 1;
}
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();
}
}
使用方式
LongPictureCreate drawLongPictureUtil = new LongPictureCreate(MainActivity.this);
drawLongPictureUtil.setListener(new LongPictureCreate.Listener() {
@Override public void onSuccess(String path) {
runOnUiThread(new Runnable() {
@Override public void run() {
//合成长图路径
}
});
}
@Override public void onFail() {
runOnUiThread(new Runnable() {
@Override public void run() {
//合成失败回调
}
});
}
});
调用方法
drawLongPictureUtil.setbuttomBitmap(buttomBitmap);
drawLongPictureUtil.setData('List<String>本地路径');
drawLongPictureUtil.startDraw();
网络地址图片转换bitmap
Glide.with(this).asBitmap().load("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2800997457,1841442195&fm=26&gp=0.jpg").into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap buttomBitmap, @Nullable Transition<? super Bitmap> transition) {
}
});
大功告成
网友评论