從零開始のcocos2dx生活(十)ScrollView
目錄
- 簡介
- 基礎變數
- ScrollViewDelegate
- Direction
- _dragging
- _container
- _touchMoved
- _bounceable
- _touchLength
- 方法
- create
- setContentSize
- deaccelerateScrolling
- maxContainerOffset 和 minContainerOffset
- 觸控的各階段
- onTouchBegan
- onTouchMoved
- onTouchEnded
簡介
scrollView是在一定可視範圍內通過滾動看到更大範圍的方法,可視的範圍是繫結在滾動檢視上的容器。
容器有兩個界限,一個是容器偏移,一個是為了回彈設定的延伸的長度。
基礎變數
ScrollViewDelegate
設定委託函式例項,繼承並重寫下面的方法,可以在滾動和縮放時使用回撥函式
virtual void scrollViewDidScroll(ScrollView* view) {}; virtual void scrollViewDidZoom(ScrollView* view) {};
//使用
scrollView->setDelegate(this); ///<新增委託
virtual void scrollViewDidScroll(ScrollView* view)
{
/* */
}
Direction
設定滾動的方向
enum class Direction
{
NONE = -1,
HORIZONTAL = 0,
VERTICAL,
BOTH
};
_dragging
是否開始拖動的標誌,在onTouchBegan時會設為true,表示開始拖動,在onTouchEnded、onTouchCancelled中設為false
_container
作為scrollView的子節點,存放顯示的所有內容,滾動檢視的滾動框就是在這個上面進行滾動的。Inset
inset分為_minInset和_maxInset,如果設定了回彈會被設定成偏移邊界加上可視範圍的20%
_touchMoved
標記正在拖動的標誌,在onTouchMoved時被設為true,在onTouchEnded、onTouchCancelled中設為false
_bounceable
回彈,在初始化時預設被設為true,是指在滑動到container的邊界之後,會繼續滑動一截最後再彈回到邊界處的一種效果。
_touchLength
用來計算兩個觸控點之間的距離,會換算成縮放的倍數
方法
create
建立的時候可以將設定好的設定好的container作為引數,將容器繫結到滾動檢視中,然後呼叫initWithViewSize方法來初始化滾動檢視
initWithViewSize
初始化時呼叫
如果沒有傳入container引數會建立一個
setContentSize
這個方法主要是為了設定容器的大小,同時也重新整理了Inset的大小,在呼叫setContentSize之前,minInset和maxInset都是0,沒有被設定,setContentSize會呼叫updateInset方法,對minInset和maxInset進行了設定,讓回彈可以進行,讓deaccelerateScrolling可以獲得正確的值。
void ScrollView::setContentSize(const Size & size)
{
if (this->getContainer() != nullptr)
{
this->getContainer()->setContentSize(size);
this->updateInset();
}
}
void ScrollView::updateInset()
{
if (this->getContainer() != nullptr)
{
_maxInset = this->maxContainerOffset();
_maxInset.set(_maxInset.x + _viewSize.width * INSET_RATIO,
_maxInset.y + _viewSize.height * INSET_RATIO);
_minInset = this->minContainerOffset();
_minInset.set(_minInset.x - _viewSize.width * INSET_RATIO,
_minInset.y - _viewSize.height * INSET_RATIO);
}
}
deaccelerateScrolling
在onTouchEnded中會呼叫這個方法來實現甩出的效果。在onTouchMoved中設定了scrollDistance引數,意思是鬆手前一幀內觸控點移動的距離,每次會將容器當前的位置加上scrollDisdtance更新位置,然後再將這個距離乘以一個引數讓它變小,實現甩出逐漸減速的效果。
void ScrollView::deaccelerateScrolling(float /*dt*/)
{
if (_dragging)
{
this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
return;
}
float newX, newY;
Vec2 maxInset, minInset;
//設定容器的位置
_container->setPosition(_container->getPosition() + _scrollDistance);
//有回彈就使用延伸出去的距離
if (_bounceable)
{
maxInset = _maxInset;
minInset = _minInset;
}
//沒有回彈就是用最大偏移的距離
else
{
maxInset = this->maxContainerOffset();
minInset = this->minContainerOffset();
}
newX = _container->getPosition().x;
newY = _container->getPosition().y;
//逐漸縮小
_scrollDistance = _scrollDistance * SCROLL_DEACCEL_RATE;
this->setContentOffset(Vec2(newX,newY));
//減速並回彈至設定最大偏移處
//移動是否小於預定值
//位置是否超出設定的延伸量
if ((fabsf(_scrollDistance.x) <= SCROLL_DEACCEL_DIST &&
fabsf(_scrollDistance.y) <= SCROLL_DEACCEL_DIST) ||
((_direction == Direction::BOTH || _direction == Direction::VERTICAL) && (newY >= maxInset.y || newY <= minInset.y)) ||
((_direction == Direction::BOTH || _direction == Direction::HORIZONTAL) && (newX >= maxInset.x || newX <= minInset.x)))
{
//取消每幀減速重新整理
this->unschedule(CC_SCHEDULE_SELECTOR(ScrollView::deaccelerateScrolling));
//重新設定容器的偏移
this->relocateContainer(true);
}
}
maxContainerOffset 和 minContainerOffset
程式碼中有些地方對container的錨點和忽略錨點影響重新設定了,但不管怎麼設定,它的錨點都是(0, 0)。
所以可以知道maxContainerOffset一直是都是(0, 0),除非是又重新設定了。
這裡其實有些難理解,最大容器偏移量指的是手指(滑鼠)按住向右滑動,container的左邊界相對於view的左邊界的偏移。
對於minContainerOffset來說也是container的左邊界相對於view的左邊界的偏移,其值是負值,從程式碼來看是viewSize - 容器的大小。
向左滑動是minContainerOffset
向右滑動是maxContainerOffset
觸控的各階段
onTouchBegan
1、要求觸控點是一個或者兩個,沒有在移動,包含在view的區域內
2、如果沒有加到touches陣列中,就加進去,用來在後面判斷觸控點個數使用
3、如果是單點觸控touchMoved設為false,dragging設為true,scrollDistance設為0,touchLength設為0
4、如果是兩點縮放,會記錄初始狀態時的 兩點中點位置 和 兩點之間的距離
onTouchMoved
單點觸控
1、獲取這一幀內觸控點的移動距離
2、對三種不同的拖動方向,判斷拖動的距離是否超出偏移範圍
3、如果是第一次touchMoved並且長度小於設定的值,直接返回
4、如果是第一次touchMoved會將moveDistance設為0,影響是對第一幀移動時的移動設為了0,實際看不出來
5、記錄了新的觸控點,將touchMoved設為true
6、對三種不同的拖動方向,分別設定了移動距離
7、設定新的移動偏移
兩點縮放
1、獲取當前兩點之間的距離
2、用 當前zoomScale * 當前兩點距離 / 開始時兩點距離 獲得設定縮放的引數,進入到setZoomScale中
setZoomScale
1、獲取當前的兩個觸控點的中點,如果是觸控長度是0,則中點為可視區域的中點,否則為兩觸控點中點
2、
//在縮放前將觸控點中點座標轉換到節點座標系
oldCenter = _container->convertToNodeSpace(center);
//執行縮放
_container->setScale(MAX(_minScale, MIN(_maxScale, s)));
//因為是按照(0,0)點縮放的,原來的觸控中點會發生改變,這個時候重新轉換觸控中點的位置到世界座標系
newCenter = _container->convertToWorldSpace(oldCenter);
3、計算縮放前後的觸控點中點的差值,作為偏移量
4、使用容器的位置加偏移量作為容器的新偏移
onTouchEnded
配合的accelerateScrolling使用,每次觸控結束會呼叫accelerateScrolling來實現甩出的效