【发布时间】:2019-10-24 12:57:43
【问题描述】:
我来自以下问题(我问过): Saving the current view as a bitmap
为了提出这个问题,我会提供尽可能多的细节。
我的最终目标是编写一个包含一些信息的视图(稍后将来自 API,可能是包含大量文本的 JSOn 对象)。 到目前为止,我所做的是: 1) 创建自定义视图 2)使用我需要的信息在此自定义视图上绘制画布(canvas.drawText()) 3)把这个CustomView放在activity_main.xml上(参考它) 4) 在 MainActivity.kt 上实例化这个 CustomView (现在问题开始了) 5)将此CustomView转换为位图(使用扩展方法。 6)将转换后的CustomView保存到SD卡
但是,当我尝试保存它时,什么也没有发生。没有创建文件夹,LogCat 窗口上也没有任何内容(我正在检查文件\文件夹是否是使用 Android Studio 上的设备文件资源管理器创建的)。
阅读后我明白我应该有一个 ViewTreeObserver 来观察变化(例如:然后视图完成绘制)。我将此作为扩展方法添加到我的代码中(在 SO 上找到但现在找不到链接)但也没有改变。
为了将位图保存到内部存储,我从以下链接获得了方法: https://android--code.blogspot.com/2018/04/android-kotlin-save-image-to-internal.html (我只是调整了该方法,因为我需要使用 Bitmap 而不是可绘制对象)。
我错过了什么吗?据我所见,我正在做正确的事情来将位图保存到 SD。 (由于我发布的代码,问题很大) 信息: 使用 Android Studio 3.5.1 Kotlin 语言
我的activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<com.example.desenhanota.CustomView
android:id="@+id/MyCustomview"
android:layout_width="match_parent"
android:layout_height="442dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
ViewTreeObserver 扩展方法:
inline fun View.doOnGlobalLayout(crossinline action: (view: View) -> Unit) {
val vto = viewTreeObserver
vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
@SuppressLint("ObsoleteSdkInt")
@Suppress("DEPRECATION")
override fun onGlobalLayout() {
action(this@doOnGlobalLayout)
when {
vto.isAlive -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
vto.removeOnGlobalLayoutListener(this)
} else {
vto.removeGlobalOnLayoutListener(this)
}
}
else -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
} else {
viewTreeObserver.removeGlobalOnLayoutListener(this)
}
}
}
}
})
}
自定义视图文件(CustomView.kt)
class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
): View(context, attrs, defStyleAttr) {
private val textoGeral = Paint()
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
textoGeral.setColor(Color.BLACK)
canvas?.drawText("DRAW TEST ON CANVAS TEST TEST ", 0f, 120f, textoGeral)
}
}
主活动
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val outraView = CustomView(this)
outraView.doOnGlobalLayout {
try {
val bmp = outraView.fromBitmap()
val uri: Uri = saveImageToInternalStorage(bmp)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun saveImageToInternalStorage(bitmap :Bitmap):Uri{
// Get the context wrapper instance
val wrapper = ContextWrapper(applicationContext)
// The bellow line return a directory in internal storage
var file = wrapper.getDir("images", Context.MODE_PRIVATE)
file = File(file, "${UUID.randomUUID()}.jpg")
try {
val stream: OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
stream.flush()
stream.close()
} catch (e: IOException){ // Catch the exception
e.printStackTrace()
}
// Return the saved image uri
return Uri.parse(file.absolutePath)
}
}
编辑 1:我更改了用户 mhedman 在 cmets 上的建议。他提到我正在访问我的自定义视图的一个新实例,而不是已经从活动布局中绘制的那个。当我在 ViewTreeObsever 事件之外尝试时,我有一个异常说“宽度和高度必须> 0”。在 ViewTreeObserver 内部,什么也没有发生(不显示任何消息)。
使用建议更新代码:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val outraView = MyCustomView
outraView.doOnGlobalLayout {
val finalBmp = outraView.fromBitmap()
val uri: Uri = saveImageToInternalStorage(finalBmp)
}
【问题讨论】:
-
“没有创建文件夹”——你是如何确定的?您是否在 Android Studio 中使用设备资源管理器工具? “没有显示吐司”——您没有任何代码可以显示
Toast,至少就您问题中的代码而言。 -
@CommonsWare 我暂时删除了 toats,抱歉(我将编辑问题)。是的,我正在使用设备资源管理器工具来检查是否创建了任何文件\文件夹。
-
您的代码中没有任何内容会导致您的
outraView实际布局。例如,您没有将其添加到活动的视图层次结构中。如果你想要的只是一个位图,为什么不创建一个支持位图的Canvas并在其上绘制,跳过所有这些自定义视图的内容? -
您似乎没有尝试获取已经淹没的自定义视图,而是使用此行 val outraView = CustomView(this) 创建了新视图,而您应该从活动布局“com”访问该视图.example.desenhanota.CustomView"
-
@mhemdan 我明白你的意思,真的很有道理。让我在这里尝试从活动布局中访问我创建的视图(就像你提到的那样),我将使用结果编辑问题。
标签: android kotlin bitmap android-view