Galoisplusplus

A fan of science, technology and Classical music.

为cocos2d-x的TableViewCell控件添加点击及长按支持

| Comments

再次来聊一聊cocos2d-x的TableView。之前之所以用继承TableView的方式实现2D的GridView是因为用到TableView点击事件的地方涉及到全局数据的复杂处理,这块处理放到TableViewDelegate类的tableCellTouched比较合理。但这个函数在cell被点击时都会被调用,而实际游戏里一个cell往往只有部分UI才能被点击(所以当点击事件只与这个cell的数据相关时,直接把点击事件处理放到cell内部实现才是王道),故而我们需要对cell的点击区域做进一步的限制。为此,我封装了一个TableViewCell的子类来实现这一功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
TouchableTableViewCell::TouchableTableViewCell():
    isValidTouched_(false),
    touchableNode_(nullptr)
{}

TouchableTableViewCell::~TouchableTableViewCell()
{}

bool TouchableTableViewCell::init()
{
    if (!TableViewCell::init()) {
        return false;
    }
    return true;
}

void TouchableTableViewCell::initTouchListener()
{
    auto touchListener = EventListenerTouchOneByOne::create();
    CC_SAFE_RETAIN(touchListener);
    Rect validTouchedRect;
    validTouchedRect.size = touchableNode_->getContentSize();
    touchListener->onTouchBegan = [=] (cocos2d::Touch* touch, cocos2d::Event* event) {
        if (touchableNode_ == nullptr) {
            return false;
        }
        auto touchLocation = touch->getLocation();
        auto localLocation = touchableNode_->convertToNodeSpace(touchLocation);
        if (validTouchedRect.containsPoint(localLocation)) {
            isValidTouched_ = true;
            return true;
        } else {
            isValidTouched_ = false;
            return false;
        }
    };
    touchListener->onTouchMoved = [=] (cocos2d::Touch* touch, cocos2d::Event* event) {
        auto touchLocation = touch->getLocation();
        auto localLocation = touchableNode_->convertToNodeSpace(touchLocation);
        if (validTouchedRect.containsPoint(localLocation)) {
            isValidTouched_ = true;
        } else {
            isValidTouched_ = false;
        }
    };
    touchListener->onTouchEnded = touchListener->onTouchMoved;
    touchListener->onTouchCancelled = touchListener->onTouchEnded;
    _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
}

void TouchableTableViewCell::setTouchableNode(cocos2d::Node* touchableNode)
{
    touchableNode_ = touchableNode;
    initTouchListener();
}

使用时只需让实际的TableViewCell类继承TouchableTableViewCell,在tableCellTouched函数中判断isValidTouched_即可。

另外,有时候我们需要让TableViewCell响应长按事件,我在之前的TouchableTableViewCell类上又做了一层封装,主要使用调度器来实现长按:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
const std::string LongTouchableTableViewCell::SCHEDULE_KEY = "LONG_TOUCHABLE_TABLE_VIEW_CELL";
const float LongTouchableTableViewCell::LONG_TOUCH_INTERVAL = 0.2f;

LongTouchableTableViewCell::LongTouchableTableViewCell():
    TouchableTableViewCell(),
    isTouchHold_(false),
    isLongTouched_(false),
    longTouchedCallback_(nullptr)
{}

LongTouchableTableViewCell::~LongTouchableTableViewCell()
{}

bool LongTouchableTableViewCell::init()
{
    if (!TouchableTableViewCell::init()) {
        return false;
    }
    return true;
}

void LongTouchableTableViewCell::initTouchListener()
{
    auto touchListener = EventListenerTouchOneByOne::create();
    CC_SAFE_RETAIN(touchListener);
    Rect validTouchedRect;
    validTouchedRect.size = touchableNode_->getContentSize();
    touchListener->onTouchBegan = [=] (cocos2d::Touch* touch, cocos2d::Event* event) {
        if (touchableNode_ == nullptr) {
            return false;
        }
        auto touchLocation = touch->getLocation();
        auto localLocation = touchableNode_->convertToNodeSpace(touchLocation);
        if (validTouchedRect.containsPoint(localLocation)) {
            isValidTouched_ = true;
            isTouchHold_ = true;
            isLongTouched_ = false;
            
            Director::getInstance()->getScheduler()->schedule([=](float) {
                if (isTouchHold_) {
                    isLongTouched_ = true;
                    if (longTouchedCallback_ != nullptr) {
                        longTouchedCallback_();
                    }
                }
                Director::getInstance()->getScheduler()->unschedule(SCHEDULE_KEY, this);
            }, this, LONG_TOUCH_INTERVAL, 0, 0.0f, false, SCHEDULE_KEY);

            return true;
        } else {
            isValidTouched_ = false;
            isTouchHold_ = false;
            isLongTouched_ = false;

            return false;
        }
    };
    touchListener->onTouchMoved = [=] (cocos2d::Touch* touch, cocos2d::Event* event) {
        isTouchHold_ = false;
        isLongTouched_ = false;

        auto touchLocation = touch->getLocation();
        auto localLocation = touchableNode_->convertToNodeSpace(touchLocation);
        if (validTouchedRect.containsPoint(localLocation)) {
            isValidTouched_ = true;
        } else {
            isValidTouched_ = false;
        }
    };
    touchListener->onTouchEnded = [=] (cocos2d::Touch* touch, cocos2d::Event* event) {
        isTouchHold_ = false;

        auto touchLocation = touch->getLocation();
        auto localLocation = touchableNode_->convertToNodeSpace(touchLocation);
        if (validTouchedRect.containsPoint(localLocation)) {
            isValidTouched_ = true;
        } else {
            isValidTouched_ = false;
        }
    };
    touchListener->onTouchCancelled = touchListener->onTouchEnded;
    _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
}

完整代码详见: cocos2d-x-TouchableTableViewCell

Comments