Android图片添加高斯模糊背景实现方案

您所在的位置:网站首页 keyshot9添加背景图片 Android图片添加高斯模糊背景实现方案

Android图片添加高斯模糊背景实现方案

2023-06-16 10:09| 来源: 网络整理| 查看: 265

效果图

image.png 项目要求比例为非1:1 的图片则增加高斯模糊背景效果,事先并不知晓图片的宽高。项目中集成了两种图片框架Fresco和Glide

方案-:使用Fresco图片加载框架

使用两层ImageView的方式,上层显示原始图片,下层显示高斯模糊背景,xml 实现如下:(SHImageView, PhotoDraweeView为继承自SimpleDraweeView实现的图片控件,封装了一些项目中常用的属性, SimpleDraweeView为Fresco的图片显示控件)

首先加载原始图片,获取到图片的宽高,判断图片宽高比是否为1:1

url?.let { loadImage(it, img) { isBlur, imageInfo -> //如果图片宽高比是非1:1的图片, 就设置高斯模糊背景 if (isBlur) { blurImg.resetVisibility(true) loadBlurImage(imageInfo, it, blurImg) } else { blurImg.resetVisibility(false) } } } private fun loadImage(url: String, image: PhotoDraweeView, doBlurImage: ((isBlur: Boolean, imageInfo: ImageInfo?)-> Unit)?) { val requestBuilder = ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)) val request = requestBuilder.build() val controller = Fresco.newDraweeControllerBuilder() .setAutoPlayAnimations(true) .setOldController(image.controller) .setControllerListener(object : BaseControllerListener() { override fun onFinalImageSet( id: String, imageInfo: ImageInfo?, animatable: Animatable? ) { super.onFinalImageSet(id, imageInfo, animatable) if (imageInfo == null) { return } image.update(imageInfo.width, imageInfo.height) val imgRatio = imageInfo.width/imageInfo.height val isBlur = imgRatio != 1 doBlurImage?.invoke(isBlur, imageInfo) } }) .setImageRequest(request) image.controller = controller.build() }

当图片宽高比不等于1:1 时,给图片的叠加一层高斯模糊背景,高斯模糊图片也使用Fresco实现 第一种方案就实现完成了,但是发现这种方式显示的交互效果并不友好,需要原图片加载完成之后再去加载高斯模糊背景,就会导致图片比背景先显示出来,有一个背景的白屏闪动。

private fun loadBlurImage(url: Uri, blurImg: PhotoDraweeView, isBlur: Boolean) { val requestBuilder = ImageRequestBuilder.newBuilderWithSource(url) requestBuilder.postprocessor = IterativeBoxBlurPostProcessor(6, 50) val request = requestBuilder.build() Fresco.getImagePipeline().prefetchToBitmapCache(request, context) val controller = Fresco.newDraweeControllerBuilder() .setLowResImageRequest(request) .setImageRequest(request) .setOldController(blurImg.controller) .setControllerListener(object : BaseControllerListener() { override fun onFinalImageSet( id: String, imageInfo: ImageInfo?, animatable: Animatable? ) { super.onFinalImageSet(id, imageInfo, animatable) if (imageInfo == null) { return } blurImg.update(imageInfo.width, imageInfo.height) } }) blurImg.controller = controller.build() }

这里考虑到是否可以先通过缓存来解决问题,通过LruCache缓存图片,当打开页面的时候直接读取缓存,会比从网络加载要快,这里实现了BitmapLruCacheUtils 通过自定义LruCache缓存背景图片,除此之外,还可以先填充原图片,给原图片设置一个透明度去进行过渡,优化后的方案。

private fun loadBlurImage(imageInfo: ImageInfo?, url: String?, blurImg: SHImageView) { //优先从缓存中获取bitmap, 解决大图模式和详情页交互的时候高斯模糊背景闪烁问题 if (BitmapLruCacheUtils.instance.isBitmapCache(url)) { BitmapLruCacheUtils.instance.loadBitmap(url, blurImg) return } imageInfo?.let { if ((imageInfo as? CloseableStaticBitmap)?.underlyingBitmap != null && (imageInfo as? CloseableStaticBitmap)?.underlyingBitmap?.isRecycled == false) { blurImg.setImageBitmap((imageInfo as? CloseableStaticBitmap)?.underlyingBitmap) blurImg.imageAlpha = 125 } ThreadUtils.executeByIo(object : ThreadUtils.SimpleTask() { override fun doInBackground(): Bitmap? { var realBitmap = (imageInfo as? CloseableStaticBitmap)?.underlyingBitmap?.let { Bitmap.createScaledBitmap(it, imageInfo.width / 5, imageInfo.height / 5, false) } if (realBitmap != null && !realBitmap.isRecycled) { NativeBlurFilter.iterativeBoxBlur(realBitmap, 6, 20) //将bitmap放入缓存 BitmapLruCacheUtils.instance.putBitmapToCache(url, realBitmap) return realBitmap } return null } override fun onSuccess(result: Bitmap?) { if (result != null && !result.isRecycled) { blurImg.imageAlpha = 255 blurImg.setImageBitmap(result) } } }) } }

到这里方案一的实现就完成了,但是并不能完全解决背景后加载显示的问题

方案二:使用Glide实现

Glide的transform,可以进行图片变换,那么我们可以利用transform在图片加载过程中进行处理,拿到原图片的bitmap后,对bitmap进行高斯模糊处理,然后将原bitmap 和高斯模糊后的bitmap合成为一个bitmap再返回显示。

实现BitmapBlurTransformation 继承自BitmapTransformation

/** * @desc: 图片高斯模糊转换合成类 * @author: wzc * @date: 2023/3/7 **/ class BitmapBlurTransformation( val radius: Int, val sampling: Int, private val bitmapWidth: Int, private val bitmapHeight: Int ): BitmapTransformation() { companion object { private const val ID = "com.module.commdity.blur.BitmapBlurTransformation" private val CHARSET: Charset = Charset.forName(STRING_CHARSET_NAME) } override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap { val imgRatio = toTransform.width/toTransform.height.toFloat() if (imgRatio == 1.0f) { return toTransform } //非1:1的图片实现高斯模糊 var composeBitmap: Bitmap = pool[toTransform.width / sampling, toTransform.height / sampling, Bitmap.Config.ARGB_8888] if (composeBitmap == null) { composeBitmap = Bitmap.createBitmap(toTransform.width / sampling, toTransform.height / sampling, Bitmap.Config.ARGB_8888) } return composeBitmap(toTransform, composeBitmap) } override fun updateDiskCacheKey(messageDigest: MessageDigest) { messageDigest.update((ID + radius + sampling).toByteArray(CHARSET)) } override fun toString(): String { return "BitmapBlurTransformation(radius=$radius, sampling=$sampling)" } override fun equals(other: Any?): Boolean { return other is BitmapBlurTransformation && other.radius == radius && other.sampling == sampling } override fun hashCode(): Int { return ID.hashCode() + radius * 1000 + sampling * 10 } /** * 两个bitmap合成为一个bitmap */ private fun composeBitmap(source: Bitmap, composeBitmap: Bitmap): Bitmap { try { val canvas =Canvas(composeBitmap) canvas.scale(1.0f / sampling.toFloat(), 1.0f / sampling.toFloat()) val paint = Paint() paint.flags = Paint.FILTER_BITMAP_FLAG paint.isAntiAlias = true canvas.drawBitmap(source, 0.0f, 0.0f, paint) NativeBlurFilter.iterativeBoxBlur(composeBitmap, sampling, radius) val newBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, source.config) val newCanvas = Canvas(newBitmap) val scaleGlideBitmap = scaleBlurBitmap(composeBitmap) val scaleResourceBitmap = scaleOriginBitmap(source) newCanvas.drawBitmap(scaleGlideBitmap, 0.0f, 0.0f, paint) //获取原图片bitmap的宽高 val originWidth = source.width val originHeight = source.height //如果宽大于高,计算绘制原图片的位置top 点位置 //(bitmapHeight - scaleResourceBitmap.height) / 2.toFloat() if (originWidth > originHeight) { newCanvas.drawBitmap(scaleResourceBitmap, 0f, (bitmapHeight - scaleResourceBitmap.height) / 2.toFloat(), paint) } else { //如果高大于宽,则计算绘制原图片的位置left 点位置 //(bitmapWidth - scaleResourceBitmap.width) / 2.toFloat() newCanvas.drawBitmap(scaleResourceBitmap, (bitmapWidth - scaleResourceBitmap.width) / 2.toFloat(), 0f, paint) } newCanvas.save() //保存 newCanvas.restore() //存储 return newBitmap } catch (e: Exception) { e.printStackTrace() //异常返回原图 return source } } /** * 计算原图片真实的宽高比例,而不是控件的宽高 */ private fun scaleOriginBitmap(bkg: Bitmap): Bitmap { val originWidth = bkg.width val originHeight = bkg.height val scaleWidth = bitmapHeight.toFloat() / originHeight val scaleHeight = bitmapWidth.toFloat() / originWidth val matrix = Matrix() if (originWidth > originHeight) { matrix.postScale(scaleHeight, scaleHeight) } else { matrix.postScale(scaleWidth, scaleWidth) } return Bitmap.createBitmap( bkg, 0, 0, bkg.width, bkg.height, matrix, true ) } /** * 拉伸背景图片的宽高为控件的显示宽高 */ private fun scaleBlurBitmap(bitmap: Bitmap): Bitmap { val scaleWidth = bitmapWidth.toFloat() / bitmap.width val scaleHeight = bitmapHeight.toFloat() / bitmap.height val matrix = Matrix() matrix.postScale(scaleWidth, scaleHeight) return Bitmap.createBitmap( bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true ) } }

然后通过Glide 加载图片显示

/** * 带高斯模糊背景的图片 */ private fun loadCompressImage(binding: DetailItemLevelOneChannelListBinding, url: String?) { Glide.with(context) .load(url) .apply(RequestOptions().transform(BitmapBlurTransformation(20, 6, dp2px(68f), dp2px(68f)))) .into(binding.detailItemChannelImage) }

两种方案对比下来,Glide的效果实现效果更好,不会出现图片和背景显示不一致的问题。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3