最近写销控表,需要有上万个数据表格的情况,
首先感谢 https://blog.csdn.net/wuyuxing24/article/details/79047924 的帖主,给了大家一个非常好的例子。本文也是根据他的帖子的原理,整理了自己在编写销控表的时候遇到的问题。
辅助字段帮助layoutDecoratedWithMargins进行定位;
很多demo 在onLayoutChildren 方法中 进行两个动作,一个是计算得到真实的最大宽高,一个是进行layout的动作, 我的观点,一旦外部数据获得的那时候,完全可以给实体添加字段计算好,左上右下的位置,把实体扔进来,比如添加下面的字段,进行定位的时候就很简单方便了
,即使是遇到异型单元格,比如有些单元格横向有合并,或者有些单元格纵向有合并的情况,也方便处理。
//辅助在 onLayoutChildren 进行layoutDecoratedWithMargins的定位
//需要布局的单元的宽度
private int width;
//需要布局的单元的高度
private int height;
//需要布局的单元的左边位置
private int left;
//需要布局的单元的上边位置
private int top;
layoutDecoratedWithMargins的时候 就可以直接把数据bean扔进来解决问题
layoutDecoratedWithMargins(view, bean.getLeft() - mOffsetHorizontal, bean.getTop() - mOffsetVertical, bean.getLeft() + bean.getWidth() - mOffsetHorizontal, bean.getTop() + bean.getHeight() - mOffsetVertical);
onLayoutChildren 中判断位置是否屏幕可见再获取view
很多demo在for循环一开始就去recycler.getViewForPosition(i),但这会有一个问题,当你的view特别多时,我遇到的是,可能有上万数据。这个时候 for循环的时候先去recycler.getViewForPosition(i); 会导致上万次的获取view ,这样的话,即使你在 Rect.intersects(mLayoutState.mSlideAreaRect, itemRect) 为 false 的时候去调用removeAndRecycleView(view, recycler); 进行view 的回收,依然会存在性能和卡顿问题。
但如果你根据屏幕是否可见,再去getView ,那个总共需要获取的view数,只是屏幕可见的那些个,理论上顶多100到200个,每次滑动的时候,scrollHorizontallyBy 或者 scrollVerticallyBy中可以detachAndScrapAttachedViews ,进行回收,性能大大提高。比如下面的代码,在view 不再屏幕可见的时候,也不需要执行removeAndRecycleView(view, recycler); 也不会卡顿,因为滑动的时候,一般都会去调用detachAndScrapAttachedViews
private void fillChildren2(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getItemCount() <= 0 || state.isPreLayout()) {
return;
}
Rect itemRect = new Rect();
//处理header
int displayNum = 0;
int skipNum = 0;
for(int i = 0; i < itemList.size(); i++) {
ItemDataBean bean = itemList.get(i);
itemRect.set(bean.getLeft(), bean.getTop(), bean.getLeft() + bean.getWidth(), bean.getTop() + bean.getHeight());
if(Rect.intersects(mLayoutState.mSlideAreaRect, itemRect)) {
View view = recycler.getViewForPosition(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
displayNum++;
layoutParams.width = bean.getWidth();
layoutParams.height = bean.getHeight();
// view.setLayoutParams(layoutParams);
addView(view);
measureChildWithMargins(view, 0, 0);
layoutDecoratedWithMargins(view, bean.getLeft() - mLayoutState.mOffsetHorizontal, bean.getTop() - mLayoutState.mOffsetVertical, bean.getLeft() + bean.getWidth() - mLayoutState.mOffsetHorizontal, bean.getTop() + bean.getHeight() - mLayoutState.mOffsetVertical);
} else {
//移除并回收掉滑出屏幕的View
// removeAndRecycleView(view, recycler);
skipNum++;
continue;
}
}
Log.e("LayoutManager", "displayNum" + displayNum);
Log.e("LayoutManager", "skipNum" + skipNum);
}
ItemDecoration 无法重新绘制的问题
当你绘制一遍所有子view 后,你会发现,当你把这些在LayoutManger绘制过的view 重新拿出一些依照你的需求摆放时,可能这样说很抽象,给个图, 如下图这种销控表,当你需要固定楼层那一列,基本上做法是重新拿出已经摆放在底层的,再绘制一遍在指定的固定位置,这个时候你会发现这些重新绘制的view的ItemDecoration不见了,但参考官网说法,ItemDecoration中的onDraw是绘制在最底层的,从下到上是canvas → ItemDecoration → 你第一次绘制的view → 你第二次绘制的view ,就是说不管是否是第二次绘制的view ,ItemDecoration 始终是在最底层,所以给人一种第二次绘制的view没有ItemDecoration 的假象,因为他会被第一次绘制view 的遮住。