Cocos2D-X弹出对话框的实现与封装


在用Cocos2DX引擎开发游戏的过程中,我们经常需要弹出一个对话框或者提示框,通知玩家一些必要的信息。这时候我们就需要考虑怎样设计和封装一个这样的弹出对话框。首先,这样的弹出框一般都是“模态窗口”,即在没有对当前弹出的对话框进行确认的时候,不能继续往下操作。


  一个对话框一般包含几个部分:背景图、两个按钮(个数可定制)、标题、文字内容。我们需要使对话框为模态窗口,并设置弹出效果。下面来说说创建弹出对话框的要点:

  1、弹出层的触摸优先级,操作与显示相一致

  因为弹出对话框是“模态窗口”,所以我们需要设置触摸优先级来保证对话框是模态的。

  在这里我们以CCMenu作为对话框按钮的操作实现,我们知道CCMenu 的默认触摸级别是-128,那么我们的【弹出层】触摸级别该设置为多少?

  在我们设定触摸级别时,请记住一句话,操作与显示相一致。此话何意?现在设想这样一种情况,我们为了屏蔽弹出层以外所有的层的操作,而将弹出层级别置为 -129 ,这样便保证了它是模态的了(一般而言,除了 CCMenu 的级别,用户自定义也无需这么大),但是如果当前弹出层上方还有其它的层(也可以是弹出层的父节点上方有其它层,如多层的 UI 设计),并且其上也有 CCMenu 按钮,那么就出现了显示在 弹出层上放层中的 CCMenu 按钮不能点击,这不科学!实际,在弹出层的 CCMenu 也不能点击,只是我们可以通过将弹出层的触摸消息传递给层中的 CCMenu 解决。所以设置为一味的追求最大的触摸优先级,并不可取,它不能很好的做到 操作与显示相一致,而优先级小于 CCMenu 并不能屏蔽下方的其它 CCMenu 操作,所以对于弹出层本身,它设置为 -128 是合理的,对于同样级别的这些元素来说(弹出层和CCMenu),能够做到,显示在最上方的优先处理。如果在弹出层上方还有层,是可以点击的,为什么不呢!而我们要做的是,通过逻辑控制,让弹出层节点,最后添加到场景的最上方。

  所以,在这里层本身的触摸优先级别设置为 -128 ,而弹出层中的按钮是 CCMenu,都是同级,操作与显示相一致,唯一要注意的是逻辑的控制,什么时候弹出层,由哪个节点弹出层(一般由场景基层来负责),以保证它显示在最上方。

  2、定制按钮个数

  我们可以定制按钮的个数。在封装的时候添加addButton方法,用来在当前对话框中动态地添加一个或几个按钮,而添加几个?当然由你来决定。但确定的是,它们的位置会随着按钮个数的不同而不同,如果一个按钮,将居中显示(一分为二),如果两个(三等份距离),如果三个(四等份距离),以此类推。这里addButton()的主要作用是创建一个menuItem添加到Menu中。然后在onEnter中(此时按钮个数已经确定)计算并设置各按钮的位置,并添加至界面之中。

  3、窗口的大小可变


  弹出对话框的窗口大小要是可变的,通过 getContentSize 来获取。

  •   如果没有设置 ContentSize ,那么采取的方案是,窗口大小与传入图片一样大。
  •   如果设置了ContentSize,则将窗口设定为指定大小。这时候需要将背景图片进行缩放,如何缩放? 【答案】是利用9宫格图CCScale9Sprite缩放图片,缩放带圆角的图片。原理如下:

  九宫格图是通过1个CCSpriteBatchNode和9个CCSprite来实现的,原理很简单,通过将原纹理资源切割成9部分(PS: 这也是叫9宫格的原因),保持4个角不变形,根据想要的尺寸来进行拉伸或压缩。

 

   cocos2d -X弹出对话框的实现与封装

  使用方法:

[AppleScript]  纯文本查看 复制代码
?
1
2
3
4
5
6
7
      CCScale 9 Sprite * background = CCScale 9 Sprite : : create ( back .png” ) ;
 
