本文以少量的代码,演示了无需声明权限、仅10余行代码即可保存任意View为图片的简洁方法,demo源码链接附在文末
特别说明:
本文采用分—总结构展示代码,即先展示局部代码,并说明其用法和作用;小节结尾处再给出该部分对应文件的完整代码。布局XML文件在方法实现之后。
一、方法实现
分两步完成:
- 转换View对象为Bitmap位图对象
- 将Bitmap位图对象保存为图片
在MainActivity文件中:
1.将View转换为Bitmap位图
/** 创建位图 */
fun createViewBitmap(view: View): Bitmap? {
val bitmap = Bitmap.createBitmap(
view.width, view.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
Bitmap.createBitmap(参数1,参数2,参数3)
这是内置的方法,直接调用即可将View对象转化为一个Bitmap位图对象。
参数1:生成的Bitmap位图对象的宽,可使用当前View实际呈现的宽度,也可自行指定
参数2:生成的Bitmap位图对象的高,可使用当前View实际呈现的高度,也可自行指定
参数3:指定Bitmap位图对象的存储方案:本文采用Bitmap.Config.ARGB_8888
位图存储方案简介:A指alpha透明通道,RGB指红绿蓝三通道。
- ARGB_8888:四通道均采用8bit存储,质量最高,体积最大
- ARGB_4444:四通道均为4bit存储,据说失真较严重
- RGB_565:不包含透明通道,不支持透明度,但质量较好,体积仅ARGB_8888的一半
- ALPHA_8:不包含颜色值,仅存储alpha透明度
一般情况下,若无需透明度,选择RGB_565,可节省空间,需要透明度,选择ARGB_8888
更多细节敬请参见这位博主的文章
2.保存Bitmap位图为图片
/** 保存为图片 */
fun saveImage(view: View) {
// 调用上一步的转换位图函数
val imageBit = createViewBitmap(view)
// 保存至系统图库
MediaStore.Images.Media.insertImage(contentResolver,imageBit, "picture_name", "demo_saveImage")
}
MediaStore.Images.Media.insertImage(contentResolver, 参数1, 参数2, 参数3)
也是一个内置的方法,在Activity中可以直接调用,contentResolver是固定传入对象。
参数1:位图对象Bitmap
参数2:图片名称,可以在系统图库的详情页面查看到
参数3:图片的描述信息
3.设置点击事件
// 布局XML文件在后文
// 分别绑定根布局和”保存图片”按钮
val layout = findViewById<ConstraintLayout>(R.id.layout)
val button = findViewById<Button>(R.id.save_button)
// 设置“保存图片”按钮的点击
button.setOnClickListener {
saveImage(layout)
}
MainActivity的完整代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 分别绑定根布局和”保存图片”按钮
val layout = findViewById<ConstraintLayout>(R.id.layout)
val button = findViewById<Button>(R.id.save_button)
// 设置“保存图片”按钮的点击
button.setOnClickListener {
saveImage(layout)
}
}
/** 创建位图 */
fun createViewBitmap(view: View): Bitmap? {
val bitmap = Bitmap.createBitmap(
view.width, view.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
/** 保存为图片 */
fun saveImage(view: View) {
val imageBit = createViewBitmap(view)
MediaStore.Images.Media.insertImage(contentResolver,imageBit, "picture_name", "demo_saveImage")
}
}
4.真机测试结果
二、布局设置
本文使用约束布局,简单的设置了1个文本框,1个“保存图片”按钮。
1.设置“保存图片”按钮id为:save_button
2.设置根布局id为:layout
3.设置根布局的背景颜色为系统自带的白色:@color/white
(第3步不是必须的,只是因为若不设置背景颜色,则默认透明,图片背景将显示为黑色,不利于观察)
activity_main.xml文件的完整代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="@+id/layout"
android:background="@color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/save_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存图片"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>
三、补充说明
之所以无需声明存储权限,是因为此图片存储于共享存储空间中,这是系统提供的公共目录,由系统进行管理。详情敬请参阅官方说明文档官方说明文档官方说明文档
在其他博主的文章中有反映到,此方法生成的图片不能在图库中立刻查看到,还需进行广播,通知图库进行更新,但实际测试结果中并未出现此问题。
MediaStore.Images.Media.insertImage(contentResolver, 参数1, 参数2, 参数3)
此方法在 API level 29 以上实际已被弃用,但并不是因为不安全而被弃用,此方法依旧是可以正常使用的,只是谷歌官方推出了可以更好控制生命周期的方法,有机会再写文章进行说明。
除此方法外,还可将指定位置下的图片保存至系统图库。
MediaStore.Images.Media.insertImage(contentResolver, "image path", "title", "description")
如有任何疑问,请在评论区留言,我会努力回复。
demo源码链接如下: