cocos2d-x改底层之获取UIListView的实际内容大小

前端之家收集整理的这篇文章主要介绍了cocos2d-x改底层之获取UIListView的实际内容大小前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

实际项目中UI界面中经常会用到UIListView,大多会在CocoStudio中直接添加这个控件。但是在使用中发现了一些坑和功能缺乏,然后就看了一下底层的逻辑,发现稍微改一下底层就可以满足需求,所以下面就针对需求来分析UIListView的底层,同时做一些改动。


需求:根据链表中的内容来动态调整listView本身的大小

首先,我们要知道,我们插入和移除链表中的一项,listView本身会如何处理:

  1. void ListView::pushBackDefaultItem()
  2. {
  3. if (!_model)
  4. {
  5. return;
  6. }
  7. /* 克隆一份模板,并添加到项的数组里 */
  8. Widget* newItem = _model->clone();
  9. _items->addObject(newItem);
  10. /* 根据listView的基础设置来调整新加项的布局关系 */
  11. remedyLayoutParameter(newItem);
  12. addChild(newItem);
  13. /* 重点:打开刷新开关 */
  14. _refreshViewDirty = true;
  15. }

这里最后一句才是重点,只有刷新了才会真正计算新的显示,之前的改动才真正生效,所以放我们添加一项的时候,当前帧其实并没有立即刷新,如果这时候获取大小,只会和之前的一样,并没有改变,那么我们要知道,开关_refreshViewDirty是在什么时候起作用了呢,如下:

  1. void ListView::sortAllChildren()
  2. {
  3. ScrollView::sortAllChildren();
  4. if (_refreshViewDirty)
  5. {
  6. /* 刷新 */
  7. refreshView();
  8. _refreshViewDirty = false;
  9. }
  10. }
  1. void ListView::refreshView()
  2. {
  3. ccArray* arrayItems = getItems()->data;
  4. int length = arrayItems->num;
  5. for (int i=0; i<length; i++)
  6. {
  7. Widget* item = static_cast<Widget*>(arrayItems->arr[i]);
  8. item->setZOrder(i);
  9. remedyLayoutParameter(item);
  10. }
  11. /* 更新内容大小 */
  12. updateInnerContainerSize();
  13. }

可以看到,最关键的改变大小的函数updateInnerContainerSize():

定义一个变量用来保存真实大小,原因是listView本身计算大小的结果并不是以内容为准,而是以最初用户设置的大小,那么真实的大小会被遗弃,所以我们要保存住她:

  1. CCSize _actualInnerSize;
  1. void ListView::updateInnerContainerSize()
  2. {
  3. switch (_direction)
  4. {
  5. case SCROLLVIEW_DIR_VERTICAL:
  6. {
  7. /*...*/
  8. /* 保存真实大小 */
  9. _actualInnerSize = CCSize(finalWidth,finalHeight);
  10. setInnerContainerSize(_actualInnerSize);
  11. break;
  12. }
  13. case SCROLLVIEW_DIR_HORIZONTAL:
  14. {
  15. /*...*/
  16. /* 保存真实大小 */
  17. _actualInnerSize = CCSize(finalWidth,finalHeight);
  18. setInnerContainerSize(_actualInnerSize);
  19. break;
  20. }
  21. default:
  22. break;
  23. }
  24. }


setInnerContainerSize ( _actualInnerSize );这个函数是在父类定义的:

  1. void ScrollView::setInnerContainerSize(const CCSize &size)
  2. {
  3. /* 获取用户设置的大小(没设置就是默认的) */
  4. float innerSizeWidth = _size.width;
  5. float innerSizeHeight = _size.height;
  6. /* 获取原始大小 */
  7. CCSize originalInnerSize = _innerContainer->getSize();
  8. /* 更新后的新的内容大小与设置的大小作比较 */
  9. if (size.width < _size.width)
  10. {
  11. /* 如果新的内容大小比设置的要小,输出提示,并以设置的大小为准,大小不改变 */
  12. CCLOG("Inner width <= scrollview width,it will be force sized!");
  13. }
  14. else
  15. {
  16. /* 如果新的内容大小比设置的要大,则以新内容大小为准 */
  17. innerSizeWidth = size.width;
  18. }
  19. if (size.height < _size.height)
  20. {
  21. CCLOG("Inner height <= scrollview height,it will be force sized!");
  22. }
  23. else
  24. {
  25. innerSizeHeight = size.height;
  26. }
  27. _innerContainer->setSize(CCSize(innerSizeWidth+5,innerSizeHeight+10));
  28. }

在updateInnerContainerSize函数中我们以保存了实际内容大小,需要写一个get函数获取
  1. CCSize ListView::getActualInnerSize()
  2. {
  3. /* 重点:立即(当前帧)执行刷新,更新大小 */
  4. refreshView();
  5. return _actualInnerSize;
  6. }


最后实现需求:listView->setSize(getActualInnerSize())


上面是在CocoStudio添加的UIListView控件,如果是手动创建的话有三点注意:

为了能够滚动,要实现两个条件

①:setTouchEnable(true)

②:一定要将UIListView 放入到UILayer中,只有UILayer才会监听UI系列触摸,CCLayer不可以

所以需要创建一个UILayer* layer;layer->addWidget(list);//一定是addWidget,表示以挂件形式添加,addChild不可以,最后再addChild(layer);

③:向列表中添加控件时,列表会自动排好位置,此时位置是不受手动管理的(而且位置通常不对,中心点在左上角,我们无法改变,做相对偏移等);但有时候我们为了调整位置,只能添加中间层,如UILayout,而UILayout要注意的是,它相当于一个层,坐标计算和层一样。

猜你在找的Cocos2d-x相关文章