sencha touch 扩展官方NavigationView 灵活添加按钮组,导航栏,自由隐藏返回按钮(2014-5-15)

扩展视频讲解:http://www.cnblogs.com/mlzs/p/3652094.html
官方NavigationView详解:http://www.cnblogs.com/mlzs/p/3550011.html
官方Container详解:http://www.cnblogs.com/mlzs/p/3548720.html

示例源码参见:https://bitbucket.org/moLangZaiShi/demo
注:扩展源码以示例为准

扩展源码:

  1 Ext.define('ux.navigation.View', {
  2     extend: 'Ext.Container',
  3     alternateClassName: 'ux.NavigationView',
  4     xtype: 'uxNavigationView',
  5     requires: ['ux.navigation.Bar'],
  6 
  7     config: {
  8         baseCls: Ext.baseCSSPrefix + 'navigationview',
  9         navigationBar: {
 10             docked: 'top',
 11             cls: 'navigationBar'
 12         },
 13         defaultBackButtonText: 'Back',
 14         useTitleForBackButtonText: false,
 15         layout: {
 16             type: 'card',
 17             animation: {
 18                 duration: 300,
 19                 easing: 'ease-out',
 20                 type: 'slide',
 21                 direction: 'left'
 22             }
 23         },
 24         index: null,
 25         //不推荐使用
 26         menu: null
 27     },
 28 
 29     platformConfig: [{
 30         theme: ['Blackberry'],
 31         navigationBar: {
 32             splitNavigation: true
 33         }
 34     }],
 35 
 36     // @private
 37     initialize: function () {
 38         var me = this,
 39         navBar = me.getNavigationBar();
 40 
 41         //监听导航栏返回按钮
 42         if (navBar) {
 43             navBar.on({
 44                 back: me.onBackButtonTap,
 45                 scope: me
 46             });
 47 
 48             me.relayEvents(navBar, 'rightbuttontap');
 49 
 50             me.relayEvents(me, {
 51                 add: 'push',
 52                 remove: 'pop'
 53             });
 54         }
 55         //<debug>
 56         var layout = me.getLayout();
 57         if (layout && !layout.isCard) {
 58             Ext.Logger.error('The base layout for a NavigationView must always be a Card Layout');
 59         }
 60         //</debug>
 61     },
 62 
 63     /**
 64      * @private
 65      */
 66     applyLayout: function (config) {
 67         config = config || {};
 68 
 69         return config;
 70     },
 71     //更新标题
 72     setTitle: function (text) {
 73         var me = this,
 74         nav = me.getNavigationBar();
 75         nav.setTitle(text);
 76         nav.backButtonStack[nav.backButtonStack.length - 1].title = text;
 77     },
 78     /*创建其他导航栏*/
 79     applyMenu: function (newItem) {
 80         if (!newItem) {
 81             return false;
 82         }
 83         var me = this;
 84         return me.factoryItem(newItem);
 85     },
 86     /*更新其他导航栏*/
 87     updateMenu: function (newItem, oldItem) {
 88         if (oldItem) {
 89             //console.log('oldItem:', oldItem.getItemId());
 90             this.remove(oldItem);
 91         }
 92         if (newItem) {
 93             //console.log('newItem:', newItem.getItemId());
 94             this.add(newItem);
 95         }
 96     },
 97     //更新index
 98     updateIndex: function (newItem, oldItem) {
 99         var me = this,
100         animation = this.getLayout().getAnimation(),
101         event = {
102             scope: me,
103             animationend: 'clearItem'
104         };
105         //添加监听
106         if (oldItem) {
107             //移除动画结束监听
108             animation.un(event);
109         }
110         if (Ext.isNumber(newItem)) {
111             //添加动画结束监听
112             animation.on(event);
113         }
114     },
115     //根据index清理多余的项
116     clearItem: function () {
117         var me = this,
118         innerItems = me.getInnerItems(),
119         length = innerItems.length,
120         index = me.getIndex();
121         count = length - index - 1;
122         toRemove = innerItems.splice(me.getIndex(), count);
123         //移除子项
124         for (i = 0; i < toRemove.length; i++) {
125             me.remove(toRemove[i]);
126         }
127     },
128     /**
129      * @private
130      * 点击返回按钮
131      */
132     onBackButtonTap: function () {
133         this.pop();
134         this.fireEvent('back', this);
135     },
136 
137     push: function (xtype, params) {
138         var me = this,
139         view = me.down('#' + xtype);
140         if (!view) {
141             params = params || {};
142             params.itemId = xtype;
143             view = Ext.create(xtype, params);
144             me.add(view);
145         } else if (view != me.getActiveItem()) {
146             me.pop(xtype);
147         }
148         return view;
149     },
150     /*移除当前激活项然后添加新的项xtype为null则只移除*/
151     popAndPush: function (xtype, params) {
152         this.pop(null, xtype);
153         if (xtype) {
154             this.push(xtype, params);
155         }
156     },
157     /**
158       * 不填写参数时,移除当前项,返回到上一项
159       * 如果参数是数字,则从最后一项开始移除指定数目的项
160       * 如果参数是string,则移除指定类型的项
161       * 如果参数是项,则移除传入的项
162       * 不论参数如何,都会保留一个活动项
163       * @return {Ext.Component} 当前活动项
164       */
165     pop: function (count, hide) {
166         if (this.beforePop(count)) {
167             return this.doPop(hide);
168         }
169     },
170 
171     /*删除指定项*/
172     beforePop: function (count) {
173         var me = this,
174         innerItems = me.getInnerItems();
175         if (Ext.isString(count) || Ext.isObject(count)) {
176             var last = innerItems.length - 1,
177             i;
178 
179             for (i = last; i >= 0; i--) {
180                 if ((Ext.isString(count) && Ext.ComponentQuery.is(innerItems[i], count)) || (Ext.isObject(count) && count == innerItems[i])) {
181                     count = last - i;
182                     break;
183                 }
184             }
185 
186             if (!Ext.isNumber(count)) {
187                 return false;
188             }
189         }
190 
191         var ln = innerItems.length,
192         toRemove;
193         //default to 1 pop
194         if (!Ext.isNumber(count) || count < 1) {
195             count = 1;
196         }
197 
198         //check if we are trying to remove more items than we have
199         count = Math.min(count, ln - 1);
200 
201         if (count) {
202             //we need to reset the backButtonStack in the navigation bar
203             me.getNavigationBar().beforePop(count);
204 
205             //get the items we need to remove from the view and remove theme
206             toRemove = innerItems.splice(-count, count - 1);
207             for (i = 0; i < toRemove.length; i++) {
208                 this.remove(toRemove[i]);
209             }
210 
211             return true;
212         }
213 
214         return false;
215     },
216 
217     /**
218      * @private
219      */
220     doPop: function (hide) {
221         var me = this,
222         innerItems = this.getInnerItems(),
223         item = innerItems[innerItems.length - 1];
224         if (hide) {
225             item.hide();
226         }
227         //set the new active item to be the new last item of the stack
228         me.remove(item);
229 
230         // Hide the backButton
231         if (innerItems.length < 3 && this.$backButton) {
232             this.$backButton.hide();
233         }
234 
235         // Update the title container
236         if (this.$titleContainer) {
237             //<debug>
238             if (!this.$titleContainer.setTitle) {
239                 Ext.Logger.error('You have selected to display a title in a component that does not 
240                     support titles in NavigationView. Please remove the `title` configuration from your 
241                     NavigationView item, or change it to a component that has a `setTitle` method.');
242             }
243             //</debug>
244             var item = innerItems[innerItems.length - 2];
245             this.$titleContainer.setTitle((item.getTitle) ? item.getTitle() : item.config.title);
246         }
247 
248         return this.getActiveItem();
249     },
250 
251     /**
252      * 返回上一项
253      * @return {Mixed} The previous view
254      */
255     getPreviousItem: function () {
256         var innerItems = this.getInnerItems();
257         return innerItems[innerItems.length - 2];
258     },
259 
260     /**
261      * Updates the backbutton text accordingly in the {@link #navigationBar}
262      * @private
263      */
264     updateUseTitleForBackButtonText: function (useTitleForBackButtonText) {
265         var navigationBar = this.getNavigationBar();
266         if (navigationBar) {
267             navigationBar.setUseTitleForBackButtonText(useTitleForBackButtonText);
268         }
269     },
270 
271     /**
272      * Updates the backbutton text accordingly in the {@link #navigationBar}
273      * @private
274      */
275     updateDefaultBackButtonText: function (defaultBackButtonText) {
276         var navigationBar = this.getNavigationBar();
277         if (navigationBar) {
278             navigationBar.setDefaultBackButtonText(defaultBackButtonText);
279         }
280     },
281 
282     // @private
283     applyNavigationBar: function (config) {
284         if (!config) {
285             config = {
286                 hidden: true,
287                 docked: 'top'
288             };
289         }
290 
291         if (config.title) {
292             delete config.title;
293             //<debug>
294             Ext.Logger.warn("Ext.navigation.View: The 'navigationBar' configuration does not accept a 'title' property. You " + "set the title of the navigationBar by giving this navigation view's children a 'title' property.");
295             //</debug>
296         }
297 
298         config.view = this;
299         config.useTitleForBackButtonText = this.getUseTitleForBackButtonText();
300         //是否分离导航按钮和标题栏
301         if (config.splitNavigation) {
302             this.$titleContainer = this.add({
303                 docked: 'top',
304                 xtype: 'titlebar',
305                 ui: 'light',
306                 title: this.$currentTitle || ''
307             });
308 
309             var containerConfig = (config.splitNavigation === true) ? {} : config.splitNavigation;
310 
311             this.$backButtonContainer = this.add(Ext.apply({
312                 xtype: 'toolbar',
313                 docked: 'bottom'
314             },
315             containerConfig));
316 
317             this.$backButton = this.$backButtonContainer.add({
318                 xtype: 'button',
319                 text: 'Back',
320                 hidden: true,
321                 ui: 'back'
322             });
323 
324             this.$backButton.on({
325                 scope: this,
326                 tap: this.onBackButtonTap
327             });
328 
329             config = {
330                 hidden: true,
331                 docked: 'top'
332             };
333         }
334 
335         return Ext.factory(config, ux.navigation.Bar, this.getNavigationBar());
336     },
337 
338     // @private
339     updateNavigationBar: function (newNavigationBar, oldNavigationBar) {
340         if (oldNavigationBar) {
341             this.remove(oldNavigationBar, true);
342         }
343 
344         if (newNavigationBar) {
345             this.add(newNavigationBar);
346         }
347     },
348 
349     /**
350      * @private
351      */
352     applyActiveItem: function (activeItem, currentActiveItem) {
353         var me = this,
354         innerItems = me.getInnerItems();
355 
356         // 确保项目已初始化
357         me.getItems();
358 
359         // 如果没有初始化, 将最后的子项激活
360         if (!me.initialized) {
361             activeItem = innerItems.length - 1;
362         }
363         //console.log(this.getItems().keys);
364         return this.callParent([activeItem, currentActiveItem]);
365     },
366     /*pop时反转切换动画*/
367     doResetActiveItem: function (innerIndex) {
368         var me = this,
369         innerItems = me.getInnerItems(),
370         animation = me.getLayout().getAnimation();
371 
372         if (innerIndex > 0) {
373             if (animation && animation.isAnimation) {
374                 animation.setReverse(true);
375             }
376             me.setActiveItem(innerIndex - 1);
377             me.getNavigationBar().onViewRemove(me, innerItems[innerIndex], innerIndex);
378         }
379     },
380 
381     /**
382      * 移除子项,调用remove方法后自动执行
383      * @private
384      */
385     doRemove: function () {
386         var animation = this.getLayout().getAnimation();
387 
388         if (animation && animation.isAnimation) {
389             animation.setReverse(false);
390         }
391 
392         this.callParent(arguments);
393     },
394 
395     onActivate: function (view) {
396         //更新menu
397         this.setMenu((view.getMenu) ? view.getMenu() : view.config.Menu);
398     },
399     /**
400      * 添加子项,调用add方法后自动执行
401      * @private
402      */
403     onItemAdd: function (item, index) {
404 
405         // 检测title配置
406         if (item && item.getDocked() && item.config.title === true) {
407             this.$titleContainer = item;
408         }
409         if (item.isInnerItem()) {
410             //添加监听,监听视图激活事件
411             item.on({
412                 scope: this,
413                 activate: 'onActivate'
414             });
415         }
416         this.doItemLayoutAdd(item, index);
417 
418         var navigaitonBar = this.getInitialConfig().navigationBar;
419 
420         if (!this.isItemsInitializing && item.isInnerItem()) {
421 
422             this.setActiveItem(item);
423             // 更新navigationBar
424             if (navigaitonBar) {
425                 //强制排序
426                 this.setIndex((item.getIndex) ? item.getIndex() : item.config.index);
427 
428                 this.getNavigationBar().onViewAdd(this, item, index);
429             }
430 
431             // 更新返回按钮
432             if (this.$backButtonContainer) {
433                 this.$backButton.show();
434             }
435         }
436 
437         if (item && item.isInnerItem()) {
438             // 更新标题
439             //console.log('更新标题', item);
440             this.updateTitleContainerTitle((item.getTitle) ? item.getTitle() : item.config.title);
441         }
442 
443         if (this.initialized) {
444             this.fireEvent('add', this, item, index);
445         }
446     },
447 
448     /**
449      * @private
450      * 更新titleContainer的标题,如果存在
451      */
452     updateTitleContainerTitle: function (title) {
453         if (this.$titleContainer) {
454             //<debug>
455             if (!this.$titleContainer.setTitle) {
456                 Ext.Logger.error('You have selected to display a title in a component that does not 
457                     support titles in NavigationView. Please remove the `title` configuration from your 
458                     NavigationView item, or change it to a component that has a `setTitle` method.');
459             }
460             //</debug>
461             this.$titleContainer.setTitle(title);
462         } else {
463             this.$currentTitle = title;
464         }
465     },
466 
467     /**
468      * 移除第一项和最后项之间的所有项(包括最后项)
469      * @return {Ext.Component} The view that is now active
470      */
471     reset: function () {
472         return this.pop(this.getInnerItems().length);
473     }
474 });
  1 /**
  2  * This component is used in {@link Ext.navigation.View} to control animations in the toolbar. You should never need to
  3  * interact with the component directly, unless you are subclassing it.
  4  * @private
  5  * @author Robert Dougan <rob@sencha.com>
  6  */
  7 Ext.define('ux.navigation.Bar', {
  8     extend: 'Ext.TitleBar',
  9 
 10     requires: ['Ext.Button', 'Ext.Spacer'],
 11 
 12     // @private
 13     isToolbar: true,
 14 
 15     config: {
 16         /**
 17          * @cfg
 18          * @inheritdoc
 19          */
 20         baseCls: Ext.baseCSSPrefix + 'toolbar',
 21         /**
 22          * @cfg {String} ui
 23          * Style options for Toolbar. Either 'light' or 'dark'.
 24          * @accessor
 25          */
 26         ui: 'dark',
 27 
 28         /**
 29          * @cfg {String} title
 30          * The title of the toolbar. You should NEVER set this, it is used internally. You set the title of the
 31          * navigation bar by giving a navigation views children a title configuration.
 32          * @private
 33          * @accessor
 34          */
 35         title: null,
 36 
 37         /**
 38          * @cfg
 39          * @hide
 40          * @accessor
 41          */
 42         defaultType: 'button',
 43 
 44         /**
 45          * @cfg
 46          * @ignore
 47          * @accessor
 48          */
 49         layout: {
 50             type: 'hbox'
 51         },
 52 
 53         /**
 54          * @cfg {Array/Object} items The child items to add to this NavigationBar. The {@link #cfg-defaultType} of
 55          * a NavigationBar is {@link Ext.Button}, so you do not need to specify an `xtype` if you are adding
 56          * buttons.
 57          *
 58          * You can also give items a `align` configuration which will align the item to the `left` or `right` of
 59          * the NavigationBar.
 60          * @hide
 61          * @accessor
 62          */
 63 
 64         /**
 65          * @cfg {String} defaultBackButtonText
 66          * The text to be displayed on the back button if:
 67          * a) The previous view does not have a title
 68          * b) The {@link #useTitleForBackButtonText} configuration is true.
 69          * @private
 70          * @accessor
 71          */
 72         defaultBackButtonText: '返回',
 73 
 74         /**
 75          * @cfg {Object} animation
 76          * @private
 77          * @accessor
 78          */
 79         animation: {
 80             duration: 300
 81         },
 82 
 83         /**
 84          * @cfg {Boolean} useTitleForBackButtonText
 85          * Set to false if you always want to display the {@link #defaultBackButtonText} as the text
 86          * on the back button. True if you want to use the previous views title.
 87          * @private
 88          * @accessor
 89          */
 90         useTitleForBackButtonText: null,
 91 
 92         /**
 93          * @cfg {Ext.navigation.View} view A reference to the navigation view this bar is linked to.
 94          * @private
 95          * @accessor
 96          */
 97         view: null,
 98 
 99         /**
100          * @cfg {Boolean} androidAnimation Optionally enable CSS transforms on Android 2
101          * for NavigationBar animations.  Note that this may cause flickering if the
102          * NavigationBar is hidden.
103          * @accessor
104          */
105         android2Transforms: false,
106 
107         /**
108          * @cfg {Ext.Button/Object} backButton The configuration for the back button
109          * @private
110          * @accessor
111          */
112         backButton: {
113             align: 'left',
114             ui: 'back',
115             hidden: true
116         },
117         /*导航栏临时控件组,在子项中配置*/
118         tmpItems: null,
119         /*导航栏临时cls,在子项中配置*/
120         tmpCls: null,
121         /*是否隐藏返回按钮,在子项中配置*/
122         backHide: true
123     },
124 
125     platformConfig: [{
126         theme: ['Blackberry'],
127         animation: false
128     }],
129 
130     /**
131      * @event back
132      * Fires when the back button was tapped.
133      * @param {Ext.navigation.Bar} this This bar
134      */
135 
136     constructor: function (config) {
137         config = config || {};
138 
139         if (!config.items) {
140             config.items = [];
141         }
142 
143         this.backButtonStack = [];
144         this.activeAnimations = [];
145 
146         this.callParent([config]);
147     },
148     /*创建导航栏临时控件组*/
149     applyTmpItems: function (newItems) {
150         if (!newItems) return false;
151         var me = this,
152         navItems = [],
153         i,
154         ln;
155         newItems = Ext.Array.from(newItems);
156         for (i = 0, ln = newItems.length; i < ln; i++) {
157             var item = newItems[i],
158             fire = item.fire,
159             btn = me.factoryItem(item);
160             if (fire) {
161                 btn.on({
162                     tap: 'onTmpItemTap',
163                     scope: me
164                 });
165             }
166             navItems.push(me.factoryItem(btn));
167         }
168         return navItems;
169     },
170     //临时控件被点击时
171     onTmpItemTap: function (t) {
172         var me = this,
173         view = me.getView().getActiveItem(),
174         fire = t.fire;
175         view.fireAction(fire, [me, view, t], 'do' + fire);
176     },
177     /*更新导航栏临时控件组*/
178     updateTmpItems: function (newItem, oldItem) {
179         if (oldItem) {
180             var i, ln;
181             for (i = 0, ln = oldItem.length; i < ln; i++) {
182                 this.remove(oldItem[i]);
183             }
184         }
185         if (newItem) {
186             this.add(newItem);
187         }
188     },
189     /*更新导航栏临时cls*/
190     updateTmpCls: function (newItem, oldItem) {
191         if (oldItem) {
192             this.removeCls(oldItem);
193         }
194         if (newItem) {
195             this.addCls(newItem);
196         }
197     },
198     /**
199      * @private
200      */
201     applyBackButton: function (config) {
202         return Ext.factory(config, Ext.Button, this.getBackButton());
203     },
204 
205     /**
206      * @private
207      */
208     updateBackButton: function (newBackButton, oldBackButton) {
209         if (oldBackButton) {
210             this.remove(oldBackButton);
211         }
212 
213         if (newBackButton) {
214             this.add(newBackButton);
215 
216             newBackButton.on({
217                 scope: this,
218                 tap: this.onBackButtonTap
219             });
220         }
221     },
222 
223     onBackButtonTap: function () {
224         this.fireEvent('back', this);
225     },
226 
227     /**
228      * @private
229      */
230     updateView: function (newView) {
231         var me = this,
232         backButton = me.getBackButton(),
233         innerItems,
234         i,
235         backButtonText,
236         item,
237         title,
238         titleText,
239         bar;
240 
241         me.getItems();
242 
243         if (newView) {
244             //update the back button stack with the current inner items of the view
245             innerItems = newView.getInnerItems();
246             for (i = 0; i < innerItems.length; i++) {
247                 item = innerItems[i];
248                 title = (item.getTitle) ? item.getTitle() : item.config.title;
249                 bar = (item.getBar) ? item.getBar() : item.config.bar;
250 
251                 me.backButtonStack.push({
252                     title: title || '&nbsp;',
253                     bar: bar
254                 });
255             }
256             titleText = me.getTitleText();
257 
258             if (titleText === undefined) {
259                 titleText = '';
260             }
261 
262             me.setTitle(titleText);
263 
264             backButtonText = me.getBackButtonText();
265             if (backButtonText) {
266                 backButton.setText(backButtonText);
267                 backButton.show();
268             }
269             //更新bar
270             me.updateBar();
271         }
272     },
273 
274     /**
275      * @private
276      */
277     onViewAdd: function (view, item) {
278         var me = this,
279         backButtonStack = me.backButtonStack,
280         hasPrevious, title, bar;
281 
282         me.endAnimation();
283 
284         title = (item.getTitle) ? item.getTitle() : item.config.title;
285         bar = (item.getBar) ? item.getBar() : item.config.bar;
286         //移除额外的历史记录
287         var index = (item.getIndex) ? item.getIndex() : item.config.index;
288         if (Ext.isNumber(index)) {
289             var length = view.getInnerItems().length,
290             count = length - index;
291             me.beforePop(count);
292         }
293         backButtonStack.push({
294             title: title || '&nbsp;',
295             bar: bar
296         });
297 
298         hasPrevious = backButtonStack.length > 1;
299         me.doChangeView(view, hasPrevious, false);
300     },
301 
302     /**
303      * @private
304      */
305     onViewRemove: function (view) {
306         var me = this,
307         backButtonStack = me.backButtonStack,
308         hasPrevious;
309 
310         me.endAnimation();
311         backButtonStack.pop();
312         hasPrevious = backButtonStack.length > 1;
313 
314         me.doChangeView(view, hasPrevious, true);
315     },
316 
317     /**
318      * @private
319      */
320     doChangeView: function (view, hasPrevious, reverse) {
321         var me = this,
322         leftBox = me.leftBox,
323         leftBoxElement = leftBox.element,
324         titleComponent = me.titleComponent,
325         titleElement = titleComponent.element,
326         backButton = me.getBackButton(),
327         titleText = me.getTitleText(),
328         backButtonText = me.getBackButtonText(),
329         animation = me.getAnimation() && view.getLayout().getAnimation(),
330         animated = animation && animation.isAnimation && view.isPainted(),
331         properties,
332         leftGhost,
333         titleGhost,
334         leftProps,
335         titleProps;
336 
337         if (animated) {
338             leftGhost = me.createProxy(leftBox.element);
339             leftBoxElement.setStyle('opacity', '0');
340             backButton.setText(backButtonText);
341             backButton[hasPrevious ? 'show' : 'hide']();
342 
343             titleGhost = me.createProxy(titleComponent.element.getParent());
344             titleElement.setStyle('opacity', '0');
345             me.setTitle(titleText);
346 
347             properties = me.measureView(leftGhost, titleGhost, reverse);
348             leftProps = properties.left;
349             titleProps = properties.title;
350 
351             me.isAnimating = true;
352             me.animate(leftBoxElement, leftProps.element);
353             me.animate(titleElement, titleProps.element,
354             function () {
355                 titleElement.setLeft(properties.titleLeft);
356                 me.isAnimating = false;
357                 me.refreshTitlePosition();
358             });
359 
360             if (Ext.browser.is.AndroidStock2 && !this.getAndroid2Transforms()) {
361                 leftGhost.ghost.destroy();
362                 titleGhost.ghost.destroy();
363             } else {
364                 me.animate(leftGhost.ghost, leftProps.ghost);
365                 me.animate(titleGhost.ghost, titleProps.ghost,
366                 function () {
367                     leftGhost.ghost.destroy();
368                     titleGhost.ghost.destroy();
369                 });
370             }
371 
372         } else {
373             if (hasPrevious) {
374                 backButton.setText(backButtonText);
375                 backButton.show();
376             } else {
377                 backButton.hide();
378             }
379             me.setTitle(titleText);
380         }
381         me.updateBar();
382     },
383     //更新bar
384     updateBar: function () {
385         //更新其他
386         var bar = this.getBar() || {};
387         //设置导航栏临时控件组
388         this.setTmpItems(bar.items);
389         //设置导航栏临时cls
390         this.setTmpCls(bar.cls);
391         //更新返回按钮状态
392         if (bar.backHide) {
393             backButton.hide();
394         }
395     },
396     /**
397      * Calculates and returns the position values needed for the back button when you are pushing a title.
398      * @private
399      */
400     measureView: function (oldLeft, oldTitle, reverse) {
401         var me = this,
402         barElement = me.element,
403         newLeftElement = me.leftBox.element,
404         titleElement = me.titleComponent.element,
405         minOffset = Math.min(barElement.getWidth() / 3, 200),
406         newLeftWidth = newLeftElement.getWidth(),
407         barX = barElement.getX(),
408         barWidth = barElement.getWidth(),
409         titleX = titleElement.getX(),
410         titleLeft = titleElement.getLeft(),
411         titleWidth = titleElement.getWidth(),
412         oldLeftX = oldLeft.x,
413         oldLeftWidth = oldLeft.width,
414         oldLeftLeft = oldLeft.left,
415         useLeft = Ext.browser.is.AndroidStock2 && !this.getAndroid2Transforms(),
416         newOffset,
417         oldOffset,
418         leftAnims,
419         titleAnims,
420         omega,
421         theta;
422 
423         theta = barX - oldLeftX - oldLeftWidth;
424         if (reverse) {
425             newOffset = theta;
426             oldOffset = Math.min(titleX - oldLeftWidth, minOffset);
427         } else {
428             oldOffset = theta;
429             newOffset = Math.min(titleX - barX, minOffset);
430         }
431 
432         if (useLeft) {
433             leftAnims = {
434                 element: {
435                     from: {
436                         left: newOffset,
437                         opacity: 1
438                     },
439                     to: {
440                         left: 0,
441                         opacity: 1
442                     }
443                 }
444             };
445         } else {
446             leftAnims = {
447                 element: {
448                     from: {
449                         transform: {
450                             translateX: newOffset
451                         },
452                         opacity: 0
453                     },
454                     to: {
455                         transform: {
456                             translateX: 0
457                         },
458                         opacity: 1
459                     }
460                 },
461                 ghost: {
462                     to: {
463                         transform: {
464                             translateX: oldOffset
465                         },
466                         opacity: 0
467                     }
468                 }
469             };
470         }
471 
472         theta = barX - titleX + newLeftWidth;
473         if ((oldLeftLeft + titleWidth) > titleX) {
474             omega = barX - titleX - titleWidth;
475         }
476 
477         if (reverse) {
478             titleElement.setLeft(0);
479 
480             oldOffset = barX + barWidth - titleX - titleWidth;
481 
482             if (omega !== undefined) {
483                 newOffset = omega;
484             } else {
485                 newOffset = theta;
486             }
487         } else {
488             newOffset = barX + barWidth - titleX - titleWidth;
489 
490             if (omega !== undefined) {
491                 oldOffset = omega;
492             } else {
493                 oldOffset = theta;
494             }
495 
496             newOffset = Math.max(titleLeft, newOffset);
497         }
498 
499         if (useLeft) {
500             titleAnims = {
501                 element: {
502                     from: {
503                         left: newOffset,
504                         opacity: 1
505                     },
506                     to: {
507                         left: titleLeft,
508                         opacity: 1
509                     }
510                 }
511             };
512         } else {
513             titleAnims = {
514                 element: {
515                     from: {
516                         transform: {
517                             translateX: newOffset
518                         },
519                         opacity: 0
520                     },
521                     to: {
522                         transform: {
523                             translateX: titleLeft
524                         },
525                         opacity: 1
526                     }
527                 },
528                 ghost: {
529                     to: {
530                         transform: {
531                             translateX: oldOffset
532                         },
533                         opacity: 0
534                     }
535                 }
536             };
537         }
538 
539         return {
540             left: leftAnims,
541             title: titleAnims,
542             titleLeft: titleLeft
543         };
544     },
545 
546     /**
547      * Helper method used to animate elements.
548      * You pass it an element, objects for the from and to positions an option onEnd callback called when the animation is over.
549      * Normally this method is passed configurations returned from the methods such as #measureTitle(true) etc.
550      * It is called from the #pushLeftBoxAnimated, #pushTitleAnimated, #popBackButtonAnimated and #popTitleAnimated
551      * methods.
552      *
553      * If the current device is Android, it will use top/left to animate.
554      * If it is anything else, it will use transform.
555      * @private
556      */
557     animate: function (element, config, callback) {
558         var me = this,
559         animation;
560 
561         //reset the left of the element
562         element.setLeft(0);
563 
564         config = Ext.apply(config, {
565             element: element,
566             easing: 'ease-in-out',
567             duration: me.getAnimation().duration || 250,
568             preserveEndState: true
569         });
570 
571         animation = new Ext.fx.Animation(config);
572         animation.on('animationend',
573         function () {
574             if (callback) {
575                 callback.call(me);
576             }
577         },
578         me);
579 
580         Ext.Animator.run(animation);
581         me.activeAnimations.push(animation);
582     },
583 
584     endAnimation: function () {
585         var activeAnimations = this.activeAnimations,
586         animation, i, ln;
587 
588         if (activeAnimations) {
589             ln = activeAnimations.length;
590             for (i = 0; i < ln; i++) {
591                 animation = activeAnimations[i];
592                 if (animation.isAnimating) {
593                     animation.stopAnimation();
594                 } else {
595                     animation.destroy();
596                 }
597             }
598             this.activeAnimations = [];
599         }
600     },
601 
602     refreshTitlePosition: function () {
603         if (!this.isAnimating) {
604             this.callParent();
605         }
606     },
607 
608     /**
609      * Returns the text needed for the current back button at anytime.
610      * @private
611      */
612     getBackButtonText: function () {
613         var text = this.backButtonStack[this.backButtonStack.length - 2],
614         useTitleForBackButtonText = this.getUseTitleForBackButtonText();
615 
616         if (!useTitleForBackButtonText) {
617             if (text) {
618                 text = this.getDefaultBackButtonText();
619             }
620         }
621 
622         return text;
623     },
624 
625     /**
626      * Returns the text needed for the current title at anytime.
627      * @private
628      */
629     getTitleText: function () {
630         if (this.backButtonStack.length == 0) {
631             return '';
632         }
633         return this.backButtonStack[this.backButtonStack.length - 1].title;
634     },
635     getBar: function () {
636         if (this.backButtonStack.length == 0) {
637             return false;
638         }
639         return this.backButtonStack[this.backButtonStack.length - 1].bar;
640     },
641     /**
642      * Handles removing back button stacks from this bar
643      * @private
644      */
645     beforePop: function (count) {
646         count--;
647         for (var i = 0; i < count; i++) {
648             this.backButtonStack.pop();
649         }
650     },
651 
652     /**
653      * We override the hidden method because we don't want to remove it from the view using display:none. Instead we just position it off
654      * the screen, much like the navigation bar proxy. This means that all animations, pushing, popping etc. all still work when if you hide/show
655      * this bar at any time.
656      * @private
657      */
658     doSetHidden: function (hidden) {
659         if (!hidden) {
660             this.element.setStyle({
661                 position: 'relative',
662                 top: 'auto',
663                 left: 'auto',
664                  'auto'
665             });
666         } else {
667             this.element.setStyle({
668                 position: 'absolute',
669                 top: '-1000px',
670                 left: '-1000px',
671                  this.element.getWidth() + 'px'
672             });
673         }
674     },
675 
676     /**
677      * Creates a proxy element of the passed element, and positions it in the same position, using absolute positioning.
678      * The createNavigationBarProxy method uses this to create proxies of the backButton and the title elements.
679      * @private
680      */
681     createProxy: function (element) {
682         var ghost, x, y, left, width;
683 
684         ghost = element.dom.cloneNode(true);
685         ghost.id = element.id + '-proxy';
686 
687         //insert it into the toolbar
688         element.getParent().dom.appendChild(ghost);
689 
690         //set the x/y
691         ghost = Ext.get(ghost);
692         x = element.getX();
693         y = element.getY();
694         left = element.getLeft();
695         width = element.getWidth();
696         ghost.setStyle('position', 'absolute');
697         ghost.setX(x);
698         ghost.setY(y);
699         ghost.setHeight(element.getHeight());
700         ghost.setWidth(width);
701 
702         return {
703             x: x,
704             y: y,
705             left: left,
706              width,
707             ghost: ghost
708         };
709     }
710 });
原文地址:https://www.cnblogs.com/mlzs/p/3655762.html