private Boolean isDrawTitleBg = false;
private Context mContext;
// 总的SpanSize
private int totalSpanSize;
private int mCurrentSpanSize;
//… 省略一些方法
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
// 绘制标题的逻辑:
// 如果该行的数据的需要显示的标题不同于上行的标题,就绘制标题
final int paddingLeft = parent.getPaddingLeft();
final int paddingRight = parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int pos = params.getViewLayoutPosition();
IGridItem item = gridItems.get(pos);
if (item == null || !item.isShow())
continue;
if (i == 0) {
drawTitle(c, paddingLeft, paddingRight, child
, (RecyclerView.LayoutParams) child.getLayoutParams(), pos);
} else {
IGridItem lastItem = gridItems.get(pos - 1);
if (lastItem != null && !item.getTag().equals(lastItem.getTag())) {
drawTitle(c, paddingLeft, paddingRight, child,
(RecyclerView.LayoutParams) child.getLayoutParams(), pos);
}
}
}
}
/**
* 绘制标题
* @param canvas 画布
* @param pl 左边距
* @param pr 右边距
* @param child 子View
* @param params RecyclerView.LayoutParams
* @param pos 位置
*/
private void drawTitle(Canvas canvas, int pl, int pr, View child, RecyclerView.LayoutParams params, int pos) {
if (isDrawTitleBg) {
mTitlePaint.setColor(mTitleBgColor);
canvas.drawRect(pl, child.getTop() - params.topMargin - mTitleHeight, pl
, child.getTop() - params.topMargin, mTitlePaint);
}
IGridItem item = gridItems.get(pos);
String content = item.getTag();
if (TextUtils.isEmpty(content))
return;
mTitlePaint.setColor(mTitleColor);
mTitlePaint.setTextSize(mTitleFontSize);
mTitlePaint.setTypeface(Typeface.DEFAULT_BOLD);
mTitlePaint.getTextBounds(content, 0, content.length(), mRect);
float x = UIUtils.dip2px(20f);
float y = child.getTop() - params.topMargin - (mTitleHeight - mRect.height()) / 2;
canvas.drawText(content, x, y, mTitlePaint);
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// 预留逻辑:
// 只要是标题下面的一行,无论这行几个,都要预留空间给标题显示
int position = parent.getChildAdapterPosition(view);
IGridItem item = gridItems.get(position);
if (item == null || !item.isShow())
return;
if (position == 0) {
outRect.set(0, mTitleHeight, 0, 0);
mCurrentSpanSize = item.getSpanSize();
} else {
if (!offsetPositions.isEmpty() && offsetPositions.contains(position)) {
outRect.set(0, mTitleHeight, 0, 0);
return;
}
if (!TextUtils.isEmpty(item.getTag()) && !item.getTag().equals(gridItems.get(position - 1).getTag())) {
mCurrentSpanSize = item.getSpanSize();
} else
mCurrentSpanSize += item.getSpanSize();
if (mCurrentSpanSize <= totalSpanSize) {
outRect.set(0, mTitleHeight, 0, 0);
offsetPositions.add(position);
}
}
}
}
总的逻辑就是:
-
如果所处的RecyclerView子视图的位置处在标题的下方,那么就需要预留空间
,设置在outRect中,需要注意的是,同一行的多个子视图都需要预留空间。 -
对不同于上一个数据标题的当前数据进行标题的绘制。
-
重复执行1、2。
2. 界面部分
public class SpecialGridActivity extends AppCompatActivity {
// GridItem实现了IGridItem接口
private List values;
private RecyclerView mRecyclerView;
private GridItemDecoration itemDecoration;
// 自己封装的RecyclerAdapter
private RecyclerAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_special_grid);
initWidget();
}
private void initWidget() {
mRecyclerView = findViewById(R.id.rv_content);
// 创建GridLayoutManager,并设置SpanSizeLookup
GridLayoutManager gll = new GridLayoutManager(this, 3);
gll.setSpanSizeLookup(new SpecialSpanSizeLookup());
mRecyclerView.setLayoutManager(gll);
values = initData();
// 自己封装的RecyclerAdapter
mRecyclerView.setAdapter(mAdapter = new RecyclerAdapter(values,null) {
@Override
public ViewHolder onCreateViewHolder(View root, int viewType) {
switch (viewType) {
case R.layout.small_grid_recycle_item:
return new SmallHolder(root);
case R.layout.normal_grid_recycle_item:
return new NormalHolder(root);
case R.layout.special_grid_recycle_item:
return new SpecialHolder(root);
default:
return null;
}
}
@Override
public int getItemLayout(GridItem gridItem, int position) {
switch (gridItem.getType()) {
case GridItem.TYPE_SMALL:
return R.layout.small_grid_recycle_item;
case GridItem.TYPE_NORMAL:
return R.layout.normal_grid_recycle_item;
case GridItem.TYPE_SPECIAL:
return R.layout.special_grid_recycle_item;
}
return 0;
}
});
//…
// 分隔线生成
// 之前的GridItemDecoration代码中我将构建者模式部分省略了
itemDecoration = new GridItemDecoration.Builder(this,values, 3)
.setTitleTextColor(Color.parseColor("#4e5864"))
.setTitleFontSize(22)
.setTitleHeight(52)
.build();
mRecyclerView.addItemDecoration(itemDecoration);
}
// 数据初始化
private List initData() {
List values = new ArrayList<>();
values.add(new GridItem(“我很忙”, “”, R.drawable.head_1,“最近常听”,1,GridItem.TYPE_SMALL));
values.add(new GridItem(“治愈:有些歌比闺蜜更懂你”, “”, R.drawable.head_2,“最近常听”,1,GridItem.TYPE_SMALL));
values.add(new GridItem("「华语」90后的青春纪念手册", “”, R.drawable.head_3,“最近常听”,1,GridItem.TYPE_SMALL));
values.add(new GridItem(“流行创作女神你霉,泰勒斯威夫特的创作历程”, “”, R.drawable.special_2
,“更多为你推荐”,3,GridItem.TYPE_SPECIAL));
values.add(new GridItem(“行走的CD写给别人的歌”, “给「跟我走吧」几分,试试这些”, R.drawable.normal_1
,“更多为你推荐”,3,GridItem.TYPE_NORMAL));
values.add(new GridItem(“爱情里的酸甜苦辣,让人捉摸不透”, “听完「靠近一点点」,他们等你翻牌”, R.drawable.normal_2
,“更多为你推荐”,3,GridItem.TYPE_NORMAL));
values.add(new GridItem(“关于喜欢你这件事,我都写在了歌里”, “「好想你」听罢,听它们吧”, R.drawable.normal_3
,“更多为你推荐”,3,GridItem.TYPE_NORMAL));
values.add(new GridItem(“周杰伦暖心混剪,短短几分钟是多少人的青春”, “”, R.drawable.special_1
,“更多为你推荐”,3,GridItem.TYPE_SPECIAL));
values.add(new GridItem(“我好想和你一起听雨滴”, “给「发如雪」几分,那这些呢”, R.drawable.normal_4
,“更多为你推荐”,3,GridItem.TYPE_NORMAL));
values.add(new GridItem(“油管周杰伦热门单曲Top20”, “「周杰伦」的这些哥,你听了吗”, R.drawable.normal_5
,“更多为你推荐”,3,GridItem.TYPE_NORMAL));
return values;
}
class SpecialSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {
@Override
public int getSpanSize(int i) {
// 返回在数据中定义的SpanSize
GridItem gridItem = values.get(i);
return gridItem.getSpanSize();
}
}
class SmallHolder extends RecyclerAdapter.ViewHolder {
//… 代码省略,就是设置图片和文字的操作
// 小的Holder
}
class NormalHolder extends RecyclerAdapter.ViewHolder {
//… 中等的Holder
}
class SpecialHolder extends RecyclerAdapter.ViewHolder {
//… 横向大的Holder
SpanSize(int i) {
// 返回在数据中定义的SpanSize
GridItem gridItem = values.get(i);
return gridItem.getSpanSize();
}
}
class SmallHolder extends RecyclerAdapter.ViewHolder {
//… 代码省略,就是设置图片和文字的操作
// 小的Holder
}
class NormalHolder extends RecyclerAdapter.ViewHolder {
//… 中等的Holder
}
class SpecialHolder extends RecyclerAdapter.ViewHolder {
//… 横向大的Holder