Recyclerview 九宫格加载图片控件变形及Glide 3.7 无法加载圆角问题解决(爬坑之旅)

睁开眼又是美好的一天,对于即将开始写代码的我,心情是这样滴

timg.jpg

戴上耳机听着自己喜欢的五月天的歌,写着让我感觉到安全的代码,我感觉这真是一件幸福的事情,一顿行云流水的操作,中午时分我终于搞定了主题框架,心里成就感还是满满的,然而我不知道的是,即将发生的,那让我陷入抓狂状态的“大坑”,更糟的是还不是一个坑,坑中带坑!

我要做的是一个图片九宫格的功能,点击还能缩放,本来这是一个老生常谈的功能,要是放在以前,我肯定二话不说,首选GridView 实现,但是GridView毕竟已经服役这么多年了,所谓长江后浪推前浪,一浪更比一浪强,我们的GridView在复用和效率上,已经远远不及后辈新秀RecyclerView.
所以,下午的时候我思忖了下,决定还是使用RecyclerView吧,毕竟顺滑,高效,才是王道。

我们先看RecyclerView 设置九宫格的第一个问题

  • 九宫格图片之间的上下左右间距
    还在用pindding 和margen 傻傻的在布局里调试吗?No,you out 啦,今天我们要用一个号称“万金油”的设置间距的方式。
            var spanCount = 3
            var spacing = 10
            val gridSpacingItemDecoration = GridSpacingItemDecoration(spanCount, spacing, true)
            picRecy.addItemDecoration(gridSpacingItemDecoration)

对,只需要只给你的Recyclerveiw设置上addItemDecoration就可以了,想要什么间距就可以写上身间距。

  • spacing 就是间距代表的实际像素,注意是px
  • spanCount 则是代表你有几列,告诉Recy是怎么帮你总体把控的
  • GridSpacingItemDecorationd的第三个参数是一个布尔类型的,传true表示包含边缘,false为不包含边缘
    对于还不熟悉ItemDecoration的童鞋,建议看一下https://www.jianshu.com/p/b46a4ff7c10a这篇文章
    Ok,至此,运行查看效果,显示的效果还是可以的,但是当你多次下拉刷新后,问题出现了,第二个坑来了:

为什么多次刷新后item之间的间隔反而变大了???

image.png

刚进来还是正常的,多次刷新后就成了这个样子了


image.png

为什么会变成这个样子呢?点击去看了一下,原来在内部,是有一个集合累积存储我们的间距的,这就导致了在多次刷新后,间距不断变大
那么我们只需要在构造函数里让设置间距的代码只执行一次就OK了,对于网上的移除间距的方法,建议不用,因为用了也是没效果的


        init {
            //设置间隔距离
            var spanCount = 3
            var spacing = 10
            val gridSpacingItemDecoration = GridSpacingItemDecoration(spanCount, spacing, true)
            picRecy.addItemDecoration(gridSpacingItemDecoration)
            var gridLayoutManager = GridLayoutManager(mContext, 3)
            picRecy.layoutManager = gridLayoutManager
        }

解决了这个小喽啰,我们再来看终极大坑Glide
Glide可是我们的老朋友了,话说,士别三日当刮目相看,但是这次,Glide的真的让我是“另眼相看”了,这么熟悉的老朋友,居然暗藏这么多大坑。

首先就是加载圆角圆形图片的问题

  • 圆形
    需要使用到下面的类
public class GlideCircleTransform extends BitmapTransformation {
    public GlideCircleTransform(Context context) {
        super(context);
    }

    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return circleCrop(pool, toTransform);
    }

    private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;
        int size = Math.min(source.getWidth(), source.getHeight());
        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;
        // TODO this could be acquired from the pool too
        Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
        Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        }
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        float r = size / 2f;
        canvas.drawCircle(r, r, r, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}

这个问题不大,无论是Glide 3.x的版本还是Glide 4.x的版本都无所谓,正常使用即可

  • 圆角
    需要使用下面的类

public class GlideRoundTransform extends BitmapTransformation {

    private static float radius = 0f;

    /**
     * 构造函数 默认圆角半径 4dp
     *
     * @param context Context
     */
    public GlideRoundTransform(Context context) {
        this(context, 4);
    }

    /**
     * 构造函数
     *
     * @param context Context
     * @param dp      圆角半径
     */
    public GlideRoundTransform(Context context, int dp) {
        super(context);
        radius = Resources.getSystem().getDisplayMetrics().density * dp;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return roundCrop(pool, toTransform);
    }

    private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;

        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName() + Math.round(radius);
    }

}

