文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android 玩转Glide4---Transformation篇

2022-06-06 14:02

关注

前言

系列文章专栏: 玩转Glide4
基础使用篇:Android 玩转Glide4—基础使用篇
进阶使用篇:Android 玩转Glide4—进阶使用篇
Transformation篇:Android 玩转Glide4—Transformation篇

概述

再基础篇和进阶篇中,我们简单介绍了Glide4的用法,和一些进阶的使用。
本篇Transformation转换篇,将给大家介绍Glide4强大的转换功能。

Glide自带的转换效果
 
Glide.with(this).load(ConstUrl.ImgUrl).into(ivScaleType)

如上代码,经常使用Glide的会发现一个问题:
对于一个宽高自适应的ImageView,并且不指定scaleType,即使加载的图片分辨率小于手机屏幕,图的宽度还是会铺满屏幕。
如下所示:
在这里插入图片描述

为什么会这样呢?

我们看一下into()方法的源码:

 public ViewTarget into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
         null,
        requestOptions);
  }

通过源码我们发现,Glide会获取ImageView的scaleType,进行centerCrop(),fitCenter(),centerInside()三种转换(这三个方法我们也可以RequestOptions直接调用)。
而我们知道在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER。

深究的Glide源码,会发现最后执行了fitCenter()方法,如果原图的宽高与ImageView的一致,不做任何改变,否则就是在保持纵横比不变的情况下对图片进行缩放。

 public static Bitmap fitCenter(@NonNull BitmapPool pool, @NonNull Bitmap inBitmap, int width,
      int height) {
    if (inBitmap.getWidth() == width && inBitmap.getHeight() == height) {
      return inBitmap;
    }
    final float widthPercentage = width / (float) inBitmap.getWidth();
    final float heightPercentage = height / (float) inBitmap.getHeight();
    final float minPercentage = Math.min(widthPercentage, heightPercentage);
    // Round here in case we've decoded exactly the image we want, but take the floor below to
    // avoid a line of garbage or blank pixels in images.
    int targetWidth = Math.round(minPercentage * inBitmap.getWidth());
    int targetHeight = Math.round(minPercentage * inBitmap.getHeight());
    if (inBitmap.getWidth() == targetWidth && inBitmap.getHeight() == targetHeight) {  
      return inBitmap;
    }
    // Take the floor of the target width/height, not round. If the matrix
    // passed into drawBitmap rounds differently, we want to slightly
    // overdraw, not underdraw, to avoid artifacts from bitmap reuse.
    targetWidth = (int) (minPercentage * inBitmap.getWidth());
    targetHeight = (int) (minPercentage * inBitmap.getHeight());
    Bitmap.Config config = getNonNullConfig(inBitmap);
    Bitmap toReuse = pool.get(targetWidth, targetHeight, config);
    // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
    TransformationUtils.setAlpha(inBitmap, toReuse);
    Matrix matrix = new Matrix();
    matrix.setScale(minPercentage, minPercentage);
    applyMatrix(inBitmap, toReuse, matrix);
    return toReuse;
  }

centerInside(),centerCrop()的源码就不贴出来,有兴趣的可以自己查看。

方法 说明
fitCenter() 如果原图的宽高与ImageView的一致,不做任何改变;否则就是在保持纵横比不变的情况下对图片进行居中缩放。默认。
centerInside() 如果原图的宽高小于ImageView的宽高,则不做任何改变;否则执行fitCenter()
centerCrop() 如果原图的宽高小于ImageView的宽高,则不做任何改变;否则就缩放图像,以使图像的宽度与给定的宽度匹配,并且的高度大于图像的给定高度,反之亦然,然后裁剪较大的尺寸以与给定的尺寸匹配
那如何加载图片的原始尺寸呢?

在Glide3中我们可以使用dontTransform()方法,表示Glide在加载图片的过程中不进行图片变换。
但是在Glide4中该方法只会停止transform方法传入的转换,不再影响fitCenter(),centerInside(),centerCrop()。
而且dontTransform()会停止所有的变换操作,显然是不够的。

