2016年4月26日 星期二

0426 對話框製作教學 第二種方式


充分利用C++11特性 Cocos2d-x對話框的實現

一般一個對話框會分為模態對話框和非模態對話框兩種,模態對話框是指在用戶想要對對話框以外的應用程序進行操作時,必須首先對該對話框進行響應。如單擊【確定】或【取消】按鈕等將該對話框關閉。非模態(Modeless)對話框,又叫做無模式對話框,當用戶打開非模態對話框時,依然可以操作其他窗口。例如,Windows提供的記事本程序中的【查找】對話框。(摘自百度百科)

目前我需要的一個對話框功能列表如下:
1.需要顯示一段提示信息,可能是一段對話可能是一個問題。  
2.必須有確認按鈕,可能會有取消按鈕  
3.確認按鈕的功能可自定義  

下面是我的實現思路:
1.首先有一個初始化對話框的函數,將對話框初始化到內存中,因為對話框是一個很常用的功能,所以直接加入內存中就好了。  
2.然後對話框需要載入功能和移除功能,這兩樣就是對話框的核心功能了  
3.對話框需要按鈕回調,一個是確認,一個是取消,確認可能還需要執行某些其他功能,我們可以用回調來實現,取消就直接關閉對話框,沒什麼好說的。  
4.對話框分模態和非模態兩種,模態就是在對話框層中加一個觸摸屏蔽。  

遵循著上面的思路,我對於對話框的函數設計如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//對話框
protected:
//初始化對話框
void InitDialog();
/* 加載對話框
 * @parm:msg 消息 顯示在界面上的提示消息
 * @parm:fun 回調函數 給確定按鈕調用的
 * @parm:isNeedCancel 是否需要取消按鈕
 * @parm:isModalDialog 是否是模態對話框
 */
void LoadDialog(std::string msg,std::function<void(Ref*)>  fun,bool isNeedCancel = false,bool isModalDialog = true);
//對話框加載完成
void LoadDialogFinish(Armature *a, MovementEventType b, std::string c);
//隱藏對話框
void HideDialog();
//隱藏對話框完成
void HideDialogFinish(Armature *a, MovementEventType b, std::string c);
//對話框取消按鈕回調
void PressDialogCancelBtn(Ref *pSender, Widget::TouchEventType type);
//對話框確認按鈕回調
void PressDialogConfirmBtn(Ref *pSender, Widget::TouchEventType type);
為什麼會有對話框加載完成和對話框隱藏完成呢?是因為我把對話框的加載和隱藏做成了兩個動畫,所以需要這兩個附加的函數。

