之前分析了个人微信Duilib的RecyclerView:[原创]微信Duilib的List优化和代价
当时的企业微信还没有RecyclerView,最近看了下,发现已经有类似的RecyclerView。
只是看起来有点奇怪,所以简单梳理一下背后的设计思路。
类似spy++,写了一个Duilib的界面元素查看工具,列表List相关的布局如下:
一般来说,Tree 类似 List的复杂结构。设计上一般两种方案:1、像原生的Duilib是Tree继承List;2、像QT是Tree和List并列,但继承同一个抽象类。但从界面布局上可以看出mmListView里面包含了mmTreeView,奇怪的点就在这里。
界面布局的关系不一定映射底层代码实现的关系,于是进一步看看这两个类:
从继承关系可以看出,mmListView和mmTreeView算是并列的,但又不像QT一样继承同一个抽象类。反而是mmListView继承mmTreeViewDataSource,提供数据给mmTreeView使用,也就是说,mmListView提供了列表的所有元素,而mmTreeView从中拿到部分元素做显示。这样子就可以进行Data和View的分离,从而实现RecyclerView。
类的关系理清,进一步就是数据的结构,看看最底层的数据是怎么组织起来的。
mmTreeView继承CContainerUI,没有重写AddAt,也就是说mmTreeView的元素数据结构和CContainerUI一样都是数组。
滚动列表,包含元素的数组会整体移动,可以看出确实有回收复用:

换句话说,mmTreeView就是RecyclerView,但是底层并不是概念上的树形结构。
有了数据结构,再看看列表滚动对应的逻辑代码:
滚动修改控件位置:
向下滚动:
向上滚动:
特殊的是,元素只是被移除,没有真正的删除,方便后续复用:
List是一维结构,Tree是二维结构,理论上也可以先实现Tree,然后List基于Tree特殊处理,让二维结构降成一维结构。
好奇Tree本身又是怎么实现的,树形Tree相关的布局如下:
再看看继承关系:
这就意味着mmTreeView3和mmTreeView一样底层都是数组。
Data和View确实是分离了,也实现了回收和复用。但不管是一维的List还是二维的Tree,在界面上显示都是一个一维的列表(二维的Tree通过缩进元素表达层次结构)。至于mmTreeView和mmTreeView3实际上都是RecyclerView。
class mmListView
class mmTreeView
class xxxItemView
class mmListView
class mmTreeView
class xxxItemView
mmListView继承:CContainerUI、mmTreeViewDataSource
mmTreeView继承:CVerticalLayoutUI(继承CContainerUI)
mmListView继承:CContainerUI、mmTreeViewDataSource
mmTreeView继承:CVerticalLayoutUI(继承CContainerUI)
DuiLib::CContainerUI::AddAt
DuiLib::CStdPtrArray::InsertAt
DuiLib::CContainerUI::AddAt
DuiLib::CStdPtrArray::InsertAt
int __thiscall mmTreeView::SetScrollPos(int this, struct tagSIZE a2)
offset = curScrollPos - *(this + 0x588);
*(this + 0x588) = curScrollPos;
if ( offset >= 0 )
sub_1B67AD(this, v6); // ⬇向下滚动
else
sub_1B66EE(this, v6); // ⬆向上滚动
int __thiscall mmTreeView::SetScrollPos(int this, struct tagSIZE a2)
offset = curScrollPos - *(this + 0x588);
*(this + 0x588) = curScrollPos;
if ( offset >= 0 )
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!