原文地址: http://blog.csdn.net/u011225840/article/details/30032379
1.继承结构
首先来看下CCTableView的继承结构
从继承结构上看,CCTableView是一种CCScrollView,所以为了研究CCTableView的源码,清先去了解CCScrollView的源码http://blog.csdn.net/u011225840/article/details/30033501。
其次,CCTableView也继承了CCScrollViewDelegate,从后面的源码分析中,我们可以看出主要是为了实现scrollViewDidScroll这个函数。从而使用CCScrollView的滚动时,可以实现CCTableView自己本身的操作。(如果你看到这里不懂,请务必先弄懂CCScrollView的源码。)
最后,除了继承结构,我们还需要了解三个重要的类。
CCTableViewCell,CCTableViewDelegate,CCTableViewDataSource。通过这三个类,CCTableView将数据与其他操作解耦。
2.相关类的分析
2.1CCTableViewCell
CCtableViewCell主要是含有一个唯一的标识符,允许TableView通过不同的idx来更新TableviewCell。
一般情况下,会写一个CustomCell来继承该类,该Cell上有每一个cell的样式(含有label?含有sprite?全在该cell中实现)
- class CCTableViewCell: public CCNode, public CCSortableObject
- {
- public:
- CCTableViewCell() {}
-
-
-
- unsigned int getIdx();
- void setIdx(unsigned int uIdx);
-
-
-
- void reset();
-
- void setObjectID(unsigned int uIdx);
- unsigned int getObjectID();
- private:
- unsigned int m_uIdx;
- };
2.2CCTableViewDataSource
CCTableViewDataSource是非常重要的一个类,TableView的数据相关的处理都与该类有关,请看他提供的四个函数,注释已经给出哦。
一般情况下,我们会让一个Custom类来继承他并实现方法。该Custom类一般是继承DataSource,TableViewDelegate,与一个CClayer,并含有一个CCTableView。
(在文章的最后,我会给出一个例子)。
-
- virtual CCSize tableCellSizeForIndex(CCTableView *table, unsigned int idx) {
- return cellSizeForTable(table);
- };
-
-
- virtual CCSize cellSizeForTable(CCTableView *table) {
- return CCSizeZero;
- };
-
-
- virtual CCTableViewCell* tableCellAtIndex(CCTableView *table, unsigned int idx) = 0;
-
-
- virtual unsigned int numberOfCellsInTableView(CCTableView *table) = 0;
2.3 CCTableViewDelegate
提供了几个Delegate函数,以供TableView使用。Delegate的用法我在CCScrollView源码分析中已经说过,这里不再赘述。
virtual void tableCellTouched(CCTableView* table, CCTableViewCell* cell) = 0;
这里只说下必须实现的这个函数,当table通过idx获取用户正在触摸该cell后,一定会调用该方法。(选择某个物件后,给人物穿上,就是通过这个方法来响应。)
3.CCTableView源码分析
3.1创建时
CCTableView提供了两个create函数
create(CCTableViewDataSource* dataSource, CCSize size);
create(CCTableViewDataSource* dataSource, CCSize size, CCNode *container);
第一个函数,调用了第二个,将container置为NULL。
下面来看第二个函数。
- CCTableView* CCTableView::create(CCTableViewDataSource* dataSource, CCSize size, CCNode *container)
- {
- CCTableView *table = new CCTableView();
- table->initWithViewSize(size, container);
- table->autorelease();
- table->setDataSource(dataSource);
- table->_updateCellPositions();
- table->_updateContentSize();
-
- return table;
- }
话说这种风格也不怕堆内存空间不足么。
发现三个重要的函数:
3.1.1 initWithViewSize
- bool CCTableView::initWithViewSize(CCSize size, CCNode* container)
- {
- if (CCScrollView::initWithViewSize(size,container))
- {
- m_pCellsUsed = new CCArrayForObjectSorting();
- m_pCellsFreed = new CCArrayForObjectSorting();
- m_pIndices = new std::set<unsigned int>();
- m_eVordering = kCCTableViewFillBottomUp;
- this->setDirection(kCCScrollViewDirectionVertical);
-
- CCScrollView::setDelegate(this);
- return true;
- }
- return false;
- }
cellsUsed是用来存放正在使用的,显示在view上面的cell。
cellsFreed是用来存放暂时不使用的,没在view上面显示的cell(从cellsUsed被移除后添加进cellsFreed),cellsFreed提供了一种缓存机制。允许我们从tableCellAtIndex中拿到cells,不需要重新创建他,只需要根据idx更新下显示。
indices是用来存放每个cell应该占据的位置区域值。
3.1.2 _updateCellPositions
根据CCTableView呈现的方向以及order,给indices赋值。
- void CCTableView::_updateCellPositions() {
-
-
-
-
- int cellsCount = m_pDataSource->numberOfCellsInTableView(this);
- m_vCellsPositions.resize(cellsCount + 1, 0.0);
-
-
- if (cellsCount > 0)
- {
- float currentPos = 0;
- CCSize cellSize;
- for (int i=0; i < cellsCount; i++)
- {
- m_vCellsPositions[i] = currentPos;
- CCLog("The postion is %f",currentPos);
-
- cellSize = m_pDataSource->tableCellSizeForIndex(this, i);
-
- switch (this->getDirection())
- {
- case kCCScrollViewDirectionHorizontal:
- currentPos += cellSize.width;
- break;
- default:
- currentPos += cellSize.height;
- break;
- }
- }
-
- m_vCellsPositions[cellsCount] = currentPos;
- CCLog("The postion is %f",currentPos);
- }
-
- }
3.1.3 _updateContentSize
这个方法调整了CCTableView的大小与偏移。(注意,调整偏移的时候,会调用scrollViewDidScroll方法。)
- void CCTableView::_updateContentSize()
- {
- CCSize size = CCSizeZero;
- unsigned int cellsCount = m_pDataSource->numberOfCellsInTableView(this);
-
-
- if (cellsCount > 0)
- {
- float maxPosition = m_vCellsPositions[cellsCount];
-
- switch (this->getDirection())
- {
- case kCCScrollViewDirectionHorizontal:
- size = CCSizeMake(maxPosition, m_tViewSize.height);
- break;
- default:
- size = CCSizeMake(m_tViewSize.width, maxPosition);
- break;
- }
- }
-
-
- this->setContentSize(size);
-
-
- if (m_eOldDirection != m_eDirection)
- {
- if (m_eDirection == kCCScrollViewDirectionHorizontal)
- {
- this->setContentOffset(ccp(0,0));
- }
- else
- {
-
- this->setContentOffset(ccp(0,this->minContainerOffset().y));
- }
- m_eOldDirection = m_eDirection;
- }
-
- }
如注释所示,我有个小问题,如果是垂直方向的,则会把初始位置放到minContainerOffset上,为何这样,没懂。。。
下面一节重点讲解scrollviewDidScroll
3.2 滚动时
根据父类CCScrollView,每次设置偏移后,会调用scrollviewDidScroll方法。
该函数中,调用了三个内部函数:
3.2.1 _indexFromOffset
- unsigned int CCTableView::_indexFromOffset(CCPoint offset)
- {
- int index = 0;
- const int maxIdx = m_pDataSource->numberOfCellsInTableView(this)-1;
-
-
- if (m_eVordering == kCCTableViewFillTopDown)
- {
- offset.y = this->getContainer()->getContentSize().height - offset.y;
- }
-
- index = this->__indexFromOffset(offset);
- if (index != -1)
- {
- index = MAX(0, index);
- if (index > maxIdx)
- {
- index = CC_INVALID_INDEX;
- }
- }
-
- return index;
- }
-
- int CCTableView::__indexFromOffset(CCPoint offset)
- {
- int low = 0;
- int high = m_pDataSource->numberOfCellsInTableView(this) - 1;
- float search;
-
- switch (this->getDirection())
- {
- case kCCScrollViewDirectionHorizontal:
- search = offset.x;
- break;
- default:
- search = offset.y;
- break;
- }
-
- while (high >= low)
- {
- int index = low + (high - low) / 2;
- float cellStart = m_vCellsPositions[index];
- float cellEnd = m_vCellsPositions[index + 1];
- CCLog("The start cell is %f",cellStart);
- if (search >= cellStart && search <= cellEnd)
- {
- return index;
- }
- else if (search < cellStart)
- {
- high = index - 1;
- }
- else
- {
- low = index + 1;
- }
- }
-
- if (low <= 0) {
- return 0;
- }
- <span style="white-space:pre"> </span>
- return -1;
- }
3.2.2 _moveCellOutOfSight
- void CCTableView::_moveCellOutOfSight(CCTableViewCell *cell)
- {
-
- if(m_pTableViewDelegate != NULL) {
- m_pTableViewDelegate->tableCellWillRecycle(this, cell);
- }
-
-
- m_pCellsFreed->addObject(cell);
- m_pCellsUsed->removeSortedObject(cell);
- m_pIndices->erase(cell->getIdx());
-
- cell->reset();
- if (cell->getParent() == this->getContainer()) {
- this->getContainer()->removeChild(cell, true);;
- }
- }
3.2.3 updateCellAtIndex
- void CCTableView::updateCellAtIndex(unsigned int idx)
- {
- if (idx == CC_INVALID_INDEX)
- {
- return;
- }
-
- unsigned int uCountOfItems = m_pDataSource->numberOfCellsInTableView(this);
- if (0 == uCountOfItems || idx > uCountOfItems-1)
- {
- return;
- }
-
- CCTableViewCell* cell = this->cellAtIndex(idx);
- if (cell)
- {
- this->_moveCellOutOfSight(cell);
- }
-
- cell = m_pDataSource->tableCellAtIndex(this, idx);
-
- this->_setIndexForCell(idx, cell);
-
- this->_addCellIfNecessary(cell);
- }
可以看出,update时,先将所有cell移出,再通过dataSource的tableCellAtIndex来获取更新cell。如果不设置缓存freed,会造成性能瓶颈。
- void CCTableView::_setIndexForCell(unsigned int index, CCTableViewCell *cell)
- {
-
- cell->setAnchorPoint(ccp(0.0f, 0.0f));
- cell->setPosition(this->_offsetFromIndex(index));
- CCLog("The cell position is %f",this->_offsetFromIndex(index).x);
- cell->setIdx(index);
- }
- void CCTableView::_addCellIfNecessary(CCTableViewCell * cell)
- {
- if (cell->getParent() != this->getContainer())
- {
- this->getContainer()->addChild(cell);
- }
- m_pCellsUsed->insertSortedObject(cell);
- m_pIndices->insert(cell->getIdx());
-
- }
看到这里,基本上CCTableView的重点已经看完了。下面继续。
3.3 触摸
3.3.1 ccTouchBegan
- bool CCTableView::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
- {
- if (!this->isVisible()) {
- return false;
- }
-
- bool touchResult = CCScrollView::ccTouchBegan(pTouch, pEvent);
-
-
- if(m_pTouches->count() == 1) {
- unsigned int index;
- CCPoint point;
-
-
- point = this->getContainer()->convertTouchToNodeSpace(pTouch);
-
- index = this->_indexFromOffset(point);
-
- if (index == CC_INVALID_INDEX)
- {
- m_pTouchedCell = NULL;
- }
- else
- {
- m_pTouchedCell = this->cellAtIndex(index);
- }
-
- if (m_pTouchedCell && m_pTableViewDelegate != NULL) {
- m_pTableViewDelegate->tableCellHighlight(this, m_pTouchedCell);
- }
- }
-
- else if(m_pTouchedCell) {
- if(m_pTableViewDelegate != NULL) {
- m_pTableViewDelegate->tableCellUnhighlight(this, m_pTouchedCell);
- }
-
- m_pTouchedCell = NULL;
- }
-
- return touchResult;
- }
3.3.2 ccTouchMoved
- void CCTableView::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent)
- {
-
- CCScrollView::ccTouchMoved(pTouch, pEvent);
-
- if (m_pTouchedCell && isTouchMoved()) {
- if(m_pTableViewDelegate != NULL) {
- m_pTableViewDelegate->tableCellUnhighlight(this, m_pTouchedCell);
- }
-
- m_pTouchedCell = NULL;
- }
- }
3.3.3 ccTouchEnded
- void CCTableView::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
- {
- if (!this->isVisible()) {
- return;
- }
-
- if (m_pTouchedCell){
- CCRect bb = this->boundingBox();
- bb.origin = m_pParent->convertToWorldSpace(bb.origin);
-
- if (bb.containsPoint(pTouch->getLocation()) && m_pTableViewDelegate != NULL)
- {
- m_pTableViewDelegate->tableCellUnhighlight(this, m_pTouchedCell);
- m_pTableViewDelegate->tableCellTouched(this, m_pTouchedCell);
- }
-
- m_pTouchedCell = NULL;
- }
-
- CCScrollView::ccTouchEnded(pTouch, pEvent);
- }
3.4 常见的操作
3.4.1 reloadData
- void CCTableView::reloadData()
- {
- m_eOldDirection = kCCScrollViewDirectionNone;
- CCObject* pObj = NULL;
- CCARRAY_FOREACH(m_pCellsUsed, pObj)
- {
- CCTableViewCell* cell = (CCTableViewCell*)pObj;
-
- if(m_pTableViewDelegate != NULL) {
- m_pTableViewDelegate->tableCellWillRecycle(this, cell);
- }
-
- m_pCellsFreed->addObject(cell);
- cell->reset();
- if (cell->getParent() == this->getContainer())
- {
- this->getContainer()->removeChild(cell, true);
- }
- }
-
- m_pIndices->clear();
- m_pCellsUsed->release();
- m_pCellsUsed = new CCArrayForObjectSorting();
-
- this->_updateCellPositions();
- this->_updateContentSize();
- if (m_pDataSource->numberOfCellsInTableView(this) > 0)
- {
- this->scrollViewDidScroll(this);
- }
- }
当你的数据源发生改变后,请调用reloadData。从中可以看出,他重新计算了与数据相关的操作。
3.4.2 refreshData
- void CCTableView::refreshData()
- {
- int startIndex = 0;
- int endIndex = 0;
-
- getStartEndIndex(startIndex, endIndex);
-
-
-
- for(unsigned int i = startIndex; i <= endIndex; ++i)
- {
- this->updateCellAtIndex(i);
- }
- }
区别是显而易见的,数据源没有发生改变,只是startIndex和endIndex发生了改变,并更新显示。
4.小结
1. CCTableView三基友:
CCTableViewCell,负责单个cell,含有唯一idx用于区别。
CCTableViewDataSource,负责数据源相关,包括数据个数,数据根据不同idx的获取,数据size等。
CCTableViewDelegate,负责delegate操作。
2.CCTableView不但继承了CCScrollView,同时也继承了CCScrollViewDelegate。