panpf / sketch

Sketch is an image loading library designed for Compose Multiplatform and Android View. It is powerful and rich in functions. In addition to basic functions, it also supports GIF, SVG, video thumbnails, Exif Orientation, etc.
Apache License 2.0
2k stars 307 forks source link

feat: 加载指定宽度,高度不定的小图片 #185

Closed RavenLiao closed 10 months ago

RavenLiao commented 10 months ago

Sketch版本:3.2.4

当前业务遇到一个场景: 一个ImageView占满屏幕宽度,但它的高度是wrap_content。 现在希望图片是占满ImageView的宽度显示的。 但对于宽度小于View宽度的图片,目前Sketch貌似无法实现一步加载完成。问一下大佬有没有思路?

我目前做法是使用Sketch加载Bitmap,再手动等比例放缩Bitmap到指定View的宽度,再set进ImageView中来解决。想知道有没有更好的方法。

看到sketch有resize方法,但只能指定确定的尺寸,像我这种一边定另一边不定的好像没法实现。或许可以用一个常量来代表是wrap_content?

panpf commented 10 months ago

你是要用在那种 RecyclerView 中竖着滚动图片的漫画场景吗?

你的这个需求我也想不到好的解决办法,因为 ImageView 没有类似 compose Image 那种 FillWidth 的选项

我能想到的就两种方案:

  1. 不修改 bitmap,scaleType 设为 Matrix,在加载完成后计算能充满宽的 Matrix 并设置,然后根据缩放后的高度修改 ImageView 的高度
  2. 修改 bitmap,scaleType 同样设置为 Matrix,ImageView 高度始终为 wrap 即可,加载图片时自定义一个 Transformation,在 Transformation 中将 bitmap 按 ImageView 的宽度等比例放大即可

个人推荐不修改 bitmap 的方案,因为内存占用小

RavenLiao commented 10 months ago

你是要用在那种 RecyclerView 中竖着滚动图片的漫画场景吗?

你的这个需求我也想不到好的解决办法,因为 ImageView 没有类似 compose Image 那种 FillWidth 的选项

我能想到的就两种方案:

  1. 不修改 bitmap,scaleType 设为 Matrix,在加载完成后计算能充满宽的 Matrix 并设置,然后根据缩放后的高度修改 ImageView 的高度
  2. 修改 bitmap,scaleType 同样设置为 Matrix,ImageView 高度始终为 wrap 即可,加载图片时自定义一个 Transformation,在 Transformation 中将 bitmap 按 ImageView 的宽度等比例放大即可

个人推荐不修改 bitmap 的方案,因为内存占用小

场景和你说的差不多,主要是竖向滚动图片。

那Request里面的resize方法是怎么实现的呢?能否通过它来做到类似的效果

panpf commented 10 months ago

resize 需要提前设置大小,你这不是无法提前得知

RavenLiao commented 10 months ago

resize 需要提前设置大小,你这不是无法提前得知

获取完图片后,不能做进一步处理么? 获取完图片后,可以知道图片宽高,再根据resize设置的单边限定,等比放缩应该可以吧。 resize不是这样发挥作用的么?

panpf commented 10 months ago

那你这不还是调用了两次加载图片吗,既然第一次加载后都已经知道宽高了,又何必再计算 resize 再调一次呢,直接算 Matrix 就可以了

RavenLiao commented 10 months ago

那你这不还是调用了两次加载图片吗,既然第一次加载后都已经知道宽高了,又何必再计算 resize 再调一次呢,直接算 Matrix 就可以了

imageView.displayImage(url){
resize(imageView.width, weap_content)
}

那有没有可能以这种形式直接实现一次加载

panpf commented 10 months ago

完全可以啊,默认 resize 只用来计算 inSampleSize,你自定义一个 Transformation 去识别你的特殊 resize,然后修改 bitmap 即可

panpf commented 10 months ago

现在的逻辑就是这样,你可以根据你的需求,去自定义各个环节,去实现你的需求

RavenLiao commented 10 months ago

现在的逻辑就是这样,你可以根据你的需求,去自定义各个环节,去实现你的需求

好的, 我试试看,谢谢大佬

RavenLiao commented 10 months ago

写了一下, 测试能达到效果。问一下大佬还有可以优化的地方不?

     class FillWidthTransform(@Px val width: Int) : Transformation {
        override val key: String
            get() = "FillWidth($width)"

        private val paint = Paint().apply {
            isAntiAlias = true
            isFilterBitmap = true
        }
        private val srcRect = Rect()
        private val destRect = Rect()

        override suspend fun transform(
            sketch: Sketch,
            requestContext: RequestContext,
            input: Bitmap
        ): TransformResult {
            val newHeight: Int = input.height * width / input.width
            val outBitmap = sketch.bitmapPool.getOrCreate(
                width = width,
                height = newHeight,
                config = input.config ?: Bitmap.Config.ARGB_8888,
                disallowReuseBitmap = requestContext.request.disallowReuseBitmap,
                caller = "FillWidthTransform"
            )
            srcRect.set(0, 0, input.width, input.height)
            destRect.set(0, 0, width, newHeight)
            val canvas = Canvas(outBitmap)
            canvas.drawBitmap(input, srcRect, destRect, paint)
            return TransformResult(outBitmap, "FillWidthTransformed($width)")
        }
    }

    //用例
        with(imageView){
            post {
                displayImage(url){
                    transformations(FillWidthTransform(imageView.width))
                }
            }
        }
panpf commented 10 months ago

是这么个意思,没啥可优化的了👍🏻