这种情况下我们只需要借助override()方法强制将图片尺寸指定成原始大小就可以了。

      val optionsScaleType = RequestOptions().override(Target.SIZE_ORIGINAL)
    Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsScaleType).into(ivScaleType)
圆形图片

圆形图片是app开发中是最常见需求,以往我们会使用自定义View的圆形图片,在Glide4中,一个方法即可实现。

       //圆形转换
        val optionsCircle = RequestOptions().circleCrop()
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsCircle).into(ivCircle)

在这里插入图片描述

Glide Transformations

借助Glide Transformations库,我们可以非常轻松的实现各种基本的图片变换,如裁剪变换、颜色变换、模糊变换等等。
如果你要更加高级的变换,比如GPU渲染等,它也能实现。基本能满足我们所有的日常开发需求。

库引用
repositories {
  jcenter()
}
dependencies {
  implementation 'jp.wasabeef:glide-transformations:4.x.x'
  // If you want to use the GPU Filters
  implementation 'jp.co.cyberagent.android:gpuimage:2.x.x'
}
基本转换

由于Glide4自带圆形转换方法circleCrop(),所以CropCircleTransformation就被废弃了。

高斯模糊

可传入模糊度,采样率两个参数。

 public BlurTransformation() {
    this(MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
  }
  public BlurTransformation(int radius) {
    this(radius, DEFAULT_DOWN_SAMPLING);
  }
  public BlurTransformation(int radius, int sampling) {
    this.radius = radius;
    this.sampling = sampling;
  }
        //高斯模糊
        val optionsBlur = RequestOptions().transform(BlurTransformation(15, 5))
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsBlur).into(ivBlur)
圆角矩形

可传入角度,外边距,圆角类型三个三个参数。
圆角矩形的CornerType,枚举了所有的可能性。

 public enum CornerType {
    ALL,
    TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
    TOP, BOTTOM, LEFT, RIGHT,
    OTHER_TOP_LEFT, OTHER_TOP_RIGHT, OTHER_BOTTOM_LEFT, OTHER_BOTTOM_RIGHT,
    DIAGONAL_FROM_TOP_LEFT, DIAGONAL_FROM_TOP_RIGHT
  }
  public RoundedCornersTransformation(int radius, int margin) {
    this(radius, margin, CornerType.ALL);
  }
  public RoundedCornersTransformation(int radius, int margin, CornerType cornerType) {
    this.radius = radius;
    this.diameter = this.radius * 2;
    this.margin = margin;
    this.cornerType = cornerType;
  }
   //圆角矩形
        val optionsRounded = RequestOptions().transform(RoundedCornersTransformation())
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsRounded).into(ivRounded)
灰度转换

ORZ…让我想起了前端时间清明节的首页灰度。

         //灰度转换
        val optionsGray = RequestOptions().transform(GrayscaleTransformation())
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsGray).into(ivGray)
裁剪变换
 public enum CropType {
    TOP,
    CENTER,
    BOTTOM
  }
  private int width;
  private int height;
  private CropType cropType = CropType.CENTER;
  public CropTransformation(int width, int height) {
    this(width, height, CropType.CENTER);
  }
  public CropTransformation(int width, int height, CropType cropType) {
    this.width = width;
    this.height = height;
    this.cropType = cropType;
  }
        //裁剪转换
        val optionsCrop = RequestOptions().transform(CropTransformation(200, 100, CropTransformation.CropType.TOP))
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsCrop).into(ivCrop)
        //正方形裁剪
        val optionsSquare = RequestOptions().transform(CropSquareTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSquare).into(ivSquare)
图形变换

这个就比较厉害了。

保留覆盖目标像素的源像素,丢弃其余的源像素和目标像素。

就是指定一个资源文件作为目标形状,加载出来的图片文件形状跟资源文件的形状完全一致。
这个转换理论上可以将图片加载为任何形状。

        //图形变换
        val optionsMask = RequestOptions().transform(MaskTransformation(R.drawable.mask_starfish))
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsMask).into(ivMask)
        val optionsMask1 = RequestOptions().transform(MaskTransformation(R.drawable.x))
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsMask1).into(ivMask1)

