Android生成view分享截图
Posted 亮亮在江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android生成view分享截图相关的知识,希望对你有一定的参考价值。
原生view截图
合并后的view截图
主要的业务逻辑,点击界面时有一个缩放动画,对当前界面进行截屏,启用DrawingCache并创建位图,将view界面转化为bitmap,进行保存下来。然后隐藏掉原界面,显示合成后的界面。
主要代码:
setAnimator(1f, 0.8f);
//对当前页面进行截屏
source.setDrawingCacheEnabled(true);
source.buildDrawingCache(); //启用DrawingCache并创建位图
//创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收
Bitmap screen = Bitmap.createBitmap(createViewBitmap(source));
sourceMid.setImageBitmap(screen);
source.setVisibility(View.GONE);
splicing.setVisibility(View.VISIBLE);
splicing.post(new Runnable() {
@Override
public void run() {
screenBitmap = Bitmap.createBitmap(createViewBitmap(splicing)); //合成后的图片
initDownloadImg();
}
});
private void initDownloadImg() {
File savedir = new File(FILE_SAVEPATH);
if (!savedir.exists()) {
savedir.mkdirs();
}
FileOutputStream out = null;
try {
out = new FileOutputStream(pathfile);
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(this,"保存失败",Toast.LENGTH_SHORT).show();
}
try {
if (null != out) {
screenBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
}
Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show();
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(pathfile))));
} catch (IOException e) {
Toast.makeText(this,"保存失败",Toast.LENGTH_SHORT).show();
}
}
public Bitmap createViewBitmap(View v) {
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
return bitmap;
}
具体demo资源下载路径:view合并分享截图
接下来涉及些其他截屏的方案,包含普通截屏,Scrollview截屏,listview截屏,RecyclerView截屏,合成Bitmap。
1、普通截屏的实现
获取当前Window的DrawingCache的方式,即decorView的DrawingCache
/**
* shot the current screen ,with the status but the status is trans *
*
* @param ctx current activity
*/
public static Bitmap shotActivity(Activity ctx) {
View view = ctx.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bp = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(),
view.getMeasuredHeight());
view.setDrawingCacheEnabled(false);
view.destroyDrawingCache();
return bp;
}
获取当前View的DrawingCache
public static Bitmap getViewBp(View v) {
if (null == v) {
return null;
}
v.setDrawingCacheEnabled(true);
v.buildDrawingCache();
if (Build.VERSION.SDK_INT >= 11) {
v.measure(MeasureSpec.makeMeasureSpec(v.getWidth(),
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
v.getHeight(), MeasureSpec.EXACTLY));
v.layout((int) v.getX(), (int) v.getY(),
(int) v.getX() + v.getMeasuredWidth(),
(int) v.getY() + v.getMeasuredHeight());
} else {
v.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
}
Bitmap b = Bitmap.createBitmap(v.getDrawingCache(), 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
v.setDrawingCacheEnabled(false);
v.destroyDrawingCache();
return b;
}
2、Scrollview截屏
三个截屏中,ScrollView最简单,因为ScrollView只有一个childView,虽然没有全部显示在界面上,但是已经全部渲染绘制,因此可以直接 调用scrollView.draw(canvas)
来完成截图
public static Bitmap shotScrollView(ScrollView scrollView) {
int h = 0;
Bitmap bitmap = null;
for (int i = 0; i < scrollView.getChildCount(); i++) {
h += scrollView.getChildAt(i).getHeight();
scrollView.getChildAt(i).setBackgroundColor(Color.parseColor("#ffffff"));
}
bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.RGB_565);
final Canvas canvas = new Canvas(bitmap);
scrollView.draw(canvas);
return bitmap;
}
3、listview截屏
而ListView就是会回收与重用Item,并且只会绘制在屏幕上显示的ItemView,根据stackoverflow上大神的建议,采用一个List来存储Item的视图,这种方案依然不够好,当Item足够多的时候,可能会发生oom。
public static Bitmap shotListView(ListView listview) {
ListAdapter adapter = listview.getAdapter();
int itemscount = adapter.getCount();
int allitemsheight = 0;
List<Bitmap> bmps = new ArrayList<Bitmap>();
for (int i = 0; i < itemscount; i++) {
View childView = adapter.getView(i, null, listview);
childView.measure(
View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
childView.setDrawingCacheEnabled(true);
childView.buildDrawingCache();
bmps.add(childView.getDrawingCache());
allitemsheight += childView.getMeasuredHeight();
}
Bitmap bigbitmap =
Bitmap.createBitmap(listview.getMeasuredWidth(), allitemsheight, Bitmap.Config.ARGB_8888);
Canvas bigcanvas = new Canvas(bigbitmap);
Paint paint = new Paint();
int iHeight = 0;
for (int i = 0; i < bmps.size(); i++) {
Bitmap bmp = bmps.get(i);
bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
iHeight += bmp.getHeight();
bmp.recycle();
bmp = null;
}
return bigbitmap;
}
4、RecyclerView截屏
我们都知道,在新的android版本中,已经可以用RecyclerView来代替使用ListView的场景,相比较ListView,RecyclerView对Item View的缓存支持的更好。可以采用和ListView相同的方案,这里也是在stackoverflow上看到的方案。
public static Bitmap shotRecyclerView(RecyclerView view) {
RecyclerView.Adapter adapter = view.getAdapter();
Bitmap bigBitmap = null;
if (adapter != null) {
int size = adapter.getItemCount();
int height = 0;
Paint paint = new Paint();
int iHeight = 0;
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize);
for (int i = 0; i < size; i++) {
RecyclerView.ViewHolder holder = adapter.createViewHolder(view, adapter.getItemViewType(i));
adapter.onBindViewHolder(holder, i);
holder.itemView.measure(
View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(),
holder.itemView.getMeasuredHeight());
holder.itemView.setDrawingCacheEnabled(true);
holder.itemView.buildDrawingCache();
Bitmap drawingCache = holder.itemView.getDrawingCache();
if (drawingCache != null) {
bitmaCache.put(String.valueOf(i), drawingCache);
}
height += holder.itemView.getMeasuredHeight();
}
bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888);
Canvas bigCanvas = new Canvas(bigBitmap);
Drawable lBackground = view.getBackground();
if (lBackground instanceof ColorDrawable) {
ColorDrawable lColorDrawable = (ColorDrawable) lBackground;
int lColor = lColorDrawable.getColor();
bigCanvas.drawColor(lColor);
}
for (int i = 0; i < size; i++) {
Bitmap bitmap = bitmaCache.get(String.valueOf(i));
bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint);
iHeight += bitmap.getHeight();
bitmap.recycle();
}
}
return bigBitmap;
}
相信有不少小伙伴用BRVH第三方库来做recycleview的适配器的。使用这个库的话再用上面的方法会报角标越界的错误,看了BRVH的源码
public void onBindViewHolder(ViewHolder holder, int positions) {
int viewType = holder.getItemViewType();
switch(viewType) {
case 0:
this.convert((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount()));
case 273:
case 819:
case 1365:
break;
case 546:
this.addLoadMore(holder);
break;
default:
this.convert((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount()));
this.onBindDefViewHolder((BaseViewHolder)holder, this.mData.get(holder.getLayoutPosition() - this.getHeaderLayoutCount()));
}
}
在调用adapter.onBindViewHolder
时,因为里面的position
参数未使用,里面用的计算holder.getLayoutPosition() - this.getHeaderLayoutCount()
的值一直是-1导致角标越界报错。
本人理解,RecyclerView的截屏原理是,首先构造每个item的ViewHolder,然后调用具体设置数据到每个item的方法,此时cache中就存有item的内容,此时绘制就能获取到完整的内容。采用v7包中的onBindViewHolder
方法即可,或者是BRVH的convert
方法,可以看到BRVH中没有暴露出这个方法,而且唯一暴露出的onBindViewHolder
还会报角标越界错误,此时我们就需要在BRVH的基础上暴露出convert
即可,代码如下
public class MyAdapter extends BaseQuickAdapter<T> {
public MyAdapter() {
super(getItemLayoutResId(), datas);
}
/**
* 用于对外暴露convert方法,构造缓存视图(截屏用)
* @param viewHolder
* @param t
*/
public void startConvert(BaseViewHolder viewHolder, T t){
convert(viewHolder,t);
}
@Override
protected void convert(BaseViewHolder viewHolder, T t) {
bindView(viewHolder, t);
}
}
然后将上面所述的获取Bitmap方法修改一下
/**
* 截取recycler view
*/
public static Bitmap getRecyclerViewScreenshot(RecyclerView view) {
BaseListFragment.MyAdapter adapter = (BaseListFragment.MyAdapter) view.getAdapter();
Bitmap bigBitmap = null;
if (adapter != null) {
int size = adapter.getData().size();
int height = 0;
Paint paint = new Paint();
int iHeight = 0;
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
LruCache<String, Bitmap> bitmaCache = new LruCache<>(cacheSize);
for (int i = 0; i < size; i++) {
BaseViewHolder holder = (BaseViewHolder) adapter.createViewHolder(view, adapter.getItemViewType(i));
//此处需要调用convert方法,否则绘制出来的都是空的item
adapter.startConvert(holder, adapter.getData().get(i));
holder.itemView.measure(
View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
holder.itemView.layout(0, 0, holder.itemView.getMeasuredWidth(),
holder.itemView.getMeasuredHeight());
holder.itemView.setDrawingCacheEnabled(true);
holder.itemView.buildDrawingCache();
Bitmap drawingCache = holder.itemView.getDrawingCache();
if (drawingCache != null) {
bitmaCache.put(String.valueOf(i), drawingCache);
}
height += holder.itemView.getMeasuredHeight();
}
bigBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), height, Bitmap.Config.ARGB_8888);
Canvas bigCanvas = new Canvas(bigBitmap);
Drawable lBackground = view.getBackground();
if (lBackground instanceof ColorDrawable) {
ColorDrawable lColorDrawable = (ColorDrawable) lBackground;
int lColor = lColorDrawable.getColor();
bigCanvas.drawColor(lColor);
}
for (int i = 0; i < size; i++) {
Bitmap bitmap = bitmaCache.get(String.valueOf(i));
bigCanvas.drawBitmap(bitmap, 0f, iHeight, paint);
iHeight += bitmap.getHeight();
bitmap.recycle();
}
}
return bigBitmap;
}
5、合成Bitmap
比如四张合成一张
/**
* 将四张图拼成一张
*
* @param pic1 图一
* @param pic2 图二
* @param pic3 图三
* @param pic4 图四
* @return only_bitmap
* 详情见说明:{@link com.bertadata.qxb.util.ScreenShotUtils}
*/
public static Bitmap combineBitmapsIntoOnlyOne(Bitmap pic1, Bitmap pic2, Bitmap pic3, Bitmap pic4, Activity context) {
int w_total = pic2.getWidth();
int h_total = pic1.getHeight() + pic2.getHeight() + pic3.getHeight() + pic4.getHeight();
int h_pic1 = pic1.getHeight();
int h_pic4 = pic4.getHeight();
int h_pic12 = pic1.getHeight() + pic2.getHeight();
//此处为防止OOM需要对高度做限制
if (h_total > HEIGHTLIMIT) {
return null;
}
Bitmap only_bitmap = Bitmap.createBitmap(w_total, h_total, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(only_bitmap);
canvas.drawColor(ContextCompat.getColor(context, R.color.color_content_bg));
canvas.drawBitmap(pic1, 0, 0, null);
canvas.drawBitmap(pic2, 0, h_pic1, null);
canvas.drawBitmap(pic3, 0, h_pic12, null);
canvas.drawBitmap(pic4, 0, h_total - h_pic4, null);
return only_bitmap;
}
以上是关于Android生成view分享截图的主要内容,如果未能解决你的问题,请参考以下文章