首页 > 解决方案 > 使用 Glide 4.11 “尝试使用回收的位图”时崩溃,转换会导致这种情况吗?

问题描述

我知道已经有很多与“尝试使用回收的位图”崩溃相关的问题,但没有一个对我有帮助。

细节:

我唯一能想到的就是转变。此项目中只有 2 个转换。

CircleTransformation(将图像剪辑为具有自定义半径的圆):

class CircleTransformation(private val radius: Float) : BitmapTransformation() {
    companion object {
        private const val ID = "com.project.transformation.circle"
        private val ID_BYTES: ByteArray = ID.toByteArray()
    }

    public override fun transform(pool: BitmapPool, source: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
        val paint = Paint()
        paint.isAntiAlias = true
        paint.shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)

        val halfWidth = source.width / 2f
        val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(output)
        canvas.drawCircle(
            halfWidth,
            (source.height / 2).toFloat(),
            halfWidth * radius,
            paint
        )
        return output
    }

    // Caching helpers
    override fun equals(other: Any?): Boolean {
        return other is CircleTransformation && other.hashCode() == hashCode()
    }
    override fun hashCode(): Int {
        return  ID.hashCode()
    }
    override fun updateDiskCacheKey(messageDigest: MessageDigest) {
        messageDigest.update(ID_BYTES)
    }
}

和 ClipWhiteTransformation (从图像中删除白色边框):

class ClipWhiteTransformation() : BitmapTransformation() {
    companion object {
        private const val ID = "com.project.transformation.clipWhite"
        private val ID_BYTES: ByteArray = ID.toByteArray()

        // Config
        const val white = 253       // White pixel, if all channels are equal or greater than this
        const val transparent = 50  // Transparent pixel, if Less than this
    }

    public override fun transform(pool: BitmapPool, source: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
        val width = source.width - 1
        val height = source.height - 1
        val halfX = width / 2
        val halfY = height / 2
        var startY = 0


        // Left Margin
        var left = 0
        for (x in 0 until halfX) {
            val pixel = source.getPixel(x, halfY)

            // Transparent?
            if (Color.alpha(pixel) < transparent) continue

            // Not white?
            if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
                left = x
                if (x > 2) {
                    startY = 2
                }
                break
            }
        }

        // Right Margin
        var right = 0
        for (x in 0 until halfX) {
            val pixel = source.getPixel(width - x, halfY)

            // Transparent?
            if (Color.alpha(pixel) < transparent) continue

            // Not white?
            if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
                right = x
                if (x > 2) {
                    startY = 2
                }
                break
            }
        }

        // Top Margin
        var top = 0
        for (y in startY until halfY) {
            val pixel = source.getPixel(halfX, y)

            // Transparent?
            if (Color.alpha(pixel) < transparent) continue

            // Not white?
            if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
                top = y
                break
            }
        }

        // Bottom Margin
        var bottom = 0
        for (y in startY until halfY) {
            val pixel = source.getPixel(halfX, height - y)

            // Transparent?
            if (Color.alpha(pixel) < transparent) continue

            // Not white?
            if (Color.red(pixel) < white || Color.green(pixel) < white || Color.blue(pixel) < white) {
                bottom = y
                break
            }
        }

        // Clip, scale and return
        val newWidth = width - (left + right)
        val newHeight = height - (top + bottom)
        val scale = if (abs(newWidth - outWidth) > abs(newHeight - outHeight)) outWidth / newWidth.toFloat() else outHeight / newHeight.toFloat()
        val matrix = Matrix().apply { setScale(scale, scale) }
        return Bitmap.createBitmap(source, left, top, newWidth, newHeight, matrix, false)
    }



    // Caching helpers
    override fun equals(other: Any?): Boolean {
        return other is ClipWhiteTransformation && other.hashCode() == hashCode()
    }
    override fun hashCode(): Int {
        return  ID.hashCode()
    }
    override fun updateDiskCacheKey(messageDigest: MessageDigest) {
        messageDigest.update(ID_BYTES)
    }
}

最初使用的是 BitmapPool,删除它并没有阻止崩溃。

顺便说一句,这是用于加载图像的扩展:

fun ImageView.setURL(url: String, 
                     @DrawableRes error: Int? = null, 
                     @DrawableRes placeholder: Int? = null, 
                     size: Int? = null, 
                     options: ((RequestBuilder<Drawable>) -> Unit)? = null, 
                     completion: ((resource: Drawable?) -> Unit)? = null) {

    // No URL, use Placeholder if exists, if not, use the error image
    if (url.isEmpty()) {
        placeholder?.also{ setImageResource(it) } ?: run { error?.also{ setImageResource(it) } }
        return
    }

    Glide.with(applicationInstance) // (I'm using an application instance directly here)
        .load(url).apply {
            completion?.also { completion ->
                this.listener(object : RequestListener<Drawable> {
                    override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
                        completion(null)
                        return false
                    }
                    override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
                        completion(resource)
                        return false
                    }
                })
            }
        }
        .apply { size?.also { this.override(it)} }
        .apply { options?.invoke(this) }
        .placeholder(placeholder ?: 0)
        .error(error ?: 0)
        .transition(DrawableTransitionOptions.withCrossFade(350))
        .into(this)
}

很抱歉在这里粘贴了这么多代码(希望对某人有用)。这些转换或加载程序会导致崩溃吗?

标签: androidkotlin

解决方案


塑造(circle/square/oval)你的形象 你不需要改变你的形象。 MaterialDesign已经介绍了ShapeableImageView它可以让你在运行时塑造你的图像,你也可以用颜色添加边框。

  1. 添加物质依赖:

    implementation 'com.google.android.material:material:1.3.0-alpha01'
    
  2. 在 xyz.xml 中添加 shapeableImageView:

    <com.google.android.material.imageview.ShapeableImageView
        android:id="@+id/imgStudent"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:shapeAppearanceOverlay="@style/circleImageView"
        android:padding="2dp"
        app:strokeColor="@color/white"
        app:strokeWidth="5dp"
        android:scaleType="centerCrop"
        android:adjustViewBounds="true"
        tools:srcCompat="@drawable/ic_kid_placeholder"
        />
    
  3. 在 res/values/style.xml 文件中添加样式

    <style name="circleImageView" parent="">
            <item name="cornerFamily">rounded</item>
            <item name="cornerSize">50%</item>
            <item name="android:shadowRadius">100</item>
            <item name="android:shadowColor">@color/gray</item>
            <item name="backgroundOverlayColorAlpha">12</item>
    </style>
    

最后用 glide 加载你的图像。


推荐阅读