1. 程式人生 > >從零開始のcocos2dx生活(十)ScrollView

從零開始の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來實現甩出的效