这个问题大了去了,在Glide3.x的时候我是经常使用的,加载的效果也是很完美,但是今天却不行了,我打开看了下,确认下我的Glide版本还没升级过,依旧是


image.png

很早以前的版本,但是因为工程的问题,一直也没更新。我心想着,没啥问题吧,于是就写了
···


···
然后代码里这样设置

  Glide.with(mContext)
                .load(picUrl)
                .placeholder(R.drawable.square_holder)
                .transform(GlideRoundTransform(mContext,10))
                .into(holder.pic)

我自认为是完美的,但是运行出来就傻眼了


image.png

为什么圆角没出现呢?而且我的Imageview明明是个正方形,怎吗还变形了,成了长方形?

一顿百度,各说各的理,我也只能一一尝试,但是效果都是不太理想,后来还是找到了问题所在:
是因为ImageView的 android:scaleType="centerCrop"和GlideRoundTransform的方法造成了冲突
顺着这个方向,又是一顿捣鼓:
把ImageView里的android:scaleType去掉,结果,各种各样奇怪的问题都来了:
有图片变形拉长的

image.png

有间距变大的
image.png

注意,图片上的圆角是我最后成功后为了复现bug而展示,并不是此刻已经完成的。

那么升级到最新的Glide 4.9.0版本,在把GlideRoundTransform改成最新的写法

public class GlideRoundedCornersTransform extends CenterCrop {
    private float mRadius;
    private CornerType mCornerType;
    private static final int VERSION = 1;
    private static final String ID = BuildConfig.APPLICATION_ID+"GlideRoundedCornersTransform." + VERSION;
    private static final byte[] ID_BYTES = ID.getBytes(CHARSET);


    public enum CornerType {
        ALL,
        TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
        TOP, BOTTOM, LEFT, RIGHT,
        TOP_LEFT_BOTTOM_RIGHT,
        TOP_RIGHT_BOTTOM_LEFT,
        TOP_LEFT_TOP_RIGHT_BOTTOM_RIGHT,
        TOP_RIGHT_BOTTOM_RIGHT_BOTTOM_LEFT,
    }

    public GlideRoundedCornersTransform(float radius, CornerType cornerType) {
        super();
        mRadius = UIUtils.dp2px(radius);//dp ->px
        mCornerType = cornerType;
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Bitmap transform = super.transform(pool, toTransform, outWidth, outHeight);
        return roundCrop(pool, transform);
    }

    private Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null) {
            return null;
        }
        int width = source.getWidth();
        int height = source.getHeight();
        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);


        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config
                    .ARGB_8888);
        }
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader
                .TileMode.CLAMP));
        paint.setAntiAlias(true);



        Path path = new Path();
        drawRoundRect(canvas, paint, path, width, height);

        return result;
    }

    private void drawRoundRect(Canvas canvas, Paint paint, Path path, int width, int height) {
         float[] rids ;
        switch (mCornerType) {
            case ALL:
                rids = new float[]{mRadius,mRadius,mRadius,mRadius,mRadius,mRadius,mRadius,mRadius};
                drawPath(rids,canvas, paint, path, width, height);
                break;
            case TOP_LEFT:
                rids = new float[]{mRadius,mRadius,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f};
                drawPath(rids,canvas, paint, path, width, height);
                break;
            case TOP_RIGHT:
                rids  = new float[]{0.0f,0.0f,mRadius,mRadius,0.0f,0.0f,0.0f,0.0f};
                drawPath(rids,canvas, paint, path, width, height);
                break;
            case BOTTOM_RIGHT:
                rids  = new float[]{0.0f,0.0f,0.0f,0.0f,mRadius,mRadius,0.0f,0.0f};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case BOTTOM_LEFT:
                rids  = new float[]{0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,mRadius,mRadius};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case TOP:
                rids = new float[]{mRadius,mRadius,mRadius,mRadius,0.0f,0.0f,0.0f,0.0f};
                drawPath(rids,canvas,  paint,  path,width, height);
                break;
            case BOTTOM:
                rids  = new float[]{0.0f,0.0f,0.0f,0.0f,mRadius,mRadius,mRadius,mRadius};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case LEFT:
                rids = new float[]{mRadius,mRadius,0.0f,0.0f,0.0f,0.0f,mRadius,mRadius};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case RIGHT:
                rids  = new float[]{0.0f,0.0f,mRadius,mRadius,mRadius,mRadius,0.0f,0.0f};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case TOP_LEFT_BOTTOM_RIGHT:
                rids  = new float[]{mRadius,mRadius,0.0f,0.0f,mRadius,mRadius,0.0f,0.0f};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case TOP_RIGHT_BOTTOM_LEFT:
                rids  = new float[]{0.0f,0.0f,mRadius,mRadius,0.0f,0.0f,mRadius,mRadius};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case TOP_LEFT_TOP_RIGHT_BOTTOM_RIGHT:
                rids  = new float[]{mRadius,mRadius,mRadius,mRadius,mRadius,mRadius,0.0f,0.0f};
                drawPath(rids,canvas,  paint, path, width, height);
                break;
            case TOP_RIGHT_BOTTOM_RIGHT_BOTTOM_LEFT:
                rids  = new float[]{0.0f,0.0f,mRadius,mRadius,mRadius,mRadius,mRadius,mRadius};
                drawPath(rids,canvas,  paint,  path,width, height);
                break;
            default:
                throw new RuntimeException("RoundedCorners type not belong to CornerType");
        }
    }


    /**@param rids 圆角的半径,依次为左上角xy半径,右上角,右下角,左下角*/
    private void drawPath(float[] rids,Canvas canvas,Paint paint,Path path, int width, int height) {
        path.addRoundRect(new RectF(0, 0, width, height), rids, Path.Direction.CW);
//        canvas.clipPath(path);
        canvas.drawPath(path,paint);
    }


    @Override
    public boolean equals(Object o) {
        return o instanceof GlideRoundedCornersTransform;
    }


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


    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        messageDigest.update(ID_BYTES);
    }
}