OK 不多說,下面是函數的具體實現:
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//----------------------------------------------------------對話框-------------------------------------------------------
//初始化對話框
void MomoScene::InitDialog()
{
    ArmatureDataManager::getInstance()->addArmatureFileInfo("publish/DialogAnimation.ExportJson");    
    Dialog = Armature::create("DialogAnimation");    
    Dialog->retain();
    Dialog->setPosition(getCenterPoint());
    m_UI_Dialog->addChild(Dialog);  
    Dialog->setVisible(false);
    //加載信息
    m_DialogMsg = Label::create();
    m_DialogMsg->retain();
    m_DialogMsg->setVisible(false);
    m_DialogMsg->setColor(Color3B::WHITE);
    m_DialogMsg->enableGlow(Color4B::WHITE);
    m_DialogMsg->setPosition(Point(0,0));
    Dialog->addChild(m_DialogMsg,20);
    //加載確認按鈕
    m_DialogConfirm = Button::create("Dialog_btn/confirm.png","Dialog_btn/confirm_press.png");
    m_DialogConfirm->retain();
    m_DialogConfirm->setPosition(Point(-80,-100));
    m_DialogConfirm->setVisible(false);
    Dialog->addChild(m_DialogConfirm);
    //加載取消按鈕
    m_DialogCancel = Button::create("Dialog_btn/cacel.png","Dialog_btn/cacel_press.png");
    m_DialogCancel->retain();
    m_DialogCancel->setPosition(Point(80,-100));
    m_DialogCancel->setVisible(false);
    Dialog->addChild(m_DialogCancel);
}
//加載對話框
void MomoScene::LoadDialog(std::string msg,std::function<void(Ref*)>  fun,bool isNeedCancel,bool isModalDialog)
{
    Dialog->setVisible(true);
    Dialog->getAnimation()->playWithIndex(0); 
    Dialog->getAnimation()
        ->setMovementEventCallFunc(CC_CALLBACK_3(MomoScene::LoadDialogFinish,this));
    m_DialogMsg->setString(msg);
    m_Dialog_ConfirmCallback = fun;
    //是否需要關閉按鈕
    if(isNeedCancel == true)
    {
        m_DialogConfirm->addTouchEventListener(CC_CALLBACK_2(MomoScene::PressDialogConfirmBtn,this));
        m_DialogCancel->addTouchEventListener(CC_CALLBACK_2(MomoScene::PressDialogCancelBtn, this));
        m_DialogConfirm->setVisible(true);
        m_DialogCancel->setVisible(true);
    }
    else
    {
        m_DialogConfirm->addTouchEventListener(CC_CALLBACK_2(MomoScene::PressDialogConfirmBtn,this));
        m_DialogConfirm->setPosition(Point(0,-100));
        m_DialogConfirm->setVisible(true);
        m_DialogCancel->setVisible(false);
    }
    //模態對話框需要做一個吞噬觸摸層
    if(isModalDialog == true)
    {
        auto listener1 = EventListenerTouchOneByOne::create();//創建一個觸摸監聽
        listener1->setSwallowTouches(true);//設置不想向下傳遞觸摸
        listener1->onTouchBegan = [](Touch* touch, Event* event){   
            CCLOG("touch Swallow");  
            return true;   
        };
        _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,m_UI_Dialog);
    }
}
void MomoScene::LoadDialogFinish(Armature *a, MovementEventType b, std::string c)
{
    //動畫完成
    if(b == COMPLETE)
    {
        Dialog->getAnimation()->setMovementEventCallFunc(nullptr);
        m_DialogMsg->setVisible(true);
    }
}
//隱藏對話框
void MomoScene::HideDialog()
{
    m_DialogMsg->setVisible(false);
    m_DialogConfirm->setVisible(false);
    m_DialogCancel->setVisible(false);
    //移除觸摸屏蔽
    _eventDispatcher->removeEventListenersForTarget(m_UI_Dialog);
    Dialog->getAnimation()->playWithIndex(1); 
    Dialog->getAnimation()
        ->setMovementEventCallFunc(CC_CALLBACK_3(MomoScene::HideDialogFinish,this));
}
//隱藏對話框完成
void MomoScene::HideDialogFinish(Armature *a, MovementEventType b, std::string c)
{
    //動畫完成
    if(b == COMPLETE)
    {
        Dialog->getAnimation()->setMovementEventCallFunc(nullptr);
        a->setVisible(false);
        a->setOpacity(255);
    }
}
//對話框取消按鈕
void MomoScene::PressDialogCancelBtn(Ref *pSender, Widget::TouchEventType type)
{
    switch(type)
    {
    case Widget::TouchEventType::ENDED:
        HideDialog();
        break;
    }
}
//對話框確認按鈕
void MomoScene::PressDialogConfirmBtn(Ref *pSender, Widget::TouchEventType type)
{
    switch(type)
    {
    case Widget::TouchEventType::ENDED:
        HideDialog();
        if(m_Dialog_ConfirmCallback != nullptr)
            m_Dialog_ConfirmCallback(NULL);
        break;
    }
}

值得一提的是 所有以m_開頭的變量全部是類的成員變量,下面也給出這些變量的定義,其中有幾個變量在類的構造函數中有初始化,也給出來:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//標誌變量
public:
    bool m_IsNeedExit;
    //UI變量
public:
    Sprite* m_spr_ExitTip; //退出提示
    Armature* Dialog; //對話框
    Label* m_DialogMsg; //對話框消息
    Button* m_DialogConfirm; //對話框確認按鈕
    Button* m_DialogCancel;  //對話框取消按鈕
    //層變量
    Layer* m_UI_Dialog;            //對話框層 最高層
    Layer* m_UI_Tool;          //工具欄層 次高層
    Layer* m_UI_Game;          //遊戲層 低層
    Layer* m_UI_Background;        //背景層 底層
    //回調函數變量
public:
    std::function<void(Ref*)> m_Dialog_ConfirmCallback; //對話框確認回調
    //------------------------------函數----------------------------------
public:
    MomoScene():m_IsNeedExit(false),m_Dialog_ConfirmCallback(nullptr){};
    ~MomoScene(){};

下面是對話框的使用方法:
1
2
3
4
auto fun = [=](Ref* e){
    CCLog("OK You enter the Confirm");
};
LoadDialog("You Enter Continue",fun,true);
上述對話框的實現效果如下:(因為素材不太好,為了更好的展示效果,我在顯示對話框的時候將菜單移除了)
1411876904309646.jpg
1411876918956889.png
一篇已經實現了如何建立一個模態對話框和非模態對話框,但是在手機上有時候【確認】或者【取消】按鈕會非常難點擊,無法關閉對話框,有時候,對話框可能僅僅是一個提示的作用 玩家可能想要只要點擊其他地方就可以關閉掉對話框。下面就對這種對話框進行實現。

