缩放图像在 phone 上无法正常工作

Scaling Image doesn't work properly on phone

我想画一张背面有图案的扑克牌。我有 1 个大小的可绘制对象并以编程方式缩放这些可绘制对象。

它在我的平板电脑(Pixel C,运行 Android 8.1)上完美运行,但卡片背面的设计在我的phone(Pixel 3a, 运行 Android 10.) 在 Pixel 3a 上,图像比预期的大得多,并且没有位于卡片背面的中央。此外,卡本身似乎可以适当缩放,只是放在上面的设计不行。

编辑:这似乎是一个 OS 级别的问题。我有一个 API 29(Android 10,与我的物理 phone 相同)的模拟 Pixel 3a,看起来与下面我的物理 phone 的屏幕截图相同。但是当我用 API 27 (Android 8.1) 创建 Pixel 3a 的模拟器时,它看起来就像在平板电脑上 运行 一样 OS。任何想法为什么?似乎是 OS 级别的错误,但我不确定哪个函数包含该问题,或者如何重现它。

编辑 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)
    }

这是它在平板电脑上的样子:

与 phone 上的对比:

注意:我不确定为什么图像如此之大,或者如何在此处缩放它们。

你 R.dimen.card_max_height 使用的单位是什么?

是dp还是px? 这很重要,因为如果您使用 px,它可以在具有不同分辨率的不同屏幕上为您提供截然不同的结果。 如果你使用 dp,最好使用这样的东西

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

(抱歉 java 版本)

不考虑不同的屏幕尺寸和分辨率,不能只将其转换为 int。

And also... I noticed you used

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

I think this is not recommended in Kotlin. It defeats Kotlin supposedly having gotten rid of the nullpointerexception, you should check if it doesn't return null and then perform the operation

我找到了一种让它工作的方法,部分要感谢@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)
    }

我做了很多实验才弄清楚需要转换什么,需要转换成哪种方式,但我想通了:-)