实现延时加载的listview

因为项目需要,物品背包最多可能展示两百个物品,所以萌生了建立一个延时加载的listview的想法。
 
所谓的延时加载( lazily load ),即只需要展示当前所能看到的页面内容,其余的内容按需要再进行延时加载。这样,在加载200个item的场合,第一次实际才加载一屏的数据,在移动设备上,这并不会感到明显的卡顿。
 
动手之前,列举了一下需要封装的目标:
1. 设置缩进、列距、行距、列数相关的属性
2. 在控件内部监听当前位置,是否触发再加载
 
传统的listview api会是这个样子的,每次主动push都会create一个item加到界面显示: 
1 for i,item in enumerate(items):
2      list_view.push_item( item )

而由于在延时加载item的时机是在内部触发的,用户并不知道什么时候item被创建,因此新的api必须传递一个 on_item_callback 到控件内部,由内部来控制调用,因此有以下调用:

1 for i,item in enumerate(items):
2      list_view.push_item_data( item )
3 list_view.load_item( len(items), on_item_callback )

 

需求确定以后,开始动手编码,控件的属性有以下:

01 self.cols = 1
02 self._items = []
03 self.load_step = 40          # 每次加载的个数
04 self._on_item_callback = None     # 用户定义的创建物品回调
05  
06 self._loaded = 0             # 当前已经加载的个数
07 self._total = 0              # 列表item总个数
08  
09 self._cur_height = 0         # 当前聚焦的高度
10 self._total_height = 0       # 列表的总高度

核心的加载函数 _load_item:

01 def _load_items(self):
02     lv_size = self._list_view.getContentSize()
03     self._list_view.setItemsMargin(self.row_spacsing)
04  
05     added = 0
06     rest = self._total - self._loaded
07     while rest > added and added < self.load_step:
08         item = self._on_item_callback(self._items[self._loaded])
09         item_size = item.getContentSize()
10  
11         cur_col = self._loaded % self._cols   
12         if cur_col == 0:
13             llist = ccui.Widget.create()
14             llist.setContentSize(cc.Size(lv_size.width, item_size.height))
15             self._list_view.pushBackCustomItem(llist);
16             self._lists.append(llist)
17             self._total_height += item_size.height + self.row_spacsing
18         else:
19             llist = self._lists[len(self._lists)-1]
20  
21         = self.col_padding + (item_size.width + self.col_spacsing)* cur_col
22         item.setPosition(cc.Vec2(x,0))
23         llist.addChild(item)
24         self._items[self._loaded]['item'= item
25  
26         added += 1
27         self._loaded += 1

需要加载数据检查 need_load:

1 def need_load(self):
2     """通知控件更新更多item"""
3     if self.get_total_num() > self.get_loaded_num():
4         self._load_items()
5     else:
6         if self.need_more_data_callback:
7             self.need_more_data_callback()
need_load的调用,是建立一个触摸listenner,检查上拉滚动的位置来触发。
另外,self._items 用list存放 { 'index', 'data', 'item' },便于listview从中间 insert和pop item进行重新排版。
 
触发列表加载更多的列表项有多种方法:
① 滚动到底部时,再进行一次上拉操作,触发再加载,这种类似于微信上拉刷新朋友圈的方法,适合显式的网络请求场景
② 滚动快到底部,用户还没浏览到最后的时,内部偷偷地触发一次再加载,保持浏览的持续性
 
这里需要注意的是,self.load_step 一次加载的个数不宜设太大,设置到用户刚好感受不到卡顿感就可以,一般的手机一次加载一个手机屏幕高的列表项是不会卡顿的
下面是内部刷新的实现:
01 def _init_list_view(self):
02     self._list_view.setBounceEnabled(True)
03  
04     # 监听下拉触摸操作,检测列表是否需要加载更多item
05     self._clear_listener()  
06     self._event_listener = cc.EventListenerTouchOneByOne.create()
07     self._event_listener.setSwallowTouches(False)
08  
09     def on_touch_began(touch, event):
10         pos = touch.getLocation()
11         pos = self._list_view.convertToNodeSpaceAR(pos)
12         rect = self._list_view.getBoundingBox()
13         rect = cc.Rect(00, rect.width, rect.height)
14  
15         if rect.containsPoint(pos):     # 判断触摸点在listview裁剪区里面
16             return True
17  
18         return False
19  
20     def on_touch_moved(touch, event):
21         pass
22  
23     def on_touch_ended(touch, event):
24         iner_y = self._list_view.getInnerContainer().getPosition().y
25         size = self._list_view.getInnerContainerSize()
26         #if iner_y > 50:     # iner_y > 0时,当前listview已经到底并且即将反弹回来
27         if iner_y > -150:        # iner_y 越接近0,表示越接近底部
28             self.need_load()
29  
30     self._event_listener.setOnTouchBeganCallback(on_touch_began)
31     self._event_listener.setOnTouchMovedCallback(on_touch_moved)
32     self._event_listener.setOnTouchEndedCallback(on_touch_ended)
33     self._list_view.getEventDispatcher().addEventListenerWithFixedPriority(self._event_listener, -100)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值