View Full Version : Massive Errors on Android 2.2

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


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)

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) {
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');
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") {

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')
selectionChange: function (sm, recs) {
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) {
deactivate: function (cmp) {
beforeorientationchange: function (cmp, sOrientation, iWidth, iHeight) {

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)

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 () {
// 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 () {
if (this.logToConsole) console.log(this.ownerCt.id + ' carousel stopped');
pauseRotation: function () {
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) {

// Remove manually created property refs (to avoid mem leaks)
delete this.cardSwitchDelay;
delete this.cardAnimation;
delete this.autoNext;

// Remove all listeners
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.

Jacky Nguyen
4 May 2011, 9:39 AM
This is a well-known issue of Android implementation in HTC devices.

Technically in order to prevent the whole browser viewport to move when panning the page with fingers (which makes the scrollers possible), we need to prevent the default touch event with Event#preventDefault(). In HTC-specific Androids, when the CPU keeps getting busy after a certain period pre-defined in an internal OS timer and the event does not propagate down to window in time, the browser freezes. To illustrate what happens:

Normal operating flow:

Finger touch -> touch event -> preventDefault() with Gesture Manager -> propagation to all DOM layers -> window

HTC Androids:

Finger touch -> touch event -> preventDefault() with Gesture Manager -> propagating to all DOM layers -> FROZEN -> window

That said, we're still trying our best to find a possible workaround for this.

4 May 2011, 9:55 AM
Thanks Jackie...I really hope that you can work this out as HTC devices are almost 50% of the Android market and it's killing potential sales (and the Sencha platform's name) when having to tell client's that our Sencha driven apps won't work on HTC devices.

Is there any way I can get updated when progress is made on this issue? We have a few large apps in production that are definitely looking to us for a solution to be worked out.

Thanks for your response.

4 May 2011, 10:32 AM
very interesting Jacky! do you have any further reading on this?

7 May 2011, 2:21 AM
Interested in what?

12 Jun 2011, 1:22 PM
Any news on this issue?

We basically cannot use Sencha on HTC Androids.

Can we somehow use a simpler scrolling (e.g. without momentum) to minimize the chance of the freeze?

12 Jun 2011, 1:41 PM
I believe Sencha is working on a new scroller for Android devices that may be in Alpha...but as usual, no word on what it is and when it will be available.

We had to stop using Touch b/c of this issue, along with load time against other frameworks. Touch is too heavy from a size standpoint and the lack of proper loading indicator feature makes the load time issue even worse. Clients don't like staring at a blank white screen for 3-4 seconds.

Hopefully they address all these issues in 2.0 and we can look to move back to Touch as there are many thing about the framework that we really like.

12 Jun 2011, 11:42 PM
Yes, it kinda sad.
Now we have to make an extra simple/plain HTML app for HTC Androids - kinda defeats the purpose of Sencha Touch to have to develop more apps for different devices......

Anyways, for the loading issue you mention, we did a fairly simple fix for that, by simply setting a background image like this:
<body style="background: black url('phone_startup.jpg') no-repeat center center;">

Sencha Guys: Is there any "temporary fixes" for this, e.g. if we use PhoneGap?
I see in the logs, that the problem always starts with the WebKit javascript bridge making a "Do Pause" and then never resumes again.

Strange idea: Can we have a timer which just re-activates/resume the WebKit JB Bridge after e.g. 50 milliseconds?
Your thoughts/ideas are highly appreciated.

13 Jun 2011, 4:47 AM
We are already doing the background image element you mentioned and moving the JS to be inside the body tag...however, it still goes blank after initially loading while the DOM gets locked up for the Sencha viewport sizing calculations. This is NOT an optimal solution.

What I would love to see is an easy to use JSBuilder function that let's me build a minified version of only what is needed to load the main page of the app...ie. a viewport, panel, list, maybe a carousel. Then have that be the only Touch core pieces to load upon startup...but then have the rest immediately load after that...and if a function is called from a lib that has not loaded...simply queue it for execution and show a loading indicator.

That would be optimal in my opinion. Touch is going to have to do something to compete with the myriad of smaller, lighter frameworks out there that load much faster. In the end, that is all the client seems to care about. Again, can't say enough how much I love Sencha, ExtJS, and Touch...but these issues are deal breakers for us right now. Really hoping 2.0 addresses this.