基于Glide自定义实现ImageView添加图片渐变倒影

篇章目标要点

Glide是目前最为流行的图片加载框架,内部提供了完善的缓存机制和内存管理机制,此前已经通过3篇源码解析文章进行了分析。Glide的强大之处更在于其内部提供了丰富的图片转换接口,比如增加圆角,图片裁剪,高斯模糊等。但是Glide提供的图片处理的场景也是有限的,在开发过程中我们难免存在需要自行开发图片处理的场景。但是图片处理过程涉及Bitmap的创建和转换过程,如果处理不当则容易产生内存溢出,导致应用崩溃。那么此类需要进行自定义的场景我们则可以考虑如何充分复用Glide原来的功能和机制,降低自己的代码风险。本文计划结合如何实现带倒影的图片为例,阐述通过继承BitmapTransformation实现带倒影的图片。

适用场景

Glide原有的图片转换功能不能满足业务要求,需要自定义的场景。尽可能复用Glide原有框架,减少代码开发工作量和降低代码风险。

Glide原有转换工具的启示

以带圆角的图片实现过程为例,我们看下Glide原始代码是如何实现的。其基本思路是实现透明背景的画布,然后裁剪图片

  @Override
  protected Bitmap transform(
      @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
      //toTransform为原图
    return TransformationUtils.roundedCorners(pool, toTransform, roundingRadius);
  }
...
//生产圆角图片的核心代码
  private static Bitmap roundedCorners(
      @NonNull BitmapPool pool, @NonNull Bitmap inBitmap, DrawRoundedCornerFn drawRoundedCornerFn) {

    // Alpha is required for this transformation.
    Bitmap.Config safeConfig = getAlphaSafeConfig(inBitmap);
    Bitmap toTransform = getAlphaSafeBitmap(pool, inBitmap);
    //创建目标图片
    Bitmap result = pool.get(toTransform.getWidth(), toTransform.getHeight(), safeConfig);

    result.setHasAlpha(true);
    //设置背景画布为透明,可以确保被裁剪的圆角位置无背景色
    BitmapShader shader =
        new BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setShader(shader);
    RectF rect = new RectF(0, 0, result.getWidth(), result.getHeight());
    BITMAP_DRAWABLE_LOCK.lock();
    try {
      Canvas canvas = new Canvas(result);
      canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
      //将图片裁剪为带圆角的矩形
      drawRoundedCornerFn.drawRoundedCorners(canvas, paint, rect);
      //转换完毕清空画布
      clear(canvas);
    } finally {
      BITMAP_DRAWABLE_LOCK.unlock();
    }

    if (!toTransform.equals(inBitmap)) {
      pool.put(toTransform);
    }

    return result;
  }

自定义转换类实现图片倒影

完成的效果图如下
在这里插入图片描述
在这里插入图片描述

(1)参照Glide源码思路自定义BitmapTransformation
本处自定义内容较为简单,主要内容是为转换类型定义一个唯一ID和引用生成带倒影图片的逻辑。代码如下

public class ReflectTransformation extends BitmapTransformation {
    /**
     * 为了符合Glide缓存机制的需要,需要针对不同类型的转换设置一个唯一ID
     */
    private static final String TRANSFORMATIONS_ID = "com.bumptech.glide.transformations.ReflectTransformation";
    private static final byte[] TRANSFORMATIONS_ID_BYTES = TRANSFORMATIONS_ID.getBytes(Charset.forName("UTF-8"));
    private static final String TAG = "ReflectTransformation";

    /**
     * 此处设置计划进行的Bitmap转换的逻辑
     * @param pool
     * @param toTransform 原始的Bitmap
     * @param outWidth ImageView的宽度
     * @param outHeight ImageView的高度
     * @return 返回转化后的Bitmap
     */
    @Override
    protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        Log.d(TAG,"prepare transform picture");
        return ReflectionBitmapUtil.handleReflectBitmap(toTransform);
    }

    /**
     * 更新硬盘缓存的索引key值
     */
    @Override
    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
        messageDigest.update(TRANSFORMATIONS_ID_BYTES);
    }

    @Override
    public int hashCode() {
        return TRANSFORMATIONS_ID.hashCode();
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        return obj instanceof ReflectTransformation;
    }
}

(2)完成带倒影的Bitmap转换。
其基本动作是生产带倒影的图片,然后将倒影图片同渐变透明遮罩进行相交处理,将处理后的合成Bitmap与原图绘制在同一个Bitmap上即完成了任务

public class ReflectionBitmapUtil {
    //本体图片与倒影之间的间隔
    private static final int INTERVAL =2;
    //倒影高度占据本体高度的比例
    private static final float REFLECT_RATIO = 0.2f;

    /**
     * 生产带倒影的图片
     * @param bitmap 待处理的原图
     * @return 返回带倒影的Bitmap
     */
    public static Bitmap handleReflectBitmap(Bitmap bitmap){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        //创建带倒影图片的画布
        Bitmap canvasBitmap = Bitmap.createBitmap(width , (int)(height*(1f+REFLECT_RATIO)+INTERVAL) , bitmap.getConfig());
        Canvas canvas = new Canvas(canvasBitmap);
        canvas.drawBitmap(bitmap , 0 ,0 , null);
        //生成倒影Bimtap
        Matrix matrix = new Matrix();
        matrix.setScale(1 , -1);
        Bitmap reflect = Bitmap.createBitmap(bitmap,0 , (int)(height*(1f-REFLECT_RATIO)) , width , (int)(height*REFLECT_RATIO) , matrix ,false);
        canvas.drawBitmap(reflect , 0 , height+INTERVAL , null);
        //透明渐变色遮罩,与倒影进行相交
        LinearGradient gradient = new LinearGradient(0 ,0 ,0,reflect.getHeight() , 0x00ffffff , 0x50ffffff , Shader.TileMode.MIRROR);
        Paint paint = new Paint();
        paint.setShader(gradient);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
        canvas.drawRect(0,height+INTERVAL , width , canvasBitmap.getHeight() , paint);
        //回收倒影Bitmap和canvas画布
        if(!reflect.isRecycled()){
            reflect.recycle();
        }
        canvas.setBitmap(null);
        return canvasBitmap;
    }
}

(3)复用Glide方法调用
参照以下代码完全实现了沿用Glide方法和框架

public class MainActivity extends AppCompatActivity {
    //计划加载的图片
    private final static String PICTURE = "https://gss0.baidu.com/70cFfyinKgQFm2e88IuM_a/baike/pic/item/7ac880515fcd8e6c43a75b63.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageViewOrigin = findViewById(R.id.image_view_origin);
        ImageView imageViewNew = findViewById(R.id.image_view_new);
        //加载原图
        Glide.with(imageViewOrigin).load(PICTURE).into(imageViewOrigin);
        //加载转换后的图片(实现圆角+倒影)
        MultiTransformation multiTransformation = new MultiTransformation(new RoundedCorners(10),new ReflectTransformation());
        Glide.with(this).load(PICTURE).transform(multiTransformation).into(imageViewNew);
    }
}

学习心得

通过参照学习Glide内部图片转换工具的代码原理,能够掌握复用Glide框架的基本上开发我们自己需要的图片转换组件,这种方式相对于自行进行Bitmap转换,在代码效率和内存消耗方面会更具优势

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值