问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Android 根据View生成图片

创作时间:
作者:
@小白创作中心

Android 根据View生成图片

引用
CSDN
1.
https://m.blog.csdn.net/SOHU_TECH/article/details/145917546

背景

随着Android技术发展,经常有将某个界面截图保存下来分享,或者生成用户或者商品二维码图片方便加好友或者购买,所以这个功能在实际应用中非常有用。下面将重点介绍这些情况的实现方法。

实现步骤:

  1. 创建View:可以是任何View,比如TextView、ImageView,也可以是LinearLayout、ScrollView或用户自定义的控件。

  2. 将View转化成Bitmap

  • Bitmap代表一张图片,存储的是像素点,安卓中不同类型的图片如jpeg、png都可以用Bitmap表示。
  • Bitmap的创建通常是使用BitmapFactory类来创建的,因为通过看Bitmap构造源码可知使用构造函数来创建Bitmap对象的操作都是再jni层来完成的。
  • 位图位数越高代表其可以存储的颜色信息越多,当然图像也就越逼真,一般默认用ARGB_8888。
  1. 保存或分享Bitmap:通过file文件保存图片文件,注意路径会涉及到申请权限,最终的结果是生成一个图片文件。

分类

开发中,我们需要根据不同的View生成图片,本文根据不同情况的View生成图片进行了一些示例,分类如下:

  1. 普通View生成图片(view已经渲染加载到界面上)
  2. 无中生有,通过java代码创建的或者inflate创建
  3. WebView 生成图片
  4. ScrollView 生成图片
  5. ListView 生成图片
  6. RecyclerView 生成图片

代码示例

3.1 普通View,就是在文本中已经绘制到界面的View

核心代码:

// 通过走一遍ViewGroup的测量(measure),布局(layout),draw流程,把布局展示的界面画到我们准备好的bitmap上(这一过程可在非UI线程完成),再把bitmap保存在文件或显示到界面上。
// 1. 在布局中写好图片的样子,然后把布局inflate成View,当然也可以直接代码编写View,设置好里面的可变元素,如头像,昵称等;
// 2. 通过调用View的measure,layout方法使之测量出内部各控件的大小和排列好各控件;
// 3. 创建一个和View大小相同的空Bitmap,新建一个画布传入该bitamp(new Canvas(bitmap)),调用view的draw(canvas)方法,view会把图片绘制在该bitmap上;
// 4. 保存到文件或直接使用图片。

3.2 代码加载但是未显示在界面的View

有一些View,我们是通过代码加载出来的,但是没有加载界面上,我们也可以对这种View生成图片。什么?既然没有显示在界面上,那还要加载来干嘛?此言差矣,用处还是有的,YY即可。

核心代码:

代码中,我们看到,我们按下截图,inflate加载一个简单布局文件, 我们看到,里面就是一个ImagView,我们待会就是要给这个ImageView 设置一张图片,然后对这个View进行生成图片,但是注意,这个ImageView从始至终都是没有显示在界面上的, 这个ImageView并没有加载到布局。我们想直接调用正常View的生成图片方法,但是如果这样会生成图片失败。

因为刚刚inflate的View是没有经过measure和layout的,没有大小,所以我们需要指定一下大小, 有了大小,就可以生成图片了。

3.3 WebView生成图片

代码中,可以看到,就是webView加载完成后,拿到当前webView的高和宽,通过Canvas生成bitmap。

3.4 ScrollView生成图片

核心代码:
代码原理:中心思想就是传入scrollview,生成一个bitmap,和前面差不多。

3.5 ListView生成图片

核心代码:(垂直排列)

中心原理:这个listView比之前的视图要复杂一些,首先要生成一个List,遍历adapter子对象,测量每一个子对象的高度和宽度,设置view缓存后生成bitmap,并放在list里面,再通过paint把每一个bitmap画出来,放在最后生成的bitmap里面,最后清除缓存文件,避免占用内存。

3.6 RecyclerView 生成图片

核心代码:(垂直排列)

代码原理:如果是水平排列,高度累加换成宽度累加即可,和ListView生成图片总体来说差不多,个别有细微差别,主要体现在生成LruCache上,啥是LruCache?说白了就是个链表,LRU Cache 的替换原则就是将最近最少使用的内容替换掉,LruCache是个泛型类,内部采用LinkedHashMap来实现缓存机制,它提供get方法和put方法来获取缓存和添加缓存,其最重要的方法trimToSize是用来移除最少使用的缓存和使用最久的缓存,并添加最新的缓存到队列中,最后释放掉多余bitmap,但是本质都是生成一个bitmap。

还有一种方法,创建一个自定义的ItemViewBinder来绑定数据到RecyclerView的ViewHolder,使用RecyclerView的measure和layout方法来布局其子视图,创建一个足够大的Bitmap对象,并使用Canvas来绘制RecyclerView的内容。

使用这个方法,你可以调用它并传入你的RecyclerView以及期望的图片尺寸。这个方法会返回一个包含了RecyclerView内容的Bitmap对象。请注意,这种方法可能不适用于包含复杂交互或自定义绘制的RecyclerView子视图。对于那些子视图,你可能需要在绘制之前手动处理他们的绘制逻辑,具体参考第一种方法。

3.7 鉴权数据可视化

如果不是复杂的图片,直接绘制也是可以的,比如要在全屏图片上展示文字并生成一个新的图片并分享出去,图片全屏,文字居中 ,上代码:

代码原理:很简单,用paint写字,居中,用Canvas保存bitmap就可以。

总结:

  1. 对于简单的view生成图片,直接采用通用方式,即创建bitmap;创建Canvas;draw(canvas);
  2. 对于列表类型的View,处理方式有所区别,以下分别说明ScrollView,Listview,Recyclerview。
  • ScrollView:三个截屏中,ScrollView最简单,因为ScrollView只有一个childView,虽然没有全部显示在界面上,但是已经全部渲染绘制,因此可以直接调用scrollView.draw(canvas)来完成图片绘制,
  • 而ListView就是会回收与重用Item,并且只会绘制在屏幕上显示的ItemView,采用一个List来存储Item的视图,这个list用啥类型都可以,目的就是存储bitmap,避免oom,而在新的Android版本中,已经可以用RecyclerView来代替使用ListView的场景,相比较ListView,RecyclerView对Item View的缓存支持的更好。可以采用和ListView相同的方案,而对于RecyclerView的manager,是Layout Manager,方向是竖直,因为是针对每个item计算高度,高度累加,如果是别的类型的,需要手动算出高度,要注意如果item是个长图片,需要等待其加载完,否则会出现高度计算错误的问题,具体问题具体分析哈。

实际应用

App客户端来了需求,要进行海报图片分享,类似这样:

使用ScrollView生成后bitmap,存储也是个坑,需考虑版本兼容以及权限问题。保存图片的方式根据「版本和权限」分为两种:

  • Android Q(Android 10) 以上:

    1. 保存到应用的内部存储空间 (内部存储);
    2. 保存到 Android 系统设置的共享存储空间(外部储存)。
  • Android Q(Android 10) 以下:

    1. 获取外部存储目录;函数使用:getExternalStorageDirectory()。
    2. 获取外部存储公共目录;函数使用:getExternalStoragePublicDirectory()。
    3. 图片(包括照片和屏幕截图),存储在 DCIM/ 或 Pictures/ 目录。

所以,不同系统版本的手机需要做兼容,否则很容易出现保存图片不成功,crash或者各种诡异问题。

参考文献:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号