Listener 监听器

什么是监听器

Listener 监听器类用于监听某个事件的发生,并作出相应的处理。

例如,下面的代码创建了一个监听鼠标按下的事件:

// 监听器的回调函数
auto callback = [](Event* evt)
{
    // 判断事件类型是否是鼠标按下
    if (evt->type == Event::MouseDown)
    {
        // 鼠标按下事件发生了
        // 作出相应处理
    }
};

// 创建监听器
auto lis = gcnew Listener(callback);
// 让某个节点使用这个监听器
node->addListener(lis);

上面的代码也许晦涩难懂,简单来说,当一个事件发生时,事件会逐一发送给每一个节点(node),节点再将事件转发给监听器,监听器收到事件时会调用回调函数(callback)来处理这个事件。

监听器可以监听到的事件有以下几种:

// 事件类型枚举
enum Type
{
    MouseMove,  // 鼠标移动
    MouseDown,  // 鼠标按下
    MouseUp,    // 鼠标抬起
    MouseWheel, // 鼠标滚轮滑动
    KeyDown,    // 按键按下
    KeyUp       // 按键抬起
};

下面的回调函数示例给出了每种类型事件的处理方式:

// 监听器的回调函数
// 展示了所有事件的处理方式
auto callback = [](Event* evt)
{
    if (evt->type == Event::MouseMove)
    {
        // 鼠标移动事件发生了
        // 将事件类型转换为事件的实际类型
        auto mouseEvt = (MouseMoveEvent*)evt;
        mouseEvt->x;    // 鼠标 x 坐标
        mouseEvt->y;    // 鼠标 y 坐标
    }
    else if (evt->type == Event::MouseDown)
    {
        // 鼠标按下事件发生了
        // 将事件类型转换为事件的实际类型
        auto mouseEvt = (MouseDownEvent*)evt;
        mouseEvt->x;        // 鼠标 x 坐标
        mouseEvt->y;        // 鼠标 y 坐标
        mouseEvt->button;   // 鼠标按键键值 MouseCode
    }
    else if (evt->type == Event::MouseUp)
    {
        // 鼠标抬起事件发生了
        // 将事件类型转换为事件的实际类型
        auto mouseEvt = (MouseUpEvent*)evt;
        mouseEvt->x;        // 鼠标 x 坐标
        mouseEvt->y;        // 鼠标 y 坐标
        mouseEvt->button;   // 鼠标按键键值 MouseCode
    }
    else if (evt->type == Event::MouseWheel)
    {
        // 鼠标滚轮滑动事件发生了
        // 将事件类型转换为事件的实际类型
        auto mouseEvt = (MouseWheelEvent*)evt;
        mouseEvt->x;        // 鼠标 x 坐标
        mouseEvt->y;        // 鼠标 y 坐标
        mouseEvt->delta;    // 鼠标滚轮滑动幅度
    }
    else if (evt->type == Event::KeyDown)
    {
        // 键盘按下事件发生了
        // 将事件类型转换为事件的实际类型
        auto keyEvt = (KeyDownEvent*)evt;
        keyEvt->key;    // 键盘按键键值 KeyCode
        keyEvt->count;  // 按键触发次数
    }
    else if (evt->type == Event::KeyUp)
    {
        // 键盘抬起事件发生了
        // 将事件类型转换为事件的实际类型
        auto keyEvt = (KeyUpEvent*)evt;
        keyEvt->key;    // 键盘按键键值 KeyCode
        keyEvt->count;  // 按键触发次数
    }
};

使用监听器实现按钮

ButtonListener 按钮事件监听器提供了按钮基础功能,它可以添加到任何一个节点上,所以它可以让一段文字或者一个精灵变成按钮。

例如,下面的代码创建了一个点击后退出游戏的按钮

// 假设有一个精灵 sprite,把它变成退出游戏的按钮
// 创建点击按钮后的回调函数
auto callback = [](ButtonEvent evt)
{
    if (evt == ButtonEvent::Clicked)
    {
        // 点击了按钮,退出游戏
        Game::quit();
    }
};