首先,這樣對話框的模式就不只是模態對話框和非模態對話框兩種模式了,我們就需要用到強類型的枚舉變量,這種在3.X版本上也是常見的,比如
[cpp] view plain copy
  1. TouchEventType::ENDED  
這樣的變量,下面就做一個這樣的枚舉變量:
[cpp] view plain copy
  1. enum class Dialog_Model  
  2. {  
  3.     Model = 0,      //模態對話框  
  4.     UnModel,        //非模態對話框  
  5.     TouchClear      //點擊其他地方自動清除對話框  
  6. };  

當然對話框的函數也要稍微改變一下:
[cpp] view plain copy
  1. /* 加載對話框 
  2.  * @parm:msg 消息 顯示在界面上的提示消息 
  3.  * @parm:fun 回調函數 給確定按鈕調用的 
  4.  * @parm:isNeedCancel 是否需要取消按鈕 
  5.  * @parm:Model 對話框模式 
  6.  */  
  7. void LoadDialog(std::string msg,std::function<void(Ref*)>  fun,int isNeedCancel = false,Dialog_Model dialogModel = Dialog_Model::Model);  

然後是實現方法:
[cpp] view plain copy
  1. //加載對話框  
  2. void MomoScene::LoadDialog(std::string msg,std::function<void(Ref*)>  fun,int isNeedCancel,Dialog_Model dialogModel)  
  3. {  
  4.     Dialog->setVisible(true);  
  5.     Dialog->getAnimation()->playWithIndex(0);   
  6.     Dialog->getAnimation()  
  7.         ->setMovementEventCallFunc(CC_CALLBACK_3(MomoScene::LoadDialogFinish,this));  
  8.     m_DialogMsg->setString(msg);  
  9.     m_Dialog_ConfirmCallback = fun;  
  10.     //是否需要關閉按鈕  
  11.     if(isNeedCancel == true)  
  12.     {  
  13.         m_DialogConfirm->addTouchEventListener(CC_CALLBACK_2(MomoScene::PressDialogConfirmBtn,this));  
  14.         m_DialogCancel->addTouchEventListener(CC_CALLBACK_2(MomoScene::PressDialogCancelBtn, this));  
  15.         m_DialogConfirm->setVisible(true);  
  16.         m_DialogCancel->setVisible(true);  
  17.     }  
  18.     else  
  19.     {  
  20.         m_DialogConfirm->addTouchEventListener(CC_CALLBACK_2(MomoScene::PressDialogConfirmBtn,this));  
  21.         m_DialogConfirm->setPosition(Point(0,-100));  
  22.         m_DialogConfirm->setVisible(true);  
  23.         m_DialogCancel->setVisible(false);  
  24.     }  
  25.     auto listener1 = EventListenerTouchOneByOne::create();//創建一個觸摸監聽  
  26.         listener1->setSwallowTouches(true);//設置不想向下傳遞觸摸  
  27.         listener1->onTouchBegan = [](Touch* touch, Event* event){     
  28.             CCLOG("touch Swallow");    
  29.             return true;     
  30.         };  
  31.     //模態對話框需要做一個吞噬觸摸層  
  32.     if(dialogModel == Dialog_Model::Model)  
  33.     {  
  34.         _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,m_UI_Dialog);  
  35.     }  
  36.     else if(dialogModel == Dialog_Model::TouchClear)  
  37.     {  
  38.         listener1->onTouchEnded = [&](Touch* touch, Event* event){     
  39.             //只有點擊對話框外面才關閉對話框  
  40.             Rect rect = Dialog->getBoundingBox();  
  41.             if(!rect.containsPoint(touch->getLocation()))  
  42.               HideDialog();  
  43.         };  
  44.         _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,m_UI_Dialog);  
  45.     }  
  46. }  


實現方法看起來是越來越臃腫了,不過只需要看後面幾行就可以了,首先獲取到對話框的碰撞區域,用getBoundingBox就可以得到它的碰撞區域,如果在碰撞區域內,即觸摸結束點在盒子內,就不隱藏對話框。否則就隱藏。

還有,上次做的對話框有一個bug忘記了,就是在對話框關閉完成的時候忘記關閉觸摸監聽了,用下面的方法關閉即可
[cpp] view plain copy
  1. _eventDispatcher->removeEventListenersForTarget(m_UI_Dialog);  

因為m_UI_Dialog中只有一個監聽,就是屏蔽層,這樣去掉的層就是屏蔽層了。

附上demo供大家研究:

沒有留言:

張貼留言

cocos2dx-lua 建立滑鼠監聽

重要關鍵字  EVENT_MOUSE_SCROLL addEventListenerWithSceneGraphPriority      if IsPc() then --建立滑鼠監聽         local listener = cc.EventListenerMouse...