Recyclerview 九宮格加載圖片控件變形及Glide 3.7 無法加載圓角問題解決(爬坑之旅)

睜開眼又是美好的一天,對于即將開始寫代碼的我,心情是這樣滴

timg.jpg

戴上耳機聽著自己喜歡的五月天的歌,寫著讓我感覺到安全的代碼,我感覺這真是一件幸福的事情,一頓行云流水的操作,中午時分我終于搞定了主題框架,心里成就感還是滿滿的,然而我不知道的是,即將發(fā)生的,那讓我陷入抓狂狀態(tài)的“大坑”,更糟的是還不是一個坑,坑中帶坑!

我要做的是一個圖片九宮格的功能,點擊還能縮放,本來這是一個老生常談的功能,要是放在以前,我肯定二話不說,首選GridView 實現(xiàn),但是GridView畢竟已經(jīng)服役這么多年了,所謂長江后浪推前浪,一浪更比一浪強,我們的GridView在復(fù)用和效率上,已經(jīng)遠(yuǎn)遠(yuǎn)不及后輩新秀RecyclerView.
所以,下午的時候我思忖了下,決定還是使用RecyclerView吧,畢竟順滑,高效,才是王道。

我們先看RecyclerView 設(shè)置九宮格的第一個問題

  • 九宮格圖片之間的上下左右間距
    還在用pindding 和margen 傻傻的在布局里調(diào)試嗎?No,you out 啦,今天我們要用一個號稱“萬金油”的設(shè)置間距的方式。
            var spanCount = 3
            var spacing = 10
            val gridSpacingItemDecoration = GridSpacingItemDecoration(spanCount, spacing, true)
            picRecy.addItemDecoration(gridSpacingItemDecoration)

對,只需要只給你的Recyclerveiw設(shè)置上addItemDecoration就可以了,想要什么間距就可以寫上身間距。

  • spacing 就是間距代表的實際像素,注意是px
  • spanCount 則是代表你有幾列,告訴Recy是怎么幫你總體把控的
  • GridSpacingItemDecorationd的第三個參數(shù)是一個布爾類型的,傳true表示包含邊緣,false為不包含邊緣
    對于還不熟悉ItemDecoration的童鞋,建議看一下http://www.lxweimin.com/p/b46a4ff7c10a這篇文章
    Ok,至此,運行查看效果,顯示的效果還是可以的,但是當(dāng)你多次下拉刷新后,問題出現(xiàn)了,第二個坑來了:

為什么多次刷新后item之間的間隔反而變大了???

image.png

剛進來還是正常的,多次刷新后就成了這個樣子了


image.png

為什么會變成這個樣子呢?點擊去看了一下,原來在內(nèi)部,是有一個集合累積存儲我們的間距的,這就導(dǎo)致了在多次刷新后,間距不斷變大
那么我們只需要在構(gòu)造函數(shù)里讓設(shè)置間距的代碼只執(zhí)行一次就OK了,對于網(wǎng)上的移除間距的方法,建議不用,因為用了也是沒效果的


        init {
            //設(shè)置間隔距離
            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可是我們的老朋友了,話說,士別三日當(dāng)刮目相看,但是這次,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;

    /**
     * 構(gòu)造函數(shù) 默認(rèn)圓角半徑 4dp
     *
     * @param context Context
     */
    public GlideRoundTransform(Context context) {
        this(context, 4);
    }

    /**
     * 構(gòu)造函數(shù)
     *
     * @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的時候我是經(jīng)常使用的,加載的效果也是很完美,但是今天卻不行了,我打開看了下,確認(rèn)下我的Glide版本還沒升級過,依舊是


image.png

很早以前的版本,但是因為工程的問題,一直也沒更新。我心想著,沒啥問題吧,于是就寫了
···

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

···
然后代碼里這樣設(shè)置

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

我自認(rèn)為是完美的,但是運行出來就傻眼了


image.png

為什么圓角沒出現(xiàn)呢?而且我的Imageview明明是個正方形,怎嗎還變形了,成了長方形?

一頓百度,各說各的理,我也只能一一嘗試,但是效果都是不太理想,后來還是找到了問題所在:
是因為ImageView的 android:scaleType="centerCrop"和GlideRoundTransform的方法造成了沖突
順著這個方向,又是一頓搗鼓:
把ImageView里的android:scaleType去掉,結(jié)果,各種各樣奇怪的問題都來了:
有圖片變形拉長的

image.png

有間距變大的
image.png

注意,圖片上的圓角是我最后成功后為了復(fù)現(xiàn)bug而展示,并不是此刻已經(jīng)完成的。

那么升級到最新的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)

對的,就是多了幾個屬性,就好了!!
走到這一步,已經(jīng)白白浪費了我將近兩個小時,這兩個小時內(nèi),真的萬般折磨,像是一只迷路的小斑馬,跌跌撞撞,找不到出路。到目前為止還有一個自己不經(jīng)意埋得坑,也是自己挖的坑,那就是:

圓角也出來了,但是我的圖片控件是正方形的,但是為什么運行到手機上就是長方形,究竟是誰,動了我的東西???

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 作為根布局,也是會出現(xiàn)各種難以定位的bug的,慎重慎重!
對于LayoutInflater不熟悉的童鞋,請看http://www.lxweimin.com/p/c1592963033a
最后附上一張完美的圖

image.png

入坑一瞬間,脫坑三小時,總結(jié)兩刻鐘,喜歡的請點贊,謝謝支持,以一首碼農(nóng)之詩共勉:

有碼走遍天下 無碼寸步難行
1024 - 夢想,永不止步!
愛編程 不愛Bug
愛加班 不愛黑眼圈
固執(zhí) 但不偏執(zhí)
瘋狂 但不瘋癲
生活里的菜鳥
工作中的大神
身懷寶藏,一心憧憬星辰大海
追求極致,目標(biāo)始于高山之巔
一群懷揣好奇,夢想改變世界的孩子
一群追日逐浪,正在改變世界的極客
你們用最美的語言,詮釋著科技的力量
你們用極速的創(chuàng)新,引領(lǐng)著時代的變遷

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容