Рендеринг в внеэкранный режим и сохранение скриншота - PullRequest
0 голосов
/ 29 ноября 2018

Существует множество вопросов о сохранении закадрового представления в растровом изображении, и я следил за всем безрезультатно, поэтому не думаю, что это дубликат.

Фон

Мое приложение предлагает два разных представления одних и тех же данных.Оба эти представления представлены с использованием пользовательских представлений, которые работают как задумано (один использует SurfaceView, что является проблемой для такого рода вещей).Существует требование об обмене этими данными в виде изображения, которое включает в себя оба вида (пользователю одновременно виден только один вид, они могут переключаться между видами).Для улучшения взаимодействия с пользователем и того, что одно и то же изображение генерируется для одних и тех же данных независимо от используемого устройства, эти данные должны каждый раз содержаться в растровом изображении (ну, в формате PNG) фиксированного размера.

Метод

Чтобы выполнить вышеизложенное, я создал

  • Создан fragment_screenshot_generator.xml макет ниже: (Горизонтальный LinearLayout с установленными шириной и высотой)на match_parent, который содержит 2 FrameLayouts, каждый с высотой match_parent и layout_weight, равный 1. Каждый занимает половину экрана. Для целей тестирования они имеют полупрозрачный фон (соответственно красный и зеленый)
  • Создано DialogFragment (диалоговое окно прогресса), которое должно выполнять эту работу в фоновом режиме и автоматически закрывать после сохранения снимка экрана в файл (возвращая файл в родительский фрагмент)
  • Во время работы диалогового окнаСоздав, я надуваю fragment_screenshot_generator.xml и настраиваю два типа представлений в каждом из FrameLayout с. Здесь используется тот же код, что и для создания исходных видимых пользователю представлений.
  • Представлениекурица, отображаемая на холсте, подкрепленная растровым изображением
  • Растровое изображение сжимается в файл

Проблема

Ничего, кроме фона видаотображается на растровом изображении.Я подозреваю, что где-то пропущен вызов, чтобы сообщить корню fragment_screenshot_generator.xml, что "все хорошо" , чтобы представления внутри него отображались.Я попытался опубликовать в корне макета, но это очень непредсказуемо, иногда занимает очень много времени, и все же рисует только прозрачность.Нужно ли прикрепить этот макет к окну?Могу ли я прикрепить его к текущему окну, не показывая его?В моем коде что-то отсутствует?

Примечания

Я пробовал

  1. Включение кэша чертежа перед вызовом отрисовки.Не исправить.Это все равно не рекомендуется.
  2. Генерация растрового изображения непосредственно из кэша отрисовки представления.Не исправить.Выдает NullPointerException, потому что метод getDrawingCache возвращает null.Все равно не рекомендуется.
  3. Рендеринг точно такого же вида на экране (как часть макета диалога) и сохранение его в растровом изображении.Это работает, но часть SurfaceView пуста.Это ожидается, потому что SurfaceView - это, по сути, 'дыра' .Я подозреваю, что API PixelCopy не пострадает от этого.
  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
}
...