Масштабирование изображения не работает должным образом на телефоне - PullRequest
0 голосов
/ 26 мая 2020

Я пытаюсь нарисовать игральную карту с рисунком на обратной стороне. У меня есть возможность рисования 1 размера и программно масштабировать эти чертежи.

Он отлично работает на моем планшете (Pixel C, работает Android 8.1), но дизайн задней части карты не масштабируется должным образом на моем телефоне (Pixel 3a, работает Android 10 .) На Pixel 3a изображение намного больше, чем ожидалось, и не отцентрировано на обратной стороне карты. Кроме того, кажется, что сама карта правильно масштабируется, но не дизайн, который на нее надет.

EDIT: Это похоже на проблему уровня ОС. У меня был эмулированный Pixel 3a с API 29 (Android 10, такой же, как у моего физического телефона), который выглядел так же, как снимок экрана с моего физического телефона ниже. Но когда я создал эмулятор Pixel 3a с API 27 (Android 8.1), похоже, что он работает на планшете с той же ОС. Есть идеи, почему ?? Похоже, что это ошибка на уровне ОС, но я не уверен, в какой функции содержится проблема или как именно ее воспроизвести.

РЕДАКТИРОВАТЬ 2: Похоже, API 27 - последний, который отображает, что Я ожидал увидеть. Я пробовал эмуляторы с API 28, 29 и R, и все они показывают изображение намного больше, чем я ожидал.

Вот код, который я запускаю:

    private fun createImageInImageCenter(context: Context, backgroundBitmap: Bitmap, bitmapToDrawInTheCenter : Bitmap) : Drawable {

        // Figure out width / height
        val resultBitmap = Bitmap.createBitmap(
            backgroundBitmap.width,
            backgroundBitmap.height,
            backgroundBitmap.config
        )

        val scaledCenter = scaleImage(bitmapToDrawInTheCenter, backgroundBitmap.height /2,
            backgroundBitmap.width / 2)

        val canvas = Canvas(resultBitmap)
        // Draw background
        canvas.drawBitmap(backgroundBitmap, Matrix(), Paint())
        // Draw design centered on top
        canvas.drawBitmap(
            scaledCenter,
            ((backgroundBitmap.width - scaledCenter.width) / 2).toFloat(), // left
            ((backgroundBitmap.height - scaledCenter.height) / 2).toFloat(), // top
            Paint()
        )

        return BitmapDrawable(context.resources, resultBitmap)
    }

    private fun scaleImage (image: Bitmap, maxHeight: Int, maxWidth: Int = -1) : Bitmap {
        var ratio = 1f

        if(maxWidth > 0 && image.width > maxWidth)
            ratio = maxWidth.toFloat() / image.width

        if(maxHeight > 0 && (image.height * ratio).roundToInt() > maxHeight)
            ratio = maxHeight.toFloat() / image.height

        val sizeX = (image.width * ratio).roundToInt()
        val sizeY = (image.height * ratio).roundToInt()

        return Bitmap.createScaledBitmap(image, sizeX, sizeY, false)
    }

    fun drawCard() {

        // Resize the card itself
        val cardHeight = context.resources.getDimension(R.dimen.card_max_height)
        val back = scaleImage(context.getDrawable(R.drawable.card_black)!!, cardHeight.toInt())

        // Resize the design on the card
        val image = scaleImage(context.getDrawable(R.drawable.triforce)!!, back.height / 2, back.width / 2)

        pic = createImageInImageCenter(context, back, image)
    }

Вот как это выглядит на планшете: tablet

vs как это выглядит на телефоне: phone

Примечание: я не уверен, почему изображения такие большие и как их здесь масштабировать.

Ответы [ 2 ]

1 голос
/ 27 мая 2020

