睁开眼又是美好的一天,对于即将开始写代码的我,心情是这样滴
戴上耳机听着自己喜欢的五月天的歌,写着让我感觉到安全的代码,我感觉这真是一件幸福的事情,一顿行云流水的操作,中午时分我终于搞定了主题框架,心里成就感还是满满的,然而我不知道的是,即将发生的,那让我陷入抓狂状态的“大坑”,更糟的是还不是一个坑,坑中带坑!
我要做的是一个图片九宫格的功能,点击还能缩放,本来这是一个老生常谈的功能,要是放在以前,我肯定二话不说,首选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之间的间隔反而变大了???
刚进来还是正常的,多次刷新后就成了这个样子了
为什么会变成这个样子呢?点击去看了一下,原来在内部,是有一个集合累积存储我们的间距的,这就导致了在多次刷新后,间距不断变大
那么我们只需要在构造函数里让设置间距的代码只执行一次就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版本还没升级过,依旧是
很早以前的版本,但是因为工程的问题,一直也没更新。我心想着,没啥问题吧,于是就写了
···
<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)
我自认为是完美的,但是运行出来就傻眼了
为什么圆角没出现呢?而且我的Imageview明明是个正方形,怎吗还变形了,成了长方形?
一顿百度,各说各的理,我也只能一一尝试,但是效果都是不太理想,后来还是找到了问题所在:
是因为ImageView的 android:scaleType="centerCrop"和GlideRoundTransform的方法造成了冲突
顺着这个方向,又是一顿捣鼓:
把ImageView里的android:scaleType去掉,结果,各种各样奇怪的问题都来了:
有图片变形拉长的
有间距变大的
注意,图片上的圆角是我最后成功后为了复现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)
对的,就是多了几个属性,就好了!!
走到这一步,已经白白浪费了我将近两个小时,这两个小时内,真的万般折磨,像是一只迷路的小斑马,跌跌撞撞,找不到出路。到目前为止还有一个自己不经意埋得坑,也是自己挖的坑,那就是:
圆角也出来了,但是我的图片控件是正方形的,但是为什么运行到手机上就是长方形,究竟是谁,动了我的东西???
没道理的,最后把,在一位的大佬的提示下,找到了原因,原来是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
最后附上一张完美的图
入坑一瞬间,脱坑三小时,总结两刻钟,喜欢的请点赞,谢谢支持,以一首码农之诗共勉:
有码走遍天下 无码寸步难行
1024 - 梦想,永不止步!
爱编程 不爱Bug
爱加班 不爱黑眼圈
固执 但不偏执
疯狂 但不疯癫
生活里的菜鸟
工作中的大神
身怀宝藏,一心憧憬星辰大海
追求极致,目标始于高山之巅
一群怀揣好奇,梦想改变世界的孩子
一群追日逐浪,正在改变世界的极客
你们用最美的语言,诠释着科技的力量
你们用极速的创新,引领着时代的变迁
------至所有正在努力奋斗的程序猿们!加油**