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

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

<ImageView
    android:layout_width="106dp"
    android:id="@+id/pic"
    android:layout_height="106dp"
    android:scaleType="centerCrop"
    android:src="@drawable/square_holder" />

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

  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的外面裹了一层线性布局,就好了!

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/pic"
        android:layout_width="106dp"
        android:layout_height="106dp"
        android:scaleType="centerCrop"
        android:src="@drawable/square_holder" />

</LinearLayout>

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

image.png

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

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容