睜開眼又是美好的一天,對于即將開始寫代碼的我,心情是這樣滴
戴上耳機聽著自己喜歡的五月天的歌,寫著讓我感覺到安全的代碼,我感覺這真是一件幸福的事情,一頓行云流水的操作,中午時分我終于搞定了主題框架,心里成就感還是滿滿的,然而我不知道的是,即將發(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之間的間隔反而變大了???
剛進來還是正常的,多次刷新后就成了這個樣子了
為什么會變成這個樣子呢?點擊去看了一下,原來在內(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版本還沒升級過,依舊是
很早以前的版本,但是因為工程的問題,一直也沒更新。我心想著,沒啥問題吧,于是就寫了
···
<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)為是完美的,但是運行出來就傻眼了
為什么圓角沒出現(xiàn)呢?而且我的Imageview明明是個正方形,怎嗎還變形了,成了長方形?
一頓百度,各說各的理,我也只能一一嘗試,但是效果都是不太理想,后來還是找到了問題所在:
是因為ImageView的 android:scaleType="centerCrop"和GlideRoundTransform的方法造成了沖突
順著這個方向,又是一頓搗鼓:
把ImageView里的android:scaleType去掉,結(jié)果,各種各樣奇怪的問題都來了:
有圖片變形拉長的
有間距變大的
注意,圖片上的圓角是我最后成功后為了復(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)意埋得坑,也是自己挖的坑,那就是:
圓角也出來了,但是我的圖片控件是正方形的,但是為什么運行到手機上就是長方形,究竟是誰,動了我的東西???
沒道理的,最后把,在一位的大佬的提示下,找到了原因,原來是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
最后附上一張完美的圖
入坑一瞬間,脫坑三小時,總結(jié)兩刻鐘,喜歡的請點贊,謝謝支持,以一首碼農(nóng)之詩共勉:
有碼走遍天下 無碼寸步難行
1024 - 夢想,永不止步!
愛編程 不愛Bug
愛加班 不愛黑眼圈
固執(zhí) 但不偏執(zhí)
瘋狂 但不瘋癲
生活里的菜鳥
工作中的大神
身懷寶藏,一心憧憬星辰大海
追求極致,目標(biāo)始于高山之巔
一群懷揣好奇,夢想改變世界的孩子
一群追日逐浪,正在改變世界的極客
你們用最美的語言,詮釋著科技的力量
你們用極速的創(chuàng)新,引領(lǐng)著時代的變遷
------至所有正在努力奮斗的程序猿們!加油**