// 给 sprite 添加监听器
auto lis = gcnew ButtonListener(callback);
sprite->addListener(lis);

// 游戏暂停时,让这个按钮继续工作
lis->ignoreGamePaused();

查看更多关于回调函数的内容

ButtonEvent 是按钮相关事件,它的定义如下

// 按钮事件
enum class ButtonEvent
{
	MouseEntered,  // 鼠标浮于按钮上
	MouseExited,   // 鼠标从按钮上移出
	Pressed,       // 鼠标按下
	Clicked,       // 鼠标点击
};

实现开关按钮

开关按钮用来实现有 “开” 和 “关” 两种状态的按钮,例如背景音乐的开关等,ToggleButtonListener 开关按钮事件监听器提供了开关按钮功能,它可以添加到任何一个节点上。

例如,下面的代码创建了一个可以控制音乐的播放与停止的按钮

// 假设有一个精灵 sprite,把它变成控制音乐播放的按钮
// 创建点击按钮后的回调函数
auto callback = [](ButtonEvent evt, bool state)
{
    if (evt == ButtonEvent::Clicked)
    {
        // 点击了按钮
        if (state)
        {
            // 按钮现在是打开状态,播放音乐
            MusicPlayer::resume("music.wav");
        }
        else
        {
            // 按钮现在是关闭状态,停止音乐
            MusicPlayer::pause("music.wav");
        }
    }
};

// 给 sprite 添加监听器
auto lis = gcnew ToggleButtonListener(true /* 默认是打开状态 */, callback);
sprite->addListener(lis);

// 游戏暂停时,让这个按钮继续工作
lis->ignoreGamePaused();

查看更多关于回调函数的内容

监听器的启动和暂停

创建监听器后,可以调用 startstop 函数启动和暂停一个监听器

auto lis = gcnew Listener();
// 启动监听器(默认就是启动状态)
lis->start();
// 暂停监听器
lis->stop();

监听器的添加和移除

创建监听器后,需要将其添加到节点上,才可以接收发送到该节点的事件:

auto lis = gcnew Listener();
// 添加监听器
node->addListener(lis);
// 移除监听器
node->removeListener(lis);

监听器可以有名字,通过名字可以启动、暂停、移除指定的监听器

auto lis = gcnew Listener();
node->addListener(lis);

// 设置监听器的名称
lis->setName("my_listener");
// 启动名称为 my_listener 的监听器
node->startListener("my_listener");
// 暂停名称为 my_listener 的监听器
node->stopListener("my_listener");
// 移除名称为 my_listener 的监听器
node->removeListener("my_listener");

也可以启动、暂停、移除一个节点上的所有监听器

// 启动所有监听器
node->startAllListeners();
// 暂停所有监听器
node->stopAllListeners();
// 移除所有监听器
node->removeAllListeners();

自定义事件

出了上面几种预先定义的事件,用户也可以自己定义事件。

下面的代码展示了如何创建自定义的事件:

// 自定义事件类型
enum MyEventType
{
    // 第一个类型,为预定义事件类型最后一个值 + 1
    First = Event::Type::Last + 1
};

// 自定义事件
struct MyEvent
	: public Event
{
    // 自定义事件的类型为 First
    MyEvent() : Event(MyEventType::First) {}

    // 自定义事件的成员变量
    int value = 0;
};

使用下面的代码将自定义事件分发给节点及其所有子节点:

// 创建自定义事件
MyEvent* evt = gcnew MyEvent;
// 设置自定义事件的成员变量
evt->value = 3;
// 分发该事件,事件会发送给节点及其所有子节点
node->dispatch(evt);

在监听器中处理自定义事件:

// 回调函数处理
auto callback = [](Event* evt)
{
    // 判断事件类型是否是鼠标按下
    if (evt->type == Event::MouseDown)
    {
        // 自定义事件发生了
        // 将事件类型转换为事件的实际类型
        auto myEvt = (MyEvent*)evt;
        myEvt->value;   // 获取自定义事件的成员变量
    }
};

这个页面对你有帮助吗?