渲染到屏幕外的视图并保存屏幕截图

时间:2018-11-28 22:52:34

标签: android android-view android-bitmap android-pixel-copy

关于将屏幕外视图保存到位图有很多问题,而我却一无所获,这就是为什么我不认为这两者都是重复的。

背景

我的应用提供了相同数据的两种不同视图。这两个视图都是使用按设计方式工作的自定义视图来呈现的(一个视图使用的是SurfaceView,这在这种情况下很难做到)。要求以包含两个视图的图像的形式共享此数据(一次只有一个视图对用户可见,他们可以在视图之间切换)。为了改善用户体验,并且无论使用什么设备,都为相同的数据生成相同的图像,该数据每次都应包含在固定大小的位图(PNG)中。

方法

为了完成上述任务,我已经

  • 在下面创建了fragment_screenshot_generator.xml布局:(水平LinearLayout的宽度和高度设置为match_parent,其中包含2个FrameLayouts,每个高度为match_parentlayout_weight(共1个)占整个屏幕的一半。出于测试目的,它们具有半透明的背景(分别为红色和绿色)
  • 创建了一个DialogFragment(进度对话框),该对话框应在后台完成此工作,并在屏幕截图保存到文件后自动关闭(将文件返回到父片段)
  • 在创建对话框的同时,我给fragment_screenshot_generator.xml充气并在每个FrameLayout中设置了两种视图类型。此处使用的代码与生成原始用户可见视图的代码完全相同。
  • 然后将视图渲染到具有位图支持的画布上
  • 位图被压缩为文件

问题

除了视图背景外,什么都不会渲染到位图上。我怀疑我在某个地方错过了一个电话来告诉fragment_screenshot_generator.xml的根“一切都很好” ,以便其中的视图可以呈现。我尝试过将其发布到布局根目录,但这是非常不可预测的,有时会花费很长时间,并且除了透明性外,什么也没有画。我必须将此布局附加到窗口吗?我可以将其附加到当前窗口而不显示它吗?您是否在我的代码中看到任何遗漏?

注释

我尝试过

  1. 在绘图调用之前启用图形缓存。没有修复。无论如何,它已被弃用。
  2. 直接从视图的绘制缓存中生成位图。没有修复。抛出NullPointerException,因为getDrawingCache方法返回null。无论如何仍然不推荐使用。
  3. 在屏幕上呈现完全相同的视图(作为对话框布局的一部分),并将其保存到位图。它可以工作,但是SurfaceView部分为空。这是可以预期的,因为SurfaceView本质上是一个'hole'。我怀疑PixelCopy API不会因此受到影响。
  4. 为简单起见,我从下面的代码中删除了后台执行。所以不要对我在主线程上运行所有这些大吼大叫! :)

代码

fragment_screenshot_generator.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false"
    android:orientation="horizontal">

    <FrameLayout
        android:id="@+id/view_1"
        android:background="#4f00"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <FrameLayout
        android:id="@+id/view_2"
        android:background="#40f0"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
</LinearLayout>

ScreenshotGeneratorDialogFragment#onCreateDialog

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    viewModel = ViewModelProviders.of(activity!!).get(DetailViewModel::class.java)

    val parameter1 = viewModel.dataTypeOne.value!!.parameter1
    val parameter2 = viewModel.dataTypeOne.value!!.parameter2

    screenshotView = View.inflate(context, R.layout.fragment_screenshot_generator, null).apply {
        measure(View.MeasureSpec.makeMeasureSpec(SCREENSHOT_WIDTH, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(SCREENSHOT_HEIGHT, View.MeasureSpec.EXACTLY))
        layout(0, 0, measuredWidth, measuredHeight)

        setupViewTypeOne(findViewById(R.id.view_1)!!, parameter1, parameter2)
        setupViewTypeTwo(findViewById(R.id.view_2)!!, parameter1, parameter2, viewModel.plotData.value)
        layout(0, 0, measuredWidth, measuredHeight)
    }

    dialogView = View.inflate(context, R.layout.dialog_screenshot_generator, null).apply {
        findViewById<TextView>(R.id.progress_label)!!.setText(R.string.preparing_screenshots)
    }

    val dialog = AlertDialog.Builder(context!!)
            .setView(dialogView)
            .create()

    dialog.setOnShowListener { capture() }

    return dialog
}

ScreenshotGeneratorDialogFragment#capture

private fun capture():File {
    var bitmap = Bitmap.createBitmap(screenshotView.measuredWidth, screenshotView.measuredHeight, Bitmap.Config.ARGB_8888)
    var canvas = Canvas(bitmap)
    canvas.drawColor(0xffffffff.toInt())
    screenshotView.draw(canvas)

    // Saving to SDcard for testing. This will move to cache dir. 
    val file = File.createTempFile("share-", ".png", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES))

    file.outputStream().use {
        bitmap.compress(Bitmap.CompressFormat.PNG, 0, it)
    }

    return file
}

0 个答案:

没有答案