基本概念
CollectionView是Google I/O 2014应用程序内置的控件,目的是以宫格样式显示不同分组的列表。
CollectionView控件可以用来显示带不同标识头部的列表。除了可以给每个分组添加标识头部之外,还可以自定义每组元素的显示列数。AdapterView类必须实现CollectionViewCallbacks接口来定制头部和列表项的布局。然后,调用CollectionView的主类必须调用CollectionView.updateInventory(Inventory)方法来给所有需要填充的数据分组。每组元素按几列显示可以由InventoryGroup.setDisplayCols(int)方法设置。
其中,Inventory类包含了所有需要填充数据的分组信息,InventoryGroup类则代表着某个分组,规定了组号,是否显示头部,头部标签,每行数据显示的列数,数据起始的索引,组中总的数据数。
类关系图
功能介绍
CollectionView的主要方法有setCollectionAdapter(),updateInventory(),前者负责注入负责绘制头部和列表项的CollectionViewCallbacks接口实例,后者的作用是填充分好组的数据。填充好数据后,会调用notifyAdapterDataSetChanged()方法要求控件重新绘制。与普通的AdapterView直接调用notifyDataSetChanged()方法提醒数据源变化不同,notifyAdapterDataSetChanged()方法内部重新配置了一个MyListAdapter实例,MyListAdapter负责具体的视图渲染任务。
到这里可能会有困惑?因为在实际使用CollectionView的过程中,我们编写了继承CursorAdapter(假设数据源是SQLite数据库)实现CollectionViewCallbacks接口的适配器类CollectionAdapter类,来负责渲染视图,那么CollectionView为什么将绘制任务交给MyListAdapter来做。
其实,仔细研究代码就可以发现,我们编写的CollectionAdapter类只是可以绘制每个列表项元素和每个分组的头部元素,并没有办法绘制一整行列表项。MyListAdapter就是在CollectionAdapter的基础上进行组织,根据InventoryGroup.setDisplayCols(int)方法设置的列数来绘制一整行元素。
MyListAdapter工作时,调用getRowViewType()方法得到当前行的视图类型,getRowViewType()方法接受一个用来保存当前行元数据的RowComputeResult实例。
类RowComputeResult的代码如下:
class RouComputeResult{
int row;
Boolean isHeader;
int groupId;
InventoryGroup group;
int groupOffset;
}
虽然InventoryGroup类保存了某个分组的元数据,但是这个分组的元素可能需要多行显示,InventoryGroup类无法反映每一行的状况。所以,MyListAdapter类调用computeRowContent()方法计算每一行的数据应该如何绘制,计算结果保存在上述的RowComputeResult实例中。computeRowContent()方法不管计算哪一行的绘制数据,都需要遍历整个mInventroy.mGroups的分组信息集。
MyListAdapter得到每行视图的类型后将调用getRowView()方法进行具体的绘制。getRowView()方法的回调栈如下:
getRowView()
——makeRow()
根据isHeader值调用
——mCallbacks.newCollectionHeaderView()
——makeItemRow()
根据布局是否可以重用调用
——recycleItemRow()
——makeNewItemRow()
——getItemView()&& setupLayoutParams()
到此,MyListAdapter将成功绘制各行元素。
详细的活动图如下:
结论
CollectionView给出了借助ListView以宫格形式显示带有头部的列表的方案,优点是简单、直观,易于理解,易于使用。
我个人认为CollectionView还有许多值得改进的地方,比如是否在分组的时候就计算好每组可能需要几行来展示,计算好每行元素的元数据?