序言
最近项目中要实现瀑布流的混排,于是写了一些工具类来实现。使用了这个工具类,可以处理混排,可以处理间距。都集成在一个接口中。
最后效果类似这样。
工具类
GridItemUI
下面的这个类是用来实现在GridLayouManger中混排的。
package com.trs.baseapp.news.list.ui.staggered;
import android.graphics.Rect;
import android.util.Pair;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.trs.library.callback.action.TRSFunction;
/**
* <pre>
* Created by zhuguohui
* Date: 2024/8/23
* Time: 10:21
* Desc:用来实现GridItem混排的接口。
*
* 用法如下:
* <code>
* int spanCount = 3;
* GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount);
* GridItemUI.setSpanSizeLookup(gridLayoutManager,position->adapter.getItems().get(position));
* GridItemUI.addItemDecoration(recyclerView, spanCount, position -> adapter.getItems().get(position));
* </code>
* </pre>
*/
public interface GridItemUI {
/**
* 获取item之间的水平间隔 horizontal
*
* @return
*/
int getItemHorizontalSpaceSize();
/**
* 获取Item 之间垂直方向的间隔
*
* @return
*/
int getItemVerticalSpaceSize();
static void setSpanSizeLookup(GridLayoutManager gridLayoutManager, TRSFunction<Integer, Object> findAdapterContentFunction) {
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
Object o = findAdapterContentFunction.call(position);
if (o instanceof GridItemUI) {
return 1;
}
return gridLayoutManager.getSpanCount();
}
});
}
static void addItemDecoration(RecyclerView recyclerView, int spanCount, TRSFunction<Integer, Object> findAdapterContentFunction) {
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
Object o = findAdapterContentFunction.call(position);
if (o instanceof GridItemUI) {
Pair<Boolean, Boolean> pair = getIsFirstLineAndIsLineFirstItem(position);
GridItemUI itemUI = (GridItemUI) o;
int hs = itemUI.getItemHorizontalSpaceSize();
int vs = itemUI.getItemVerticalSpaceSize();
outRect.left = pair.second ? hs : 0;
outRect.right = hs;
outRect.top = pair.first ? vs : 0;
outRect.bottom = vs;
} else {
super.getItemOffsets(outRect, view, parent, state);
}
}
private Pair<Boolean, Boolean> getIsFirstLineAndIsLineFirstItem(int position) {
int beforeItemCount = 0;
int checkPosition = position;
do {
checkPosition--;
if (checkPosition < 0) {
break;
}
Object o = findAdapterContentFunction.call(checkPosition);
if (!(o instanceof GridItemUI)) {
break;
}
beforeItemCount++;
} while (true);
boolean lineFirst = beforeItemCount % spanCount == 0;
boolean firstLine = beforeItemCount < spanCount;
return new Pair<>(firstLine, lineFirst);
}
});
}
}
用法
int spanCount = 3;
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount);
//以下代码实现混排
GridItemUI. setSpanSizeLookup(gridLayoutManager,position->adapter. getItems().get(position));
//以下代码实现间距
GridItemUI. addItemDecoration(recyclerView, spanCount, position -> adapter. getItems().get(position));
StaggeredUI
package com.trs.baseapp.news.list.ui.staggered;
import android.graphics.Rect;
import android.util.Pair;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.trs.library.callback.action.TRSFunction;
/**
* <pre>
* Created by zhuguohui
* Date: 2024/8/28
* Time: 11:24
* Desc:实现了该接口,表示当前UI是为了瀑布流设计的
* </pre>
*/
public interface StaggeredUI {
/**
* 用来查找adapter中对应position数据的接口
*/
interface FindAdapterContentFunction{
Object call(int position);
}
static void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder,FindAdapterContentFunction findAdapterContentFunction) {
int adapterPosition = holder.getAdapterPosition();
Object o = findAdapterContentFunction.call(adapterPosition);
StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
if (o instanceof StaggeredUI) {
params.setFullSpan(false);
} else {
params.setFullSpan(true);
}
}
static void addItemDecoration(RecyclerView recyclerView, int spanCount, FindAdapterContentFunction findAdapterContentFunction) {
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
Object o = findAdapterContentFunction.call(position);
if (o instanceof StaggeredUI itemUI) {
StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
int hs = itemUI.getItemHorizontalSpaceSize();
int vs = itemUI.getItemVerticalSpaceSize();
outRect.left = lp.getSpanIndex() == 0 ? hs : 0;
outRect.right = hs;
outRect.top = isFirstLine(position) ? vs : 0;
outRect.bottom = vs;
} else {
super.getItemOffsets(outRect, view, parent, state);
}
}
private boolean isFirstLine(int position) {
int beforeItemCount = 0;
int checkPosition = position;
do {
checkPosition--;
if (checkPosition < 0) {
break;
}
Object o = findAdapterContentFunction.call(checkPosition);
if (!(o instanceof StaggeredUI)) {
break;
}
beforeItemCount++;
} while (true);
boolean firstLine = beforeItemCount < spanCount;
return firstLine;
}
});
}
/**
* 返回垂直间距
* @return
*/
int getItemVerticalSpaceSize();
/**
* 返回水平间距
* @return
*/
int getItemHorizontalSpaceSize();
}
使用
1.在混排的数据中,把要瀑布流显示的数据实现接口
默认所有数据都不是瀑布流,只有实现了StaggeredUI 接口的数据才是瀑布流
2.使用接口的静态方法设置间距
3.重写adapter的 onViewAttachedToWindow 方法
直接调用StaggeredUI的同名静态方法就行了
4.布局layout_width使用 match_parent
如果要保持一定比例使用约束布局就可以了。