在这里插入图片描述

GPU转换

GPU渲染效果转换,非常炫酷屌炸天。
我按照源码的顺序都实现了一遍,直接看效果图。

       //高亮效果
        val optionsBrightness = RequestOptions().transform(BrightnessFilterTransformation(0.3f))
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsBrightness).into(ivBrightness)
        //滤镜效果
        val optionsContrast = RequestOptions().transform(ContrastFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsContrast).into(ivContrast)
        //虚幻效果
        val optionsInvert = RequestOptions().transform(InvertFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsInvert).into(ivInvert)
        //马赛克效果
        val optionsKuwahara = RequestOptions().transform(KuwaharaFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsKuwahara).into(ivKuwahara)
        //像素效果
        val optionsPixelation = RequestOptions().transform(PixelationFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsPixelation).into(ivPixelation)
        //漫画效果
        val optionsSepia = RequestOptions().transform(SepiaFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSepia).into(ivSepia)
        //铅笔画效果
        val optionsSketch = RequestOptions().transform(SketchFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSketch).into(ivSketch)
        //漩涡效果
        val optionsSwirl = RequestOptions().transform(SwirlFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSwirl).into(ivSwirl)
        //油画效果
        val optionsToon = RequestOptions().transform(ToonFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsToon).into(ivToon)
        //暗边效果
        val optionsVignette = RequestOptions().transform(VignetteFilterTransformation())
        Glide.with(this).load(ConstUrl.ImgOne).apply(optionsVignette).into(ivVignette)

在这里插入图片描述

自定义转换

当然这些肯定不可能满足所有的需求,比如我要实现一个聊天头饰效果,在所有的用户头像上加一个如下图的头饰效果。
在这里插入图片描述
我们想一下有了图形变换MaskTransformation我们基本可以实现所有的形状变换,MaskTransformation是在目标资源文件上绘制。
而我们这个需求是需要在目标资源文件下绘制,即将目标资源文件覆盖在加载图片上方。
分析需求后我们只需要修改MaskTransformation其中一行代码设置为

xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)
,即可实现将一个将图片覆盖在另一个图片上的需求需求。

PorterDuff.Mode有很多种模式,在Android低层的graphics包里面,有兴趣的同学多了解。

因此修改后的代码几乎跟MaskTransformation一致,如下:

class HeaddressTransformation constructor(private val maskId: Int) : BitmapTransformation() {
    private val VERSION = 1
    private val ID = "com.demon.glide4img.HeaddressTransformation.$VERSION"
    private val sMaskingPaint by lazy {
        Paint().apply {
            xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)
        }
    }
    override fun hashCode(): Int {
        return ID.hashCode() + maskId * 10
    }
    override fun equals(other: Any?): Boolean {
        return other is HeaddressTransformation &&
                other.maskId == maskId
    }
    override fun updateDiskCacheKey(messageDigest: MessageDigest) {
        messageDigest.update((ID + maskId).toByteArray(Key.CHARSET))
    }
    override fun transform(context: Context, pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
        val width = toTransform.width
        val height = toTransform.height
        val result: Bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888)
        Canvas(result).run {
            val mask = Utils.getMaskDrawable(context.applicationContext, maskId)
            mask.setBounds(0, 0, width, height)
            mask.draw(this)
            drawBitmap(toTransform, 0f, 0f, sMaskingPaint)
        }
        return result
    }
}

由于头饰是圆形的,我们又需要将加载的图片转换为圆形效果。RequestOptions的transforms方法可以同事加载多个Transformation,进行复合变换。

 public RequestOptions transforms(@NonNull Transformation... transformations) {
    return transform(new MultiTransformation(transformations),  true);
  }

最后使用如下:

    //圆形头饰效果
        val optionsHeaddress = RequestOptions().centerCrop().transforms(CropCircleTransformation(), HeaddressTransformation(R.drawable.pic_kehu_hg))
        Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsHeaddress).into(ivHead)

在这里插入图片描述

代码

GitHub: https://github.com/DeMonDemo/Glide4Img

参考

郭霖的专栏—Glide最全解析


作者:DeMonnnnnn


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