sencha touch 自定义cardpanel控件 模仿改进NavigationView 灵活添加按钮组,导航栏,自由隐藏返回按钮(废弃 仅参考)
最新版本我将会放在:http://www.cnblogs.com/mlzs/p/3382229.html这里的示例里面,这里不会再做更新
代码:
1 /* 2 *模仿且改进NavigationView 3 *返回时自动销毁视图,节约内存 4 */ 5 Ext.define('ux.CardPanel', { 6 extend: 'Ext.Container', 7 xtype: 'cardPanel', 8 requires: ['Ext.TitleBar'], 9 config: { 10 //顶部导航条 11 navBar: { 12 docked: 'top' 13 }, 14 //布局,type不可更改 15 layout: { 16 type: 'card', 17 animation: { 18 duration: 300, 19 easing: 'ease-out', 20 type: 'slide', 21 direction: 'left' 22 } 23 }, 24 //顶部控件组,在子项中配置 25 navBarItems: null, 26 //返回按钮 27 backBtn: { 28 align: 'left', 29 ui: 'back' 30 }, 31 //返回按钮显示状态,在子项中配置 32 backHide: false, 33 //临时cls,在子项中配置 34 pCls: null, 35 //底部导航栏、可选格式btmBar:'xtype',在子项中配置 36 btmBar: null, 37 //当子项被激活后移除除了指定项之外的项,在子项中配置 38 nRemove: null 39 }, 40 //初始化 41 initialize: function () { 42 var layout = this.getLayout(); 43 if (layout && !layout.isCard) { 44 Ext.Logger.error('CardPanel中layout的布局只能是card布局'); 45 } 46 this.initStatus(); 47 }, 48 //初始化历史记录 49 initStatus: function () { 50 if (!this.viewStack) { 51 //历史记录 52 this.viewStack = []; 53 //视图加载状态 54 this.viewStatus = {}; 55 } 56 }, 57 //更新返回按钮显示状态 58 updateBackHide: function (newItem, oldItem) { 59 var backBtn = this.getBackBtn(); 60 if (newItem) { 61 backBtn.hide(); 62 } else { 63 backBtn.show(); 64 } 65 }, 66 //更新底部导航栏 67 updateBtmBar: function (newItem, oldItem) { 68 if (oldItem) { 69 oldItem = this.down(oldItem); 70 this.remove(oldItem); 71 } 72 if (newItem) { 73 newItem = Ext.create(newItem); 74 this.add(newItem); 75 } 76 }, 77 //创建导航栏控件组 78 applyNavBarItems: function (newItems) { 79 if (!newItems) return false; 80 var me = this, 81 navItems = [], 82 i, 83 ln; 84 newItems = Ext.Array.from(newItems); 85 for (i = 0, ln = newItems.length; i < ln; i++) { 86 navItems.push(me.factoryItem(newItems[i])); 87 } 88 return navItems; 89 }, 90 //更新导航栏控件组 91 updateNavBarItems: function (newItem, oldItem) { 92 var navBar = this.getNavBar(); 93 if (oldItem) { 94 var i, ln; 95 for (i = 0, ln = oldItem.length; i < ln; i++) { 96 navBar.remove(oldItem[i]); 97 } 98 } 99 if (newItem) { 100 navBar.add(newItem); 101 } 102 }, 103 //创建顶部导航栏 104 applyNavBar: function (config) { 105 return Ext.factory(config, Ext.TitleBar, this.getNavBar()); 106 }, 107 //更新顶部导航栏 108 updateNavBar: function (newItem, oldItem) { 109 if (oldItem) { 110 this.remove(oldItem); 111 } 112 if (newItem) { 113 this.add(newItem); 114 } 115 }, 116 //更新临时cls 117 updatePCls: function (newItem, oldItem) { 118 if (oldItem) { 119 this.removeCls(oldItem); 120 } 121 if (newItem) { 122 this.addCls(newItem); 123 } 124 }, 125 //创建返回按钮 126 applyBackBtn: function (config) { 127 return Ext.factory(config, Ext.Button, this.getBackBtn()); 128 }, 129 //更新返回按钮 130 updateBackBtn: function (newItem, oldItem) { 131 if (oldItem) { 132 this.getNavBar().remove(oldItem); 133 } 134 if (newItem) { 135 this.getNavBar().add(newItem); 136 newItem.on({ 137 scope: this, 138 tap: this.onBackButtonTap 139 }); 140 } 141 }, 142 //更新移除 143 updateNRemove: function (newItem, oldItem) { 144 var animation = this.getLayout().getAnimation(); 145 if (oldItem) { 146 //移除动画结束监听 147 animation.un({ 148 scope: this, 149 animationend: 'onAnimationend' 150 }); 151 } 152 if (newItem) { 153 //添加动画结束监听 154 animation.on({ 155 scope: this, 156 animationend: 'onAnimationend' 157 }); 158 } 159 }, 160 /** 161 * 移除历史记录 162 * @private 163 */ 164 viewStackPop: function (count) { 165 for (var i = 0; i < count; i++) { 166 this.viewStack.pop(); 167 } 168 }, 169 //添加视图 170 //注意xtype是指alternateClassName 171 push: function (xtype, params) { 172 var me = this, 173 view = this.getActiveItem(), 174 oldXtype = view && (view.config.xtype || view.getItemId()); 175 /*过滤已经添加的视图*/ 176 if (!this.viewStatus[xtype]) { 177 params = params || {}; 178 params.itemId = xtype; 179 view = Ext.create(xtype, params); 180 this.add(view); 181 } else if (oldXtype != xtype) { 182 this.viewStack.push(xtype); 183 this.setActiveItem(xtype); 184 } 185 }, 186 //当动画效果结束时,子项配置了nRemove属性将会激活 187 onAnimationend: function () { 188 // console.log('animationend'); 189 this.popAll(this.getNRemove()); 190 }, 191 //项被激活 192 onActivate: function (view) { 193 var config = view.config; 194 // console.log('activate', config.xtype || view.getItemId()); 195 //更新后退按钮 196 // console.log('setBackHide', this.viewStack); 197 this.setBackHide(config.backHide || this.viewStack.length == 1); 198 //更新按钮组 199 var items = config.navBarItems || false; 200 this.setNavBarItems(items); 201 //更新cls 202 var pCls = config.pCls || false; 203 this.setPCls(pCls); 204 //更新标题 205 var title = config.title || ''; 206 this.getNavBar().setTitle(title); 207 //更新底部导航栏 208 var btmBar = config.btmBar || false; 209 this.setBtmBar(btmBar); 210 //更新移除 211 var nRemove = config.nRemove || false; 212 this.setNRemove(nRemove); 213 }, 214 //项被销毁 215 onDestroy: function (view) { 216 // console.log('onDestroy', view.config.xtype || view.getItemId()); 217 this.viewStatus[view.config.xtype || view.getItemId()] = false; 218 }, 219 /** 220 * 不填写参数时,移除当前项,返回到上一项 221 * 如果参数是数字,则从最后一项开始移除指定数目的项 222 * 如果参数是string,则移除指定类型的项 223 * 如果参数是项,则移除传入的项 224 * 不论参数如何,都会保留一个活动项 225 * @return {Ext.Component} 当前活动项 226 */ 227 pop: function (count) { 228 if (this.beforePop(count)) { 229 return this.doPop(); 230 } 231 }, 232 /** 233 * @private 234 *删除指定项 235 */ 236 beforePop: function (count) { 237 var me = this, 238 innerItems = me.getInnerItems(); 239 if (Ext.isString(count) || Ext.isObject(count)) { 240 var last = innerItems.length - 1, 241 i; 242 for (i = last; i >= 0; i--) { 243 if ((Ext.isString(count) && Ext.ComponentQuery.is(innerItems[i], count)) || (Ext.isObject(count) && count == innerItems[i])) { 244 //获得移除项序号 245 count = last - i; 246 break; 247 } 248 } 249 if (!Ext.isNumber(count)) { 250 return false; 251 } 252 } 253 var ln = innerItems.length, 254 toRemove; 255 //默认移除一项 256 if (!Ext.isNumber(count) || count < 1) { 257 count = 1; 258 } 259 //当我们试图移除更多视图时 260 count = Math.min(count, ln - 1); 261 if (count) { 262 this.viewStackPop(count); 263 //开始移除视图 264 toRemove = innerItems.splice(-count, count - 1); 265 for (i = 0; i < toRemove.length; i++) { 266 this.remove(toRemove[i]); 267 } 268 return true; 269 } 270 return false; 271 }, 272 /** 273 * @private 274 *移除最后一项 275 */ 276 doPop: function () { 277 var me = this, 278 innerItems = this.getInnerItems(), 279 ord = innerItems[innerItems.length - 1]; 280 me.remove(ord); 281 //触发被移除项的事件 282 return this.getActiveItem(); 283 }, 284 doResetActiveItem: function (innerIndex) { 285 var me = this, 286 innerItems = me.getInnerItems(), 287 animation = me.getLayout().getAnimation(); 288 if (innerIndex > 0) { 289 if (animation && animation.isAnimation) { 290 animation.setReverse(true); 291 } 292 me.setActiveItem(innerIndex - 1); 293 } 294 }, 295 /** 296 * @private 297 *执行移除项,调用remove方法后自动执行 298 */ 299 doRemove: function () { 300 var animation = this.getLayout().getAnimation(); 301 if (animation && animation.isAnimation) { 302 animation.setReverse(false); 303 } 304 this.callParent(arguments); 305 }, 306 /** 307 * @private 308 *执行添加项,调用add方法后自动执行 309 */ 310 onItemAdd: function (item, index) { 311 if (item.isInnerItem()) { 312 var xtype = item.config.xtype || item.getItemId(); 313 //如果配置了items,会先于initialize执行,所以需要初始化历史纪录 314 this.initStatus(); 315 this.viewStatus[xtype] = true; 316 this.viewStack.push(xtype); 317 //添加监听 318 item.on({ 319 scope: this, 320 activate: 'onActivate', 321 destroy: 'onDestroy' 322 }); 323 } 324 this.doItemLayoutAdd(item, index); 325 if (!this.isItemsInitializing && item.isInnerItem()) { 326 this.setActiveItem(item); 327 } 328 if (this.initialized) { 329 this.fireEvent('add', this, item, index); 330 } 331 }, 332 /** 333 * 移除第一项和最后项之间的所有项(包括最后项) 334 * @return {Ext.Component} 当前活动视图 335 */ 336 reset: function (noUp) { 337 return this.pop(this.getInnerItems().length); 338 }, 339 //除了指定项,从倒数第二项开始移除 340 popAll: function (xtype) { 341 var me = this, 342 innerItems = this.getInnerItems(), 343 length = innerItems.length - 1, 344 oldXtype, 345 ord, 346 i; 347 this.viewStack = [xtype]; 348 for (i = length; i > -1; i--) { 349 /*过滤掉需要显示的视图*/ 350 ord = innerItems[i]; 351 oldXtype = ord.config.xtype || ord.getItemId(); 352 if (i == length) { 353 me.viewStack.push(oldXtype); 354 } else if (oldXtype != xtype) { 355 me.remove(ord, true); 356 } 357 } 358 }, 359 //返回上一个历史记录 360 onBackButtonTap: function () { 361 this.pop(); 362 this.fireEvent('back', this); 363 } 364 });
主视图js:
1 /* 2 *主视图,负责视图切换 3 */ 4 Ext.define('app.view.Main', { 5 extend: 'ux.CardPanel', 6 requires: ['app.view.Home', 'Ext.picker.Picker', 'app.view.uitl.MyBar', 'app.view.user.Site'], 7 xtype: 'main', 8 config: { 9 id: 'main', 10 cls: 'cardPanel', 11 backBtn: { 12 iconCls: 'reply', 13 iconMask: true, 14 cls: 'replyBtn' 15 }, 16 //items只能配置一项 17 items: [{ 18 xtype: 'home' 19 }] 20 } 21 });
子视图js:
1 Ext.define('app.view.message.List', { 2 alternateClassName: 'messageList', 3 extend: 'Ext.List', 4 xtype: 'messageList', 5 requires: ['Ext.plugin.ListPaging'], 6 config: { 7 cls: 'list', 8 //标题 9 title: '校园资讯', 10 //用于控制navBarItems中的css 11 pCls: '', 12 //底部导航栏,直接填写alternateClassName 13 btmBar: 'myBar', 16 //额外的按钮组,只能是按钮组。不能添加其他属性 17 navBarItems: [{ 18 itemId: 'mBtn', 19 xtype: 'button', 20 align: 'right', 21 iconMask: true, 22 cls: 'nBg', 23 iconCls: 'search', 24 action: 'show', 25 show: 'messageSearch' 26 }], 27 plugins: [{ 28 xclass: 'Ext.plugin.ListPaging' 29 }], 30 itemTpl: '<div class="title">{Title}</div><div class="sm">时间 {Time} 发布来源:{Auth}</div><div class="like"><div class="ico comment">0</div></div>', 31 store: 'messageList' 32 } 33 });
2013.9.6
更改CardPanel
优化navBarItems结构,itemId不再必须设置
优化for循环结果
修复popAll方法中ord为全局变量的问题
2013.9.6
更改push方法,可以在传入配置参数(只在第一次创建视图时有效)
2013.10.7
优化代码细节
2013.10.10
更改pop方法,将代码this.setActiveItem(this.getLateViewStack());移动到onBackButtonTap方法中,以解决pop后再push无动画效果的问题
2013.10.18
参照NavigationView大幅度修改代码,去除一些无用代码
2013.10.22
更新push方法,更好的配置自定义参数
移除debug标签,解决cmd压缩后出错的问题。感谢@低碳哥指出
2013.10.23
优化代码,移除了除了back之外的所有自定义事件。如需使用可以考虑用 activate与deactivate事件代替