Bucs
26 Apr 2011, 7:37 AM
Sencha Touch version tested:
1.1 rev ____
custom css (include details)
CSS is pretty generic CSS generated with standard SCSS file.
Platform tested against:
iOS 3.x
iOS 4
Android 2.2
Description:
All works perfect on iOS 3/4 but we're seeing all kinds of scrolling issues and "overlay/underlay" scrolling happening on Android devices with 2.2 running. The pages that exhibit this behavior are all custom components extended from Ext.Panel. It is almost as if all the separate components that are initialized in the initComponent event are scrolling independently of each other.
Test Case:
Viewport component
mmEngine.Viewport = Ext.extend(Ext.Panel, {
id: 'viewport',
fullscreen: true,
layout: 'card',
// Custom Properties
lastPage: '',
// Init
initComponent: function () {
Ext.apply(this, {
dockedItems: [
{ xtype: 'MenuBar', height: 35 }
],
listeners: {
beforecardswitch: function (cont, newCard, oldCard, index, anim) {
// code removed...not doing much here
},
afterrender: function (cmp) {
// code removed...just doing selective dispatching here
}
}
});
mmEngine.Viewport.superclass.initComponent.apply(this, arguments);
}
});
ViewHome code (initial page)
Ext.namespace('mmEngine.Views');
mmEngine.Views.ViewHome = Ext.extend(Ext.Panel, {
// config properties
id: 'ViewHome',
title: 'Home',
fullscreen: false,
scroll: 'vertical',
monitorOrientation: true,
// custom properties
menuCardIndex: 0,
initComponent: function () {
Ext.apply(this, {
items: [
{
xtype: 'Header'
},
{
xtype: 'AutoCarousel',
style: 'text-align: center',
height: 127,
direction: 'horizontal',
centered: true,
indicator: false,
scroll: false,
logToConsole: false,
items: [
{ title: 'Home Image 1', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/More-Interface-Solutions.jpg" width="320" /></div>' },
{ title: 'Home Image 2', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/Serving-The-Professionals.jpg" width="320" /></div>' },
{ title: 'Home Image 3', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/Free-Markertek-Catalog.jpg" width="320" /></div>' },
{ title: 'Home Image 4', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/Weekly-How-To-Video.jpg" width="320" /></div>' }
]
},
{ xtype: 'container', height: 1, style: 'background-color: #34537E' },
{
xtype: 'SearchField',
listeners: {
searchEntered: function (term) {
this.setValue('');
Ext.dispatch({ controller: 'page', action: 'changePage', viewName: 'ViewSearchResults', searchTerm: term, parentView: this.ownerCt.id });
//alert('You entered: ' + term);
}
}
},
{
id: 'listHomeMenu',
xtype: 'list',
scroll: false,
style: 'background-color: #00275E; padding: 10px',
store: new Ext.data.Store({
fields: ['menuItemName', 'action', 'viewName', 'url'],
data: [
{
menuItemName: 'Shop by Category',
action: 'changePage',
viewName: 'ViewNestedMenu',
url: mmEngine.settings.SiteUrl + 'menus.asp'
}, {
menuItemName: 'Shop by Brand',
action: 'changePage',
viewName: 'ViewListMenu',
url: mmEngine.settings.SiteUrl + 'SEOMfgList.asp'
}, {
menuItemName: 'Weekly How-To Videos',
action: 'redirect',
viewName: 'ViewVideoChannel',
url: 'http://m.youtube.com/markertekvideosupply'
}, {
menuItemName: 'Today\'s Industry News',
action: 'changePage',
viewName: 'ViewIndustryNews',
url: 'http://www.markertek.com/rss/rss.xml'
}, {
menuItemName: 'Daily Top 100 Products',
action: 'changePage',
viewName: 'ViewProductList',
url: mmEngine.settings.SiteUrl + 'SEOspecialNewItems.asp'
}, {
menuItemName: 'Contact Us',
action: 'changePage',
viewName: 'ViewContactUs',
url: ''
}
]
}),
listeners: {
itemtap: function (dv, index, item, e) {
var ds = dv.getStore();
r = ds.getAt(index);
switch (r.get('action')) {
case "redirect":
window.location.href = r.get('url');
break;
case "changePage":
// Set MenuBar to 'Shop' if needed
if (r.get('menuItemName') == "Shop by Category" ||
r.get('menuItemName') == "Shop by Brand" ||
r.get('menuItemName') == "Daily Top 100 Products") {
this.ownerCt.ownerCt.query('MenuBar')[0].setActiveButton('ViewShop');
}
Ext.dispatch({
controller: 'page',
action: r.get('action'),
viewName: r.get('viewName'),
menuItemName: r.get('menuItemName'),
url: r.get('url'),
menuTitle: r.get('menuItemName'),
parentView: 'ViewHome',
listName: r.get('menuItemName'),
listUrl: r.get('url')
});
break;
}
},
selectionChange: function (sm, recs) {
sm.deselectAll();
}
},
itemTpl: '<tpl for="."><div>{menuItemName}<span style="float: right; padding-right: 10px; padding-top: 3px"><image src="UI/Assets/Images/arrow.gif" /></span></div></tpl>'
},
{ xtype: 'Footer' }
],
listeners: {
beforeactivate: function (cmp) {
this.query('AutoCarousel')[0].resumeEvents();
this.query('AutoCarousel')[0].fireEvent('startRotation');
},
deactivate: function (cmp) {
this.query('AutoCarousel')[0].fireEvent('stopRotation');
},
beforeorientationchange: function (cmp, sOrientation, iWidth, iHeight) {
this.query('AutoCarousel')[0].setWidth(iWidth);
}
}
});
mmEngine.Views.ViewHome.superclass.initComponent.apply(this, arguments);
},
// Customm Events
destroyComponents: function () {
var cmp = null;
cmp = Ext.getCmp('ViewHome');
if (cmp != null) { cmp.destroy(); }
},
getOrientation: function () {
return Ext.orientation == 'portrait' ? 320 : 480;
}
});
Ext.reg('ViewHome', mmEngine.Views.ViewHome);
AutoCarousel (extends Carousel to provide auto rotating methods to listen to)
Ext.namespace('mmEngine.Views.Controls');
mmEngine.Views.Controls.AutoCarousel = Ext.extend(Ext.Carousel, {
// Required Config Items
height: null,
items: null,
// Default Config Properties
itemId: 'AutoCarousel',
direction: 'horizontal',
centered: true,
indicator: false,
scroll: false,
logToConsole: false,
// Component Custom Properties
cardSwitchDelay: 4000,
cardAnimation: { type: 'slide', direction: 'left' },
autoNext: new Ext.util.DelayedTask(
function () {
this.fireEvent('autoNextCard');
}
),
// Component Events
listeners: {
autoNextCard: function () {
var next = this.layout.getNext();
next = next ? next : this.items.get(0);
this.setActiveItem(next, this.cardAnimation);
this.autoNext.delay(this.cardSwitchDelay, null, this);
if (this.logToConsole) console.log( this.ownerCt.id + ' carousel card changed');
},
startRotation: function () {
this.autoNext.delay(this.cardSwitchDelay, null, this);
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel started');
},
stopRotation: function () {
this.autoNext.cancel();
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel stopped');
},
pauseRotation: function () {
this.suspendEvents(false);
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel paused');
}
},
// Component Initialization
initComponent: function (config) {
// Add component Events
this.addEvents('autoNextCard', 'startRotation', 'pauseRotation', 'stopRotation');
Ext.apply(this, config);
mmEngine.Views.Controls.AutoCarousel.superclass.initComponent.apply(this, arguments);
},
onDestroy: function () {
Ext.each(this.items, function (item) {
item.destroy();
});
// Remove manually created property refs (to avoid mem leaks)
delete this.cardSwitchDelay;
delete this.cardAnimation;
delete this.autoNext;
// Remove all listeners
this.clearListeners();
mmEngine.Views.Controls.AutoCarousel.superclass.onDestroy.call(this);
}
});
Ext.reg('AutoCarousel', mmEngine.Views.Controls.AutoCarousel);
See this URL : http://m.markertek.com/
Steps to reproduce the problem:
Simply browse to the above home page on an HTC Incredible device
The result that was expected:
Expecting similar layout, scrolling, and functionality as iPhone since Android is a WebKit browser
The result that occurs instead:
See for yourself with the above URL but scrolling is a mess and quickly locks up.
Debugging already done:
Trying to break page into smaller units and isolate where errors are coming from. We think there is a issue with the Carousel control on this same device that may be causing some, if not all, of these issues. We have even tried to replace our extension of Carousel with the normal Sencha component and had the same results.
Potential Fixes:
Do we have a layout issue in our pages within the viewport? Are we not providing proper layout config's? Again, works great in iOS, but a mess in Android...could this be CSS differences or does this relate to Android browser rendering issues?
Lastly, we LOVE the Sencha framework, and have implemented for some high exposure clients and have several coming up that we love to use Sencha on...however, we are starting to get push back from the higher ups on dumping Sencha due to all the Android (which is WebKit) incompatibilities. The dev group really does NOT want to let this happen as we see so much value in the framework...but it's getting tough to argue this point with upper mgmt when all they hear is complaints from these high profile clients..
Is there a list of Android gotcha's that we need to worry about, or best practices to minimize Android related issues? I would highly recommend an Android specific forum it's increasingly becoming it's own animal and it's hard to track down any info on issues that are popping up at an alarming rate.
Again, we LOVE the product, it's fantastic...but we need to know where Sencha is headed on Android support as our clients are going to expect the functionality to be as similar as possible or degrade gracefully somehow. Thx.
1.1 rev ____
custom css (include details)
CSS is pretty generic CSS generated with standard SCSS file.
Platform tested against:
iOS 3.x
iOS 4
Android 2.2
Description:
All works perfect on iOS 3/4 but we're seeing all kinds of scrolling issues and "overlay/underlay" scrolling happening on Android devices with 2.2 running. The pages that exhibit this behavior are all custom components extended from Ext.Panel. It is almost as if all the separate components that are initialized in the initComponent event are scrolling independently of each other.
Test Case:
Viewport component
mmEngine.Viewport = Ext.extend(Ext.Panel, {
id: 'viewport',
fullscreen: true,
layout: 'card',
// Custom Properties
lastPage: '',
// Init
initComponent: function () {
Ext.apply(this, {
dockedItems: [
{ xtype: 'MenuBar', height: 35 }
],
listeners: {
beforecardswitch: function (cont, newCard, oldCard, index, anim) {
// code removed...not doing much here
},
afterrender: function (cmp) {
// code removed...just doing selective dispatching here
}
}
});
mmEngine.Viewport.superclass.initComponent.apply(this, arguments);
}
});
ViewHome code (initial page)
Ext.namespace('mmEngine.Views');
mmEngine.Views.ViewHome = Ext.extend(Ext.Panel, {
// config properties
id: 'ViewHome',
title: 'Home',
fullscreen: false,
scroll: 'vertical',
monitorOrientation: true,
// custom properties
menuCardIndex: 0,
initComponent: function () {
Ext.apply(this, {
items: [
{
xtype: 'Header'
},
{
xtype: 'AutoCarousel',
style: 'text-align: center',
height: 127,
direction: 'horizontal',
centered: true,
indicator: false,
scroll: false,
logToConsole: false,
items: [
{ title: 'Home Image 1', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/More-Interface-Solutions.jpg" width="320" /></div>' },
{ title: 'Home Image 2', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/Serving-The-Professionals.jpg" width="320" /></div>' },
{ title: 'Home Image 3', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/Free-Markertek-Catalog.jpg" width="320" /></div>' },
{ title: 'Home Image 4', html: '<div><image src="' + (mmEngine.settings.UseTinySrc ? mmEngine.settings.TinySrcUrl : "") + mmEngine.settings.SiteUrl + 'images/Banners/Weekly-How-To-Video.jpg" width="320" /></div>' }
]
},
{ xtype: 'container', height: 1, style: 'background-color: #34537E' },
{
xtype: 'SearchField',
listeners: {
searchEntered: function (term) {
this.setValue('');
Ext.dispatch({ controller: 'page', action: 'changePage', viewName: 'ViewSearchResults', searchTerm: term, parentView: this.ownerCt.id });
//alert('You entered: ' + term);
}
}
},
{
id: 'listHomeMenu',
xtype: 'list',
scroll: false,
style: 'background-color: #00275E; padding: 10px',
store: new Ext.data.Store({
fields: ['menuItemName', 'action', 'viewName', 'url'],
data: [
{
menuItemName: 'Shop by Category',
action: 'changePage',
viewName: 'ViewNestedMenu',
url: mmEngine.settings.SiteUrl + 'menus.asp'
}, {
menuItemName: 'Shop by Brand',
action: 'changePage',
viewName: 'ViewListMenu',
url: mmEngine.settings.SiteUrl + 'SEOMfgList.asp'
}, {
menuItemName: 'Weekly How-To Videos',
action: 'redirect',
viewName: 'ViewVideoChannel',
url: 'http://m.youtube.com/markertekvideosupply'
}, {
menuItemName: 'Today\'s Industry News',
action: 'changePage',
viewName: 'ViewIndustryNews',
url: 'http://www.markertek.com/rss/rss.xml'
}, {
menuItemName: 'Daily Top 100 Products',
action: 'changePage',
viewName: 'ViewProductList',
url: mmEngine.settings.SiteUrl + 'SEOspecialNewItems.asp'
}, {
menuItemName: 'Contact Us',
action: 'changePage',
viewName: 'ViewContactUs',
url: ''
}
]
}),
listeners: {
itemtap: function (dv, index, item, e) {
var ds = dv.getStore();
r = ds.getAt(index);
switch (r.get('action')) {
case "redirect":
window.location.href = r.get('url');
break;
case "changePage":
// Set MenuBar to 'Shop' if needed
if (r.get('menuItemName') == "Shop by Category" ||
r.get('menuItemName') == "Shop by Brand" ||
r.get('menuItemName') == "Daily Top 100 Products") {
this.ownerCt.ownerCt.query('MenuBar')[0].setActiveButton('ViewShop');
}
Ext.dispatch({
controller: 'page',
action: r.get('action'),
viewName: r.get('viewName'),
menuItemName: r.get('menuItemName'),
url: r.get('url'),
menuTitle: r.get('menuItemName'),
parentView: 'ViewHome',
listName: r.get('menuItemName'),
listUrl: r.get('url')
});
break;
}
},
selectionChange: function (sm, recs) {
sm.deselectAll();
}
},
itemTpl: '<tpl for="."><div>{menuItemName}<span style="float: right; padding-right: 10px; padding-top: 3px"><image src="UI/Assets/Images/arrow.gif" /></span></div></tpl>'
},
{ xtype: 'Footer' }
],
listeners: {
beforeactivate: function (cmp) {
this.query('AutoCarousel')[0].resumeEvents();
this.query('AutoCarousel')[0].fireEvent('startRotation');
},
deactivate: function (cmp) {
this.query('AutoCarousel')[0].fireEvent('stopRotation');
},
beforeorientationchange: function (cmp, sOrientation, iWidth, iHeight) {
this.query('AutoCarousel')[0].setWidth(iWidth);
}
}
});
mmEngine.Views.ViewHome.superclass.initComponent.apply(this, arguments);
},
// Customm Events
destroyComponents: function () {
var cmp = null;
cmp = Ext.getCmp('ViewHome');
if (cmp != null) { cmp.destroy(); }
},
getOrientation: function () {
return Ext.orientation == 'portrait' ? 320 : 480;
}
});
Ext.reg('ViewHome', mmEngine.Views.ViewHome);
AutoCarousel (extends Carousel to provide auto rotating methods to listen to)
Ext.namespace('mmEngine.Views.Controls');
mmEngine.Views.Controls.AutoCarousel = Ext.extend(Ext.Carousel, {
// Required Config Items
height: null,
items: null,
// Default Config Properties
itemId: 'AutoCarousel',
direction: 'horizontal',
centered: true,
indicator: false,
scroll: false,
logToConsole: false,
// Component Custom Properties
cardSwitchDelay: 4000,
cardAnimation: { type: 'slide', direction: 'left' },
autoNext: new Ext.util.DelayedTask(
function () {
this.fireEvent('autoNextCard');
}
),
// Component Events
listeners: {
autoNextCard: function () {
var next = this.layout.getNext();
next = next ? next : this.items.get(0);
this.setActiveItem(next, this.cardAnimation);
this.autoNext.delay(this.cardSwitchDelay, null, this);
if (this.logToConsole) console.log( this.ownerCt.id + ' carousel card changed');
},
startRotation: function () {
this.autoNext.delay(this.cardSwitchDelay, null, this);
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel started');
},
stopRotation: function () {
this.autoNext.cancel();
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel stopped');
},
pauseRotation: function () {
this.suspendEvents(false);
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel paused');
}
},
// Component Initialization
initComponent: function (config) {
// Add component Events
this.addEvents('autoNextCard', 'startRotation', 'pauseRotation', 'stopRotation');
Ext.apply(this, config);
mmEngine.Views.Controls.AutoCarousel.superclass.initComponent.apply(this, arguments);
},
onDestroy: function () {
Ext.each(this.items, function (item) {
item.destroy();
});
// Remove manually created property refs (to avoid mem leaks)
delete this.cardSwitchDelay;
delete this.cardAnimation;
delete this.autoNext;
// Remove all listeners
this.clearListeners();
mmEngine.Views.Controls.AutoCarousel.superclass.onDestroy.call(this);
}
});
Ext.reg('AutoCarousel', mmEngine.Views.Controls.AutoCarousel);
See this URL : http://m.markertek.com/
Steps to reproduce the problem:
Simply browse to the above home page on an HTC Incredible device
The result that was expected:
Expecting similar layout, scrolling, and functionality as iPhone since Android is a WebKit browser
The result that occurs instead:
See for yourself with the above URL but scrolling is a mess and quickly locks up.
Debugging already done:
Trying to break page into smaller units and isolate where errors are coming from. We think there is a issue with the Carousel control on this same device that may be causing some, if not all, of these issues. We have even tried to replace our extension of Carousel with the normal Sencha component and had the same results.
Potential Fixes:
Do we have a layout issue in our pages within the viewport? Are we not providing proper layout config's? Again, works great in iOS, but a mess in Android...could this be CSS differences or does this relate to Android browser rendering issues?
Lastly, we LOVE the Sencha framework, and have implemented for some high exposure clients and have several coming up that we love to use Sencha on...however, we are starting to get push back from the higher ups on dumping Sencha due to all the Android (which is WebKit) incompatibilities. The dev group really does NOT want to let this happen as we see so much value in the framework...but it's getting tough to argue this point with upper mgmt when all they hear is complaints from these high profile clients..
Is there a list of Android gotcha's that we need to worry about, or best practices to minimize Android related issues? I would highly recommend an Android specific forum it's increasingly becoming it's own animal and it's hard to track down any info on issues that are popping up at an alarming rate.
Again, we LOVE the product, it's fantastic...but we need to know where Sencha is headed on Android support as our clients are going to expect the functionality to be as similar as possible or degrade gracefully somehow. Thx.