今天就记录一下。
先说明一下,之前没有做过类似的东西,百度了一两天才知道,说来很惭愧、有点笨,只能这样说。
在我的脑里只明白,如果要动态创建图片:
一、就是new 嘛
二、就是LayoutInflater.from()这种。
而微信分享图片到朋友圈,这种不可能new textview或者Imageview,所以用第二种,LayoutInflater,加载布局的父类引用view,索性就开始干,结果,跳转到微信的界面图片直接就是空白图片,一直以为是没有把数据加入进去,反复地测试数据是有点,但还是空白图片,百度了一下,View.getDrawingCache() 只适用于分享的View已经完整展示在用户的屏幕上,超出屏幕范围内的内容是不在生成的Bitmap内的。
一、
大家可以看看这个解释:android后台通过View生成分享图片
虽然有点燥,但还是和我经过几个小时测试得出结论相差不多需要展示在界面才能根据view的宽高:
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
这里用的ARGB_4444是16位的,可以用ARGB_8888 32位的,为了防止图片太大,分享不成功,把图片分辨率弄低一点,展示一下view下生成代码:
View view = inflater.inflate(R.layout.dialog, null, false);
private void checkAndRequestSavePermission(View view, int type) {
//判断当前系统的SDK版本是否大于23
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//如果当前申请的权限没有授权
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//请求权限
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 1);
} else {//已经授权了就走这条分支
savePic(view, type);
}
} else {
savePic(view, type);
}
}
上面有个权限是,生成图片需要把图片保存在手机相册里面。
private void savePic(final View view, final int type) {
view.post(new Runnable() {
@Override
public void run() {
//获取view 长宽
int width = view.getWidth();
int height = view.getHeight();
//若传入的view长或宽为小于等于0,则返回,不生成图片
if (width <= 0 || height <= 0) {
return;
}
//生成一个ARGB8888的bitmap,宽度和高度为传入view的宽高
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
//根据bitmap生成一个画布
Canvas canvas = new Canvas(bitmap);
//注意:这里是解决图片透明度问题,给底色上白色,不然是透明色背景可能会很难看,若存储时保存的为png格式的图,则无需此步骤
canvas.drawColor(Color.WHITE);
view.draw(canvas);
//不清晰,应该是压缩的厉害,很模糊
// view.setDrawingCacheEnabled(true);
// view.buildDrawingCache();
// view.setBackgroundColor(Color.WHITE);
// bitmap = BitmapUtils.reduce(view.getDrawingCache(),500, 500, true);
//把bitmip保存到手机本地
path = BitmapUtils.saveImage(bitmap, context);
if (TextUtils.isEmpty(path)) {
showMessage("保存图片失败");
} else {
//这里是需要实现的图片地址path是String
}
}
});
}
public static String saveImage(Bitmap bmp, Context context) {
File appDir = new File(Environment.getExternalStorageDirectory(), "自己定义");
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).setData(Uri.fromFile(file)));//更新相册广播
return file.getPath();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
二、
上面这种方法对于已经显示在屏幕上,是完全没有问题的,但是对不需要在屏幕上显示的view而不行,还是这个大神给的启发:
这个是先自定义LinearLayout,在里面实现画布局方法,相当于后台操作,直接开始。
public class SaveImgLinearLayout extends LinearLayout {
private Listener listener;
private Context context;
// 长图的宽度,默认为屏幕宽度
private int longPictureWidth;
// 长图两边的间距
private int picMargin;
private View rootView;
// 被认定为长图的长宽比
private int widthTop = 0;
private int heightTop = 0;
private int widthContent = 0;
private int heightContent = 0;
private int widthBottom = 0;
private int heightBottom = 0;
private ImageView imgTop, imgCenter;
private LinearLayout llBottom;
private GoodsDetail bean;
public interface Listener {
/**
* 生成长图成功的回调
*
* @param path 长图路径
*/
void onSuccess(String path);
/**
* 生成长图失败的回调
*/
void onFail();
}
public void setListener(Listener listener) {
this.listener = listener;
}
public SaveImgLinearLayout(Context context, GoodsDetail bean, View view) {
super(context);
this.bean = bean;
this.rootView = view;
init(context);
}
public SaveImgLinearLayout(Context context) {
super(context);
init(context);
}
public SaveImgLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SaveImgLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
this.context = context;
longPictureWidth = ScreenUtils.getScreenWidth(context);
picMargin = 40;
initView();
}
private void initView() {
imgTop = (ImageView) rootView.findViewById(R.id.image_top);
imgCenter = (ImageView) rootView.findViewById(R.id.imageview_center);
llBottom = (LinearLayout) rootView.findViewById(R.id.ll_buttom);
//测量头、中、底部
layoutView(imgTop);
layoutView(imgCenter);
layoutView(llBottom);
widthTop = imgTop.getMeasuredWidth();
heightTop = imgTop.getMeasuredHeight();
widthContent = imgCenter.getMeasuredWidth();
// 由于高度是可变的,这里需要用post方法算出
imgCenter.post(new Runnable() {
@Override
public void run() {
heightContent = imgCenter.getHeight();
}
});
widthBottom = llBottom.getMeasuredWidth();
heightBottom = llBottom.getMeasuredHeight();
}
/**
* 手动测量view宽高
*/
private void layoutView(View v) {
//获取屏幕宽高
int width = ScreenUtils.getScreenWidth(context);
int height = ScreenUtils.getScreenHeight(context);
v.layout(0, 0, width, height);
int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
int measuredHeight = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
v.measure(measuredWidth, measuredHeight);
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
}
public void startDraw() {
// 需要先下载全部需要用到的图片(用户头像、图片等),下载完成后再进行长图的绘制操作
downloadAllImage();
}
private void downloadAllImage() {
// 之类根据自己的逻辑进行图片的下载,此Demo为了简单,制作一个延时模拟下载过程
new Thread(new Runnable() {
@Override
public void run() {
// 模拟下载图片的耗时操作,推荐使用:implementation 'com.liulishuo.filedownloader:library:1.7.3'
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 图片下载完成后,进行view的绘制
// 模拟保存图片url、路径的键值对
// 开始绘制view
draw();
}
}).start();
}
private void draw() {
// 创建空白画布
Bitmap.Config config = Bitmap.Config.ARGB_8888;
Bitmap bitmapAll;
// 计算出最终生成的长图的高度 = 上、中、图片总高度、下等个个部分加起来
int allBitmapHeight = heightTop + heightContent + heightBottom;
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);
// 绘制top view
if (widthTop <= 0 && heightTop <= 0 && widthContent <= 0 && heightContent <= 0 && widthBottom <= 0 && heightBottom <= 0) {
return;
}
//决定你布局显示的位置,如果设置为0 ,就会如fragment一样,都在0.0位置显示重叠在一起
canvas.drawBitmap(getLinearLayoutBitmap(imgTop, widthTop, heightTop), 0, 0, paint);
canvas.drawBitmap(getLinearLayoutBitmap(imgCenter, widthContent, heightContent), 0, heightTop, paint);
canvas.drawBitmap(getLinearLayoutBitmap(llBottom, widthBottom, heightBottom), 0, heightTop + heightContent, paint);
canvas.save();
// 绘制content view
// canvas.translate(MyDensity.dp2px(context, 20), heightTop);
// staticLayout.draw(canvas);
// 生成最终的文件,并压缩大小,这里使用的是:implementation
'com.github.nanchen2251:CompressHelper:1.0.5'
//这里可以压缩,,我没有压缩,如果有需要可以压缩
try {
//把图片保存在手机底部。
String path = BitmapUtils.saveImage(bitmapAll, context);
if (listener != null) {
listener.onSuccess(path);
}
} catch (Exception e) {
e.printStackTrace();
if (listener != null) {
listener.onFail();
}
}
}
private Bitmap getLinearLayoutBitmap(View imgTop, int w, int h) {
Bitmap originBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(originBitmap);
imgTop.draw(canvas);
return resizeImage(originBitmap, longPictureWidth, h);
}
public 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 void removeListener() {
this.listener = null;
}
}
使用时候,直接new 就行了,,然后调用startDraw()方法,就可以了。
至此,已经完了,希望大家给出意见,有什么更好的方法,提出来。