或者


public class GlideRoundTransform extends BitmapTransformation {
 
    private static float radius = 0f;
 
    public GlideRoundTransform(Context context) {
        this(context, 4);
    }
 
    public GlideRoundTransform(Context context, int dp) {
        super(context);
        this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
    }
 
    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return roundCrop(pool, toTransform);
    }
 
    private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;
 
        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }
 
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }
 
    public String getId() {
        return getClass().getName() + Math.round(radius);
    }
 
    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
 
    }
 
}

但是由于我的项目用的3.7版本的Glide,用了一些方法,在新版本里可能都已废弃了,所以,升级Glide 版本是走不通的
就这样,让我抓狂了一个多小时,后来在一次次尝试一次次百度后,终于找到了解决办法:

     Glide.with(mContext)
                .load(picUrl)
                .placeholder(R.drawable.square_holder)
                .transform(CenterCrop(context), GlideRoundTransform(context, 6))
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .crossFade()
                .dontAnimate()
                .into(holder.pic)

对的,就是多了几个属性,就好了!!
走到这一步,已经白白浪费了我将近两个小时,这两个小时内,真的万般折磨,像是一只迷路的小斑马,跌跌撞撞,找不到出路。到目前为止还有一个自己不经意埋得坑,也是自己挖的坑,那就是:

圆角也出来了,但是我的图片控件是正方形的,但是为什么运行到手机上就是长方形,究竟是谁,动了我的东西???

image.png

image.png

没道理的,最后把,在一位的大佬的提示下,找到了原因,原来是LayoutInflater的原因

  override fun onCreateViewHolder(parent: ViewGroup, positon: Int): NinePiceRecyAdapter.ViewHolder {
        var view = LayoutInflater.from(mContext).inflate(R.layout.layout_recy_nine_pic, null)
        return ViewHolder(view)
    }

因为自己在解析布局的时候,root传的是null,而且,在item的布局里,自己本来是想减少布局嵌套的才直接使用ImageView作为根布局的,但是就是这个“小聪明”,害的自己掉坑了,后来在布局里,在ImageView的外面裹了一层线性布局,就好了!




    


注意,线性布局一定要写,如果不写,你就等着掉坑吧,另外ListView GirdView 的item里如果直接用ImageView 作为根布局,也是会出现各种难以定位的bug的,慎重慎重!
对于LayoutInflater不熟悉的童鞋,请看https://www.jianshu.com/p/c1592963033a
最后附上一张完美的图

image.png

入坑一瞬间,脱坑三小时,总结两刻钟,喜欢的请点赞,谢谢支持,以一首码农之诗共勉:

有码走遍天下 无码寸步难行
1024 - 梦想,永不止步!
爱编程 不爱Bug
爱加班 不爱黑眼圈
固执 但不偏执
疯狂 但不疯癫
生活里的菜鸟
工作中的大神
身怀宝藏,一心憧憬星辰大海
追求极致,目标始于高山之巅
一群怀揣好奇,梦想改变世界的孩子
一群追日逐浪,正在改变世界的极客
你们用最美的语言,诠释着科技的力量
你们用极速的创新,引领着时代的变迁

------至所有正在努力奋斗的程序猿们!加油**

你可能感兴趣的:(Recyclerview 九宫格加载图片控件变形及Glide 3.7 无法加载圆角问题解决(爬坑之旅))