天天看點

cocos2d-x改底層之擷取UIListView的實際内容大小

實際項目中UI界面中經常會用到UIListView,大多會在CocoStudio中直接添加這個控件。但是在使用中發現了一些坑和功能缺乏,然後就看了一下底層的邏輯,發現稍微改一下底層就可以滿足需求,是以下面就針對需求來分析UIListView的底層,同時做一些改動。

需求:根據連結清單中的内容來動态調整listView本身的大小

首先,我們要知道,我們插入和移除連結清單中的一項,listView本身會如何處理:

void ListView::pushBackDefaultItem()
{
    if (!_model)
    {
        return;
    }
    /* 克隆一份模闆,并添加到項的數組裡 */
    Widget* newItem = _model->clone();
    _items->addObject(newItem);
    /* 根據listView的基礎設定來調整新加項的布局關系 */
    remedyLayoutParameter(newItem);
    addChild(newItem);
    /* 重點:打開重新整理開關 */
    _refreshViewDirty = true;
}
           

這裡最後一句才是重點,隻有重新整理了才會真正計算新的顯示,之前的改動才真正生效,是以放我們添加一項的時候,目前幀其實并沒有立即重新整理,如果這時候擷取大小,隻會和之前的一樣,并沒有改變,那麼我們要知道,開關_refreshViewDirty是在什麼時候起作用了呢,如下:

void ListView::sortAllChildren()
{
    ScrollView::sortAllChildren();
    if (_refreshViewDirty)
    {
        /* 重新整理 */
        refreshView();
        _refreshViewDirty = false;
    }
}
           
void ListView::refreshView()
{
    ccArray* arrayItems = getItems()->data;
    int length = arrayItems->num;
    for (int i=0; i<length; i++)
    {
        Widget* item = static_cast<Widget*>(arrayItems->arr[i]);
        item->setZOrder(i);
        remedyLayoutParameter(item);
    }
    /* 更新内容大小 */
    updateInnerContainerSize();
}
           

可以看到,最關鍵的改變大小的函數updateInnerContainerSize():

定義一個變量用來儲存真實大小,原因是listView本身計算大小的結果并不是以内容為準,而是以最初使用者設定的大小,那麼真實的大小會被遺棄,是以我們要儲存住她:

CCSize _actualInnerSize;
           
void ListView::updateInnerContainerSize()
{
    switch (_direction)
    {
        case SCROLLVIEW_DIR_VERTICAL:
        {
            /*...*/
            
            /* 儲存真實大小 */
            _actualInnerSize = CCSize(finalWidth, finalHeight);
            setInnerContainerSize(_actualInnerSize);
            break;
        }
        case SCROLLVIEW_DIR_HORIZONTAL:
        {
            /*...*/
            
            /* 儲存真實大小 */
            _actualInnerSize = CCSize(finalWidth, finalHeight);
            setInnerContainerSize(_actualInnerSize);
            break;
        }
        default:
            break;
    }
}
           

setInnerContainerSize ( _actualInnerSize );這個函數是在父類定義的:

void ScrollView::setInnerContainerSize(const CCSize &size)
{
    /* 擷取使用者設定的大小(沒設定就是預設的) */
    float innerSizeWidth = _size.width;
    float innerSizeHeight = _size.height;
    /* 擷取原始大小 */
    CCSize originalInnerSize = _innerContainer->getSize();
    /* 更新後的新的内容大小與設定的大小作比較 */
    if (size.width < _size.width)
    {
        /* 如果新的内容大小比設定的要小,輸出提示,并以設定的大小為準,大小不改變 */
        CCLOG("Inner width <= scrollview width, it will be force sized!");
    }
    else
    {
        /* 如果新的内容大小比設定的要大,則以新内容大小為準 */
        innerSizeWidth = size.width;
    }
    if (size.height < _size.height)
    {
        CCLOG("Inner height <= scrollview height, it will be force sized!");
    }
    else
    {
        innerSizeHeight = size.height;
    }
    _innerContainer->setSize(CCSize(innerSizeWidth+5, innerSizeHeight+10));
}
           

在updateInnerContainerSize函數中我們以儲存了實際内容大小,需要寫一個get函數來擷取:

CCSize ListView::getActualInnerSize()
{
    /* 重點:立即(目前幀)執行重新整理,更新大小 */
	refreshView();
    return _actualInnerSize;
}
           

最後實作需求:listView->setSize(getActualInnerSize())

上面是在CocoStudio中添加的UIListView控件,如果是手動建立的話有三點注意:

為了能夠滾動,要實作兩個條件

①:setTouchEnable(true)

②:一定要将UIListView 放入到UILayer中,隻有UILayer才會監聽UI系列觸摸,CCLayer不可以

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

③:向清單中添加控件時,清單會自動排好位置,此時位置是不受手動管理的(而且位置通常不對,中心點在左上角,我們無法改變,做相對偏移等);但有時候我們為了調整位置,隻能添加中間層,如UILayout,而UILayout要注意的是,它相當于一個層,坐标計算和層一樣。

繼續閱讀