background - > setContentSize ( CCSizeMake ( 400 , 200 ) ) ; / / 400 x 200 是要生成的尺寸
 
background - > setPosition ( Center ) ;
 
this - > addChild ( background ) ;


  4、回调函数的实现方案

  对于回调一般有两种方式,一种是 delegate 回调,这需要定义接口,由上层,继承实现接口,并把自己当作参数,传入弹出层,由弹出层调用 delegate 的接口方法实现,在 Cocos2d-x 里面很多地方用到此方式。另一种则是函数绑定,就像 CCMenu 那样的绑定函数。

  在这里设计的是按钮个数可变,功能也不尽相同,所以我们选择绑定函数!进一步封装,将弹出层的按钮回调函数设置为内部实现,然后在 回调给它的上层,之后关闭对话框(关闭的操作由对话框层来完成)。回调给它的上层的时候传递CCNode参数,以其 tag 标示,点击的是哪个按钮。【用void setCallbackFunc(CCObject* target, SEL_CallFuncN callfun); 作为外部接口,设置上层对象和上层的回调函数。】

  5、onEnter动态创建弹出层

  根据设置的属性去创建层 有两种方式:

  其一,实时设置,实时刷新。比如在 static PopupLayer* create(const char* gackgroundImage) 的实现里面,创建一个精灵,并设置好图片,添加到当前层,如果调用了 setContentSize 我们再在此方法获取精灵后去修改这个精灵的大小

  其二,保留属性,动态组建。也就是说前面一些封装的函数比如setTitle()、setContentText(),addButton()、setCallbackFunc()只用于设置属性参数(即给变量赋值)。参数设置好以后,在一个适当的执行时期,根据以上参数,动态创建符合需求的精灵/层,而这个操作在 onEnter 里尤为合适。

  在这里我们使用动态组建的方式,即在前面用一些变量来保存对话框所需的信息,然后在 onEnter 中,读取这些信息,以实时添加至界面之中。

  封装

[AppleScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
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
/ / PopupLayer.h
 
   #include “cocos2d.h”
 
   #include “cocos-ext.h”
 
  USING_NS_CC;
 
  USING_NS_CC_EXT;
 
   class PopupLayer : public CCLayer {
 
  public :
 
  PopupLayer ( ) ;
 
  ~PopupLayer ( ) ;
 
  virtual bool init ( ) ;
 
   / / 需要重写触摸注册函数,重新给定触摸级别
 
  virtual void registerWithTouchDispatcher ( ) ;
 
   / / 重写触摸函数,返回 true ,屏蔽其它层,达到“模态”效果
 
  bool ccTouchBegan ( CCTouch * pTouch , CCEvent * pEvent ) ;
 
   / / 静态创建函数,创建一个弹出层,设置背景图片
 
  static PopupLayer * create ( const char * backgroundImage ) ;
 
   / / 设置标题
 
  void setTitle ( const char * title , int fontsize = 20 ) ;
 
   / / 设置文本内容,padding 为文字到对话框两边预留的距离,这是可控的,距上方的距离亦是如此
 
  void setContentText ( const char * text , int fontsize = 20 , int padding = 50 , int paddingTop = 100 ) ;
 
   / / 设置上层对象和上层回调函数,用于回调时传递CCNode参数
 
  void setCallBackFunc ( CCObject * target , SEL_CallFuncN callfun ) ;
 
   / / 添加menuItem按钮,封装了一个函数,传入些必要的参数
 
  bool addButton ( const char * normalImage , const char * selectedImage , const char * title , int tag = 0 ) ;
 
   / / 为了在显示层时的属性生效,选择在onEnter里动态生成
 
  virtual void onEnter ( ) ;
 
  virtual void onExit ( ) ;
 
  CREATE_FUNC ( PopupLayer ) ;
 
  private :
 
  void buttonCallBack ( CCObject * pSender ) ;
 
   / / 文字内容两边的空白区域
 
  int m_contentPadding;
 
  int m_contentPaddingTop;
 
  CCObject * m_callbackListener;
 
  SEL_CallFuncN m_callback;
 
   / / 定义了CCMenu * 类型变量m_pMenu , 并且直接定义默认的 set / get 方法
 
  CC_SYNTHESIZE_RETAIN ( CCMenu * , m_pMenu , MenuButton ) ;
 
  CC_SYNTHESIZE_RETAIN ( CCSprite * , m_sfBackGround , SpriteBackGround ) ;
 
  CC_SYNTHESIZE_RETAIN ( CCScale 9 Sprite * , m_s 9 BackGround , Sprite 9 BackGround ) ;
 
  CC_SYNTHESIZE_RETAIN ( CCLabelTTF * , m_ltTitle , LabelTitle ) ;
 
  CC_SYNTHESIZE_RETAIN ( CCLabelTTF * , m_ltContentText , LabelContentText ) ;
 
   } ;


  //PopupLayer.cpp

[AppleScript]  纯文本查看 复制代码
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#include “PopupLayer.h”
 
  USING_NS_CC;
 
   / / 构造函数中变量设初值
 
  PopupLayer : : PopupLayer ( )
 
   {
 
  m_contentPadding = 0 ;
 
  m_contentPaddingTop = 0 ;
 
  m_pMenu = NULL;
 
  m_callbackListener = NULL;
 
  m_callback = NULL;
 
  m_sfBackGround = NULL;
 
  m_s 9 BackGround = NULL;
 
  m_ltContentText = NULL;
 
  m_ltTitle = NULL;
 
   }
 
   / / 释放
 
  PopupLayer : : ~PopupLayer ( )
 
   {
 
  CC_SAFE_RELEASE ( m_pMenu ) ;
 
  CC_SAFE_RELEASE ( m_sfBackGround ) ;
 
  CC_SAFE_RELEASE ( m_s 9 BackGround ) ;
 
  CC_SAFE_RELEASE ( m_ltContentText ) ;
 
  CC_SAFE_RELEASE ( m_ltTitle ) ;
 
   }
 
   / / 初始化
 
  bool PopupLayer : : init ( )
 
   {
 
   if ( !CCLayer : : init ( ) ) {
 
   return false ;
 
   }
 
  this - > setContentSize ( CCSizeZero ) ;
 
   / / 初始化需要的Menu
 
  CCMenu * menu = CCMenu : : create ( ) ;
 
   menu - > setPosition ( CCPointZero ) ;
 
  setMenuButton ( menu ) ; / / set ( ) 方法
 
  setTouchEnabled ( true ) ; / / 开启触摸响应
 
   return true ;
 
   }
 
   / / 重写触摸注册函数,重新给定触摸级别
 
  void PopupLayer : : registerWithTouchDispatcher ( ) {
 
   / / 这里的触摸优先级设置为 -128 ,与CCMenu同级,保证了屏蔽下方的触摸
 
  CCDirector : : sharedDirector ( ) - > getTouchDispatcher ( ) - > addTargetedDelegate ( this , -128 , true ) ;
 
   }
 
   / / 触摸函数ccTouchBegan,返回 true
 
  bool PopupLayer : : ccTouchBegan ( CCTouch * pTouch , CCEvent * pEvent ) {
 
   return true ;
 
   }
 
   / / 创建一个弹出层,给背景精灵变量赋值
 
  PopupLayer * PopupLayer : : create ( const char * backgroundImage ) {
 
  PopupLayer * popup = PopupLayer : : create ( ) ;
 
  popup - > setSpriteBackGround ( CCSprite : : create ( backgroundImage ) ) ;
 
  popup - > setSprite 9 BackGround ( CCScale 9 Sprite : : create ( backgroundImage ) ) ;
 
   return popup;
 
   }
 
   / / 给标题变量赋值
 
  void PopupLayer : : setTitle ( const char * title , int fontsize ) {
 
  CCLabelTTF * ltfTitle = CCLabelTTF : : create ( title , “Arial” , fontsize ) ;
 
  ltfTitle - > setColor ( ccc 3 ( 0 , 0 , 0 ) ) ;
 
  setLabelTitle ( ltfTitle ) ;
 
   }
 
   / / 给文本变量赋值
 
  void PopupLayer : : setContentText ( const char * text , int fontsize , int padding , int paddingTop ) {
 
  CCLabelTTF * content = CCLabelTTF : : create ( text , “Arial” , fontsize ) ;
 
   content - > setColor ( ccc 3 ( 0 , 0 , 0 ) ) ;
 
  setLabelContentText ( content ) ;
 
  m_contentPadding = padding;
 
  m_contentPaddingTop = paddingTop;
 
   }
 
   / / 给下层层变量和回调函数变量赋值
 
  void PopupLayer : : setCallBackFunc ( CCObject * target , SEL_CallFuncN callfun ) {
 
  m_callbackListener = target ;
 
  m_callback = callfun;
 
   }
 
   / / menu 菜单变量添加Item
 
  bool PopupLayer : : addButton ( const char * normalImage , const char * selectedImage , const char * title , int tag ) {
 
  CCSize winSize = CCDirector : : sharedDirector ( ) - > getWinSize ( ) ;
 
  CCPoint center = ccp ( winSize.width / 2 , winSize.height / 2 ) ;
 
   / / 创建图片菜单按钮
 
  CCMenuItemImage * menuImage = CCMenuItemImage : : create (
 
  normalImage , selectedImage , this , menu_selector ( PopupLayer : : buttonCallBack ) ) ;
 
  menuImage - > setTag ( tag ) ;
 
  menuImage - > setPosition ( center ) ;
 
   / / 添加文字说明并设置位置
 
  CCSize menuSize = menuImage - > getContentSize ( ) ;
 
  CCLabelTTF * ttf = CCLabelTTF : : create ( title , “Arial” , 15 ) ;
 
  ttf - > setColor ( ccc 3 ( 0 , 0 , 0 ) ) ;
 
  ttf - > setPosition ( ccp ( menuSize.width / 2 , menuSize.height / 2 ) ) ;
 
  menuImage - > addChild ( ttf ) ;
 
  getMenuButton ( ) - > addChild ( menuImage ) ;
 
   return true ;
 
   }
 
   / / 销毁弹出框,传递参数node给下层
 
  void PopupLayer : : buttonCallBack ( CCObject * pSender ) {
 
  CCNode * node = dynamic_cast ( pSender ) ;
 
  CCLog ( “touch tag : %d” , node - > getTag ( ) ) ;
 
   if ( m_callback & & m_callbackListener )
 
   {
 
   / / 执行HelloWorld层的回调函数,传递node参数
 
   ( m_callbackListener - > * m_callback ) ( node ) ;
 
   }
 
  this - > removeFromParentAndCleanup ( true ) ;
 
   }
 
   / / 全部参数都设定好后,在运行时动态加载
 
  void PopupLayer : : onEnter ( ) {
 
  CCLayer : : onEnter ( ) ;
 
  CCSize winSize = CCDirector : : sharedDirector ( ) - > getWinSize ( ) ;
 
  CCPoint center = ccp ( winSize.width / 2 , winSize.height / 2 ) ;
 
  CCSize contentSize;
 
   / / 设定好参数,在运行时加载
 
   if ( getContentSize ( ) . equals ( CCSizeZero ) ) {
 
  getSpriteBackGround ( ) - > setPosition ( center ) ;
 
  this - > addChild ( getSpriteBackGround ( ) , 0 , 0 ) ;
 
  contentSize = getSpriteBackGround ( ) - > getTexture ( ) - > getContentSize ( ) ;
 
   }
 
   else {
 
  CCScale 9 Sprite * background = getSprite 9 BackGround ( ) ;
 
  background - > setContentSize ( getContentSize ( ) ) ;
 
  background - > setPosition ( center ) ;
 
  this - > addChild ( background , 0 ) ;
 
  contentSize = getContentSize ( ) ;
 
   }
 
   / / 添加按钮,并根据Item的个数设置其位置
 
  this - > addChild ( getMenuButton ( ) ) ;
 
  float btnWidth = contentSize.width / ( getMenuButton ( ) - > getChildrenCount ( ) + 1 ) ;
 
  CCArray * array = getMenuButton ( ) - > getChildren ( ) ;
 
  CCObject * pObj = NULL;
 
  int i = 0 ;
 
  CCARRAY_FOREACH ( array , pObj ) {
 
  CCNode * node = dynamic_cast ( pObj ) ;
 
  node - > setPosition ( ccp ( winSize.width / 2 - contentSize.width / 2 + btnWidth * ( i + 1 ) ,
 
  winSize.height / 2 - contentSize.height / 3 ) ) ;
 
  i + + ;
 
   }
 
   / / 显示对话框标题
 
   if ( getLabelTitle ( ) ) {
 
  getLabelTitle ( ) - > setPosition ( ccpAdd ( center , ccp ( 0 , contentSize.height / 2 - 25.0 f ) ) ) ;
 
  this - > addChild ( getLabelTitle ( ) ) ;
 
   }
 
   / / 显示文本内容
 
   if ( getLabelContentText ( ) ) {
 
  CCLabelTTF * ltf = getLabelContentText ( ) ;
 
  ltf - > setPosition ( center ) ;
 
  ltf - > setDimensions ( CCSizeMake ( contentSize.width - m_contentPadding * 2 , contentSize.height - m_contentPaddingTop ) ) ;
 
  ltf - > setHorizontalAlignment ( kCCTextAlignmentLeft ) ;
 
  this - > addChild ( ltf ) ;
 
   }
 
   / / 弹出效果
 
  CCSequence * popupActions = CCSequence : : create (
 
  CCScaleTo : : create ( 0.0 , 0.0 ) ,
 
  CCScaleTo : : create ( 0.0 6 , 1.0 5 ) ,
 
  CCScaleTo : : create ( 0.0 8 , 0.9 5 ) ,
 
  CCScaleTo : : create ( 0.0 8 , 1.0 ) , NULL ) ;
 
  this - > runAction ( popupActions ) ;
 
   }
 
   / / 退出
 
  void PopupLayer : : onExit ( ) {
 
  CCLayer : : onExit ( ) ;
 
   }
 
  弹出层调用:
 
   / / 定义一个弹出层,传入一张背景图片
 
  PopupLayer * popup = PopupLayer : : create ( “popupBackGround.png” ) ;
 
   / / ContentSize是可选的设置,可以不设置,如果设置则把它当做 9 图缩放
 
   / / popup - > setContentSize ( CCSizeMake ( 400 , 360 ) ) ;
 
  popup - > setTitle ( “Message” ) ;
 
  popup - > setContentText ( “Most people ... blunder round this city .” , 20 , 50 , 150 ) ;
 
   / / 设置回调函数,回调传回一个CCNode以获取 tag 判断点击的按钮
 
   / / 这只是作为一种封装实现,如果使用delegate那就能够更灵活的控制参数了
 
  popup - > setCallBackFunc ( this , callfuncN_selector ( HelloWorld : : buttonCallBack ) ) ;
 
   / / 添加按钮,设置图片、文字, tag 信息
 
  popup - > addButton ( button .png” , button .png” , “Ok” , 0 ) ;
 
  popup - > addButton ( button .png” , button .png” , “Cancel” , 1 ) ;
 
  this - > addChild ( popup ) ;


  测试截图:

 

  Cocos2D-X弹出对话框的实现与封装

  这样,对话框的基本模型就完成了,它实现了以下功能:

  •   一个可以弹出的对话框实现
  •   模态窗口的实现(需要逻辑的控制)
  •   多按钮的支持,位置自适应,提供回调函数
  •   提供标题和内容设置
  •   支持 九图,控制适应弹出框大小
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值