充分利用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 ); |
上述對話框的實現效果如下:(因為素材不太好,為了更好的展示效果,我在顯示對話框的時候將菜單移除了)
一篇已經實現了如何建立一個模態對話框和非模態對話框,但是在手機上有時候【確認】或者【取消】按鈕會非常難點擊,無法關閉對話框,有時候,對話框可能僅僅是一個提示的作用 玩家可能想要只要點擊其他地方就可以關閉掉對話框。下面就對這種對話框進行實現。
首先,這樣對話框的模式就不只是模態對話框和非模態對話框兩種模式了,我們就需要用到強類型的枚舉變量,這種在3.X版本上也是常見的,比如
- TouchEventType::ENDED
- enum class Dialog_Model
- {
- Model = 0, //模態對話框
- UnModel, //非模態對話框
- TouchClear //點擊其他地方自動清除對話框
- };
當然對話框的函數也要稍微改變一下:
- /* 加載對話框
- * @parm:msg 消息 顯示在界面上的提示消息
- * @parm:fun 回調函數 給確定按鈕調用的
- * @parm:isNeedCancel 是否需要取消按鈕
- * @parm:Model 對話框模式
- */
- void LoadDialog(std::string msg,std::function<void(Ref*)> fun,int isNeedCancel = false,Dialog_Model dialogModel = Dialog_Model::Model);
然後是實現方法:
- //加載對話框
- void MomoScene::LoadDialog(std::string msg,std::function<void(Ref*)> fun,int isNeedCancel,Dialog_Model dialogModel)
- {
- 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);
- }
- auto listener1 = EventListenerTouchOneByOne::create();//創建一個觸摸監聽
- listener1->setSwallowTouches(true);//設置不想向下傳遞觸摸
- listener1->onTouchBegan = [](Touch* touch, Event* event){
- CCLOG("touch Swallow");
- return true;
- };
- //模態對話框需要做一個吞噬觸摸層
- if(dialogModel == Dialog_Model::Model)
- {
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,m_UI_Dialog);
- }
- else if(dialogModel == Dialog_Model::TouchClear)
- {
- listener1->onTouchEnded = [&](Touch* touch, Event* event){
- //只有點擊對話框外面才關閉對話框
- Rect rect = Dialog->getBoundingBox();
- if(!rect.containsPoint(touch->getLocation()))
- HideDialog();
- };
- _eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,m_UI_Dialog);
- }
- }
實現方法看起來是越來越臃腫了,不過只需要看後面幾行就可以了,首先獲取到對話框的碰撞區域,用getBoundingBox就可以得到它的碰撞區域,如果在碰撞區域內,即觸摸結束點在盒子內,就不隱藏對話框。否則就隱藏。
還有,上次做的對話框有一個bug忘記了,就是在對話框關閉完成的時候忘記關閉觸摸監聽了,用下面的方法關閉即可
- _eventDispatcher->removeEventListenersForTarget(m_UI_Dialog);
因為m_UI_Dialog中只有一個監聽,就是屏蔽層,這樣去掉的層就是屏蔽層了。
附上demo供大家研究:
沒有留言:
張貼留言