可以通过StaggeredGridLayoutManager.LayoutParams的setFullSpan()方法来实现,继承recyclerView.Adapter 重写onViewAttachedToWindow方法:
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
super.onViewAttachedToWindow(holder)
val lp: ViewGroup.LayoutParams = holder.itemView.layoutParams
if (lp is StaggeredGridLayoutManager.LayoutParams) {
//根据viewType判断是否是占满一行
if (isFullSpan(holder.itemViewType)) {
lp.isFullSpan = true
holder.itemView.layoutParams = lp
} else {
lp.isFullSpan = false
holder.itemView.layoutParams = lp
}
}
}
override fun getItemViewType(position: Int): Int {
val item = list[position]
return if (item is BannerBean) {//banner
ITEM_BANNER_HEADER
} else if(item is OtherHeaderBean){//其他的header
ITEM_OTHER_HEADER
}else{
ITEM_NORMAL//瀑布流item
}
}
//判断是否占满一行
fun isFullSpan(viewType: Int)=viewType != ITEM_NORMAL
复制代码
瀑布流滑动出现跳动问题,是由于ViewHolder的回收机制,item重新绘制,导致的跳动,先拿到item的尺寸就行了,就可以避免这些问题。
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = list[position]
when(holder.itemViewType){
ITEM_BANNER -> (holder as BannerHolder).bind(item as BannerBean)//banner
ITEM_OTHER_HEADER -> (holder as OtherHeaderHolder).bind(item as OtherHeaderBean)//其他的header
else -> (holder as NormalHolder).bind(mRecyclerView,item as ImageBean)//瀑布流item
}
}
复制代码
在瀑布流的NormalHolder的bind方法中设置好item的高度就行了,ImageView的宽度就是屏幕宽度-间距后除以spanCount,然后拿到图片的宽高比就可以得到ImageView的高度了。
fun bind(recyclerView: RecyclerView,imageBean : ImageBean){
val manager: RecyclerView.LayoutManager? = recyclerView.getLayoutManager()
if (manager is StaggeredGridLayoutManager) {
val screenWidth = getScreenWidth(itemView.getContext())
//(屏幕宽度-间距)/2 就是图片的宽度 ,当然这里的spanCount是2
val width = (screenWidth - Utils.convertDpToPixel(54f))/2
val lp: ViewGroup.LayoutParams = thumbIv.getLayoutParams()
lp.width = width.toInt()
//宽度*比例就得到高度,这里事先拿到图片宽高的比例
lp.height = (width*imageBean.ratio).toInt()
imageView.setLayoutParams(lp)
}
...
}
复制代码
最后就是item直接间距的问题,如果UI给的效果图是左边的间距加右边的间距等于中间的间距,那就用常规的配置就行
public class SpaceItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpaceItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.left = space;
outRect.top = space;
outRect.right = space;
outRect.bottom = space;
}
}
复制代码
如果是这种,左边item到左边是20dp,左右item中间的间隔也是20dp,右边item到右边是20dp,这种怎么办?
第一种方式设置recyclerView 的magin或者padding,设置为10dp,然后space也为10dp就行了。
第二种方式就是通过StaggeredGridLayoutManager.LayoutParams里的getSpanIndex()来判断这个item是左边的还是右边的。
public class StaggeredGridItemDecoration extends RecyclerView.ItemDecoration {
//瀑布流的viewType类型
private int staggeredGridViewType = 0;
//间距
private int space;
public StaggeredGridItemDecoration(int staggeredGridViewType, int space) {
this.staggeredGridViewType = staggeredGridViewType;
this.space = space;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
RecyclerView.Adapter adapter = parent.getAdapter();
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (adapter==null || layoutManager==null){
return;
}
int position = parent.getChildAdapterPosition(view);
int viewType = adapter.getItemViewType(position);
if(viewType == staggeredGridViewType){
if (layoutManager instanceof StaggeredGridLayoutManager){
int spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
int spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
if (spanCount==2 && spanIndex != GridLayoutManager.LayoutParams.INVALID_SPAN_ID) {
if (spanIndex % 2 == 0) {
//这个是左边item
outRect.left = space;
outRect.right = space/2;
} else {
//这个是右边item
outRect.left = space/2;
outRect.right = space;
}
outRect.bottom = space;
}
}
}
}
}
复制代码