一、随机高度问题
一般使用RecyclerView+StaggeredGridLayoutManager
构建瀑布流时,如果事先知道图片的长宽比,则直接设置就好,如果不知道的一般也采用随机高度,但是会导致图片不完整问题。可以尝试使用Glide
加载图片时,通过Target
对象获取图片长宽比。
- 每个Item的布局文件高度采用
wrap_content
android:layout_height="wrap_content"
- 每个 Item宽度固定,高度保存在Map中
imageHeightMap
,方便复用 - 重写
getItemViewType(int position)
因为每个Item高度都不同,导致每个ViewHolder
都不一样,为每个ViewHolder
制定唯一的viewType
值。(用途:处理Item乱跳的问题) - 使用Glide加载
if (!this.imageHeightMap.containsKey(position)){
//当首次加载图片时,调用 loadImageFirst(),保存图片高度
loadImageFirst(imageVH.imageView,position);
}else{
//非首次加载,直接根据保存的长宽,获取图片
Glide.with(this.mContext)
.load(this.entities.get(position).getUrl()) .override(this.imageWidth,this.imageHeightMap.get(position))
.into(imageVH.imageView);
}
loadImageFirst()
方法代码:
public void loadImageFirst(View view, final int position){
//构造方法中参数view,就是回调方法中的this.view
ViewTarget<View,Bitmap> target = new ViewTarget<View, Bitmap>(view) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
//加载图片成功后调用
float scaleType = ((float) resource.getHeight())/resource.getWidth();
int imageHeight = (int) (imageWidth*scaleType);
//获取图片高度,保存在Map中
imageHeightMap.put(position,imageHeight);
//设置图片布局的长宽,Glide会根据布局的自动加载适应大小的图片
ViewGroup.LayoutParams lp = this.view.getLayoutParams();
lp.width=imageWidth;
lp.height=imageHeight;
this.view.setLayoutParams(lp);
//resource就是加载成功后的图片资源
((ImageView)view).setImageBitmap(resource);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
//加载图片失败后调用
super.onLoadFailed(e, errorDrawable);
int imageHeight = imageWidth;
imageHeightMap.put(position,imageHeight);
ViewGroup.LayoutParams lp = this.view.getLayoutParams();
lp.width=imageWidth;
lp.height=imageHeight;
this.view.setLayoutParams(lp);
((ImageView)view).setImageResource(R.mipmap.ic_launcher);
}
};
Glide.with(this.mContext)
.load(this.entities.get(position).getUrl())
.asBitmap() //作为Bitmap加载,对应onResourceReady回调中第一个参数的类型
.into(target);
}
二、快速上拉时Item乱跳的问题
解决方法:在RecyclerView.adapter
中重写getItemViewType(int position)
方法
原理分析:
查看源码,针对该方法的注释为:
Return the view type of the item at <code>position</code> for the purposes of view recycling.The default implementation of this method returns 0, making the assumption of a single view type for the adapter. Unlike ListView adapters, types need not be contiguous. Consider using id resources to uniquely identify item view types.
大概的意思是view type作为View复用时的标记,可以是不连续的整数,默认返回为0。
从RecyclerView.adapter
复用ViewHolder
构建列表视图的过程来看:
- 针对不同的
position
调用getViewForPosition(int position, boolean dryRun)
。依次从mChangedScrap
、mAttachedScrap
、mCachedViews
中获取。都得不到的话,则从RecyclerViewPool
中通过viewType
来取,如果有多个ViewHolder
的viewType
一样,则取最后一个。仍然取不到的话,则通过final
方法createViewHolder
创建
-
createViewHolder
方法调用我们重写的onCreateViewHolder
方法。如果没有重写getItemViewType()
方法,则创建过程中的viewType
值为0。即RecyclerViewPool
中有多个以0为标志的ViewHolder
。 - 所以当上拉时,之前的
ViewHolder
重新从RecyclerViewPool
获取,而不是调用onCreateViewHolder
方法创建。因为之前多次创建了viewType
为0的ViewHolder
,所以获取的并不是当前我们需要的,而是最后一次由onCreateViewHolder
方法创建的。从而导致该position
位置的图片高度不合适,出现一定的gaps
(空隙)。而在StaggeredGridLayoutManager
有专门的一个线程,每当滚动停止时对出现的gaps
进行重新布局,导致Item乱跳