Сделайте снимок экрана составной забавы программно в Jetpack Compose Android

Я хотел бы захватить пользовательский интерфейс, излучаемый Jetpack compose, в виде растрового изображения. В XML это было сделано так:

Обычно принимает представление в качестве входного параметра и возвращает его как растровое изображение.

      //take screenshot of the view added as an input argument
fun takeScreenShot(view: View) : Bitmap {
    val bitmap = Bitmap.createBitmap(
        view.width,
        view.height,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    view.draw(canvas)
    return bitmap
}

Что эквивалентно этому в Jetpack compose?

2 ответа

Решение

В тестах можно делать скриншоты из составного объекта .
Чтобы сделать снимки экрана в производственном коде, см. Этот вопрос и эту проблему ..

Во-первых, убедитесь, что у вас есть следующая зависимость в вашем сценарии сборки (наряду с другими необходимыми зависимостями Compose):

      debugImplementation("androidx.compose.ui:ui-test-manifest:<version>")

Примечание. Вместо указанной выше зависимости вы можете просто добавить AndroidManifest.xml в каталог androidTest и добавить следующее в manifest> application элемент: <activity android:name="androidx.activity.ComponentActivity" />.
Обратитесь к этому ответу .

Вот полный пример сохранения, чтения и сравнения снимков экрана:

      class ScreenshotTest {

    @get:Rule val composeTestRule = createComposeRule()

    @Test fun takeAndSaveScreenshot() {
        composeTestRule.setContent { MyComposableFunction() }
        val node = composeTestRule.onRoot()
        val screenshot = node.captureToImage().asAndroidBitmap()
        saveScreenshot("screenshot.png", screenshot)
    }

    @Test fun readAndCompareScreenshots() {
        composeTestRule.setContent { MyComposableFunction() }
        val node = composeTestRule.onRoot()
        val screenshot = node.captureToImage().asAndroidBitmap()

        val context = InstrumentationRegistry.getInstrumentation().targetContext
        val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val file = File(path, "screenshot.png")
        val saved = readScreenshot(file)

        println("Are screenshots the same: ${screenshot.sameAs(saved)}")
    }

    private fun readScreenshot(file: File) = BitmapFactory.decodeFile(file.path)

    private fun saveScreenshot(filename: String, screenshot: Bitmap) {
        val context = InstrumentationRegistry.getInstrumentation().targetContext
        // Saves in /Android/data/your.package.name.test/files/Pictures on external storage
        val path = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val file = File(path, filename)
        file.outputStream().use { stream ->
            screenshot.compress(Bitmap.CompressFormat.PNG, 100, stream)
        }
    }
}

Благодаря этому .

Я бы посмотрел, как это делает тестирование JP-Compose.

Хорошей отправной точкой может быть android-compose-codelab, см.:

      /**
 * Simple on-device screenshot comparator that uses golden images present in
 * `androidTest/assets`. It's used to showcase the [AnimationClockTestRule] used in
 * [AnimatingCircleTests].
 *
 * Minimum SDK is O. Densities between devices must match.
 *
 * Screenshots are saved on device in `/data/data/{package}/files`.
 */
@RequiresApi(Build.VERSION_CODES.O)
fun assertScreenshotMatchesGolden(
    goldenName: String,
    node: SemanticsNodeInteraction
) {
    val bitmap = node.captureToImage().asAndroidBitmap()
}

из ScreenshotComparator.kt . Ты можешь найти captureToImage()здесь, в AndroidHelpers.kt .

Также вы можете найти здесь ImageBitmap.kt , где только asAndroidBitmap ()проверяет , что основная «общая» версия ImageBitmap на самом деле android.graphics.Bitmap на Android (это сделано для того, чтобы код стал более независимым от платформы, чтобы его можно было запускать и на JVM / рабочем столе)