Я нашел способ заставить его работать, отчасти благодаря @mmmcho за его ответ, а также благодаря другому ответу, который я нашел где-то еще с обеими функциями преобразования.

    fun convertDpToPixel(context: Context, dp: Float): Float {
        return dp * (context.resources
            .displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
    }

    fun convertPixelsToDp(context: Context, px: Float): Float {
        return px / (context.resources
            .displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
    }

    private fun createImageInImageCenter(context: Context, background: Drawable, bitmapToDrawInTheCenter : Drawable) : Drawable {

        val backgroundBitmap = background.toBitmap()
        // Figure out width / height
        val resultBitmap = Bitmap.createBitmap(
            backgroundBitmap.width,
            backgroundBitmap.height,
            backgroundBitmap.config
        )

        val canvas = Canvas(resultBitmap)

        // Draw background
        canvas.drawBitmap(backgroundBitmap, Matrix(), Paint())

        val scaledCenter = scaleImage(context, bitmapToDrawInTheCenter, resultBitmap.height / 2,
            resultBitmap.width / 2)

        // Do conversion from DP to Pixel for API 28+
        if(Build.VERSION.SDK_INT >= 28) {
            // Draw design on top
            canvas.drawBitmap(
                scaledCenter,
                (backgroundBitmap.width.toFloat() /2) - convertDpToPixel(context, scaledCenter.width.toFloat() / 2),
                (backgroundBitmap.height.toFloat() /2) - convertDpToPixel(context, scaledCenter.height.toFloat() / 2),
                Paint()
        )
        }
        else {
            // Draw design on top
            canvas.drawBitmap(
                scaledCenter,
                ((backgroundBitmap.width - scaledCenter.width) / 2).toFloat(), // left
                ((backgroundBitmap.height - scaledCenter.height) / 2).toFloat(), // top
                Paint()
            )
        }

        return BitmapDrawable(context.resources, resultBitmap)
    }

    private fun scaleImage (context : Context, image: Bitmap, maxHeight: Int, maxWidth: Int = -1) : Bitmap {
        var ratio = 1f

        // Do conversion from pixels to DP for API 28+
        if(Build.VERSION.SDK_INT >= 28)
        {
            val width = convertPixelsToDp(context, image.width.toFloat())
            val height = convertPixelsToDp(context, image.height.toFloat())
            val newMaxWidth = convertPixelsToDp(context, maxWidth.toFloat())
            val newMaxHeight = convertPixelsToDp(context, maxHeight.toFloat())

            if(newMaxWidth > 0 && width > newMaxWidth)
                ratio = newMaxWidth / image.width

            if(newMaxHeight > 0 && (height * ratio).roundToInt() > newMaxHeight)
                ratio = newMaxHeight / image.height
        }
        else {
            if (maxWidth > 0 && image.width > maxWidth)
                ratio = maxWidth.toFloat() / image.width

            if (maxHeight > 0 && (image.height * ratio).roundToInt() > maxHeight)
                ratio = maxHeight.toFloat() / image.height
        }

        val sizeX = (image.width * ratio).roundToInt()
        val sizeY = (image.height * ratio).roundToInt()

        return Bitmap.createScaledBitmap(image, sizeX, sizeY, false)
    }

    fun drawCard() {
        val back = context.getDrawable(R.drawable.card_black)!!

        // Image to draw on the back
        val image = context.getDrawable(R.drawable.triforce)!!

        pic = createImageInImageCenter(context, back, image)
    }

Это заняло много экспериментов, чтобы выяснить, что нужно преобразовать, и каким образом это нужно преобразовать, но я понял это: -)

1 голос
/ 26 мая 2020

Какую единицу вы использовали для R.dimen.card_max_height?

Это dp или px? Это важно, потому что если вы использовали пиксель, он может дать очень разные результаты на разных экранах с разным разрешением. Если вы использовали dp, лучше использовать что-то вроде этого

int px = Math.round(TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP, 14,r.getDisplayMetrics()));

(извините за java версию)

Вы не можете просто преобразовать его в int, не учитывая другой экран размеры и разрешения.

А также ... Я заметил, что вы использовали

context.getDrawable(R.drawable.triforce)!!

Я думаю, что это не рекомендуется в Kotlin. Он побеждает Kotlin, предположительно избавившись от исключения nullpointerexception , вы должны проверить, не возвращает ли он значение null, а затем выполнить операцию

...