GridLayoutManager这么用,你可能还真没尝试过,android软件开发教程第二版答案

本文介绍了如何在RecyclerView中自定义ItemDecoration,通过绘制上层的分隔线来实现标题的吸顶效果。主要涉及自定义IGridItem接口,GridItemDecoration类的onDraw和getItemOffsets方法,以及在界面部分如何应用。通过这种方式,可以灵活地在网格布局中展示带有标题的列表项。
摘要由CSDN通过智能技术生成
  1. 将分隔线绘制在RecyclerView子视图的上层,因为其绘制方法ItemDecoration#onDrawOver发生在RecyclerView子视图绘制绘制完成以后,这也是ItemDecoration能够实现吸顶的效果。
三、代码实战

有了上面的知识储备,下面就简单了。

1. 自定义ItemDecoration

自定义ItemDecoration需要实现的三个方法,跟我们上面提及的原理相关:

| 方法名 | 解释 |

| — | — |

| onDraw | 绘制子视图下层的分隔线 |

| getItemOffsets | 通常为了显示下层分隔线而预留的空间 |

| onDrawOver | 绘制上层的分隔线 |

我们的任务仅仅是绘制一个标题,所以使用上面的两个方法就够了。

1.1 定义数据接口

/**

* 数据约束

*/

public interface IGridItem {

/**

* 是否启用分割线

* @return true

*/

boolean isShow();

/**

* 分类标签

*/

String getTag();

/**

* 权重

*/

int getSpanSize();

}

1.2 自定义ItemDecoration类

核心代码就100多行:

/**

* 适用于GridLayoutManager的分割线

*/

public class GridItemDecoration extends RecyclerView.ItemDecoration {

// 记录上次偏移位置 防止一行多个数据的时候视图偏移

private List offsetPositions = new ArrayList<>();

// 显示数据

private List<? extends IGridItem> gridItems;

// 画笔

private Paint mTitlePaint;

// 存放文字

private Rect mRect;

// 颜色

private int mTitleBgColor;

private int mTitleColor;

private int mTitleHeight;

private int mTitleFontSize;

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 - m

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

TitleHeight, 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);

}

}

}

}

总的逻辑就是:

  1. 如果所处的RecyclerView子视图的位置处在标题的下方,那么就需要预留空间,设置在outRect中,需要注意的是,同一行的多个子视图都需要预留空间。

  2. 对不同于上一个数据标题的当前数据进行标题的绘制。

  3. 重复执行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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值