Monday, April 30, 2012

PhoneGap Android SplashScreen Just Got Better

One PhoneGap 1.7.0 is released in the next day or so doing a splash screen on PhoneGap Android is going to get a lot better. First off the long standing issue of using 9-patch images has been fixed. Really and truly it has this time. I've tested the fix on three different screen sizes.

Secondly, when you setup a splash screen you do the following:



super.setIntegerProperty("splashscreen", R.drawable.splash);
super.loadUrl("file:///android_asset/www/index.html", 10000);
This would show your splash screen 10 seconds then your index.html page would be loaded. Notice I said then. Yup, that's right showing the splash screen is a blocking call. While the splash screen is being displayed your .html/.js/.css is not being loaded in the background. Well, that is all changing so that your splash screen is shown on one thread while your application loads underneath the splash screen in another thread. The best thing is you don't need to make any changes to your code. Just keep calling the splash screen like you normally would.

But wait, I'm anticipating your next question "What if I want the splash screen to go away once my application is loaded before the timeout value is reached?" Okay, I've got good news it's coming. The official way to do this on iOS and Android once the 1.8.0 release is out will be to call:
navigator.splashscreen.hide();
but that call isn't ready yet. However, if you want to be on the bleeding edge you should be able to call:
cordova.exec(null, null, "SplashScreen", "hide", []);
but you are wait out on the edge there so don't blame me if it doesn't work. Then you'll be able to tell the splash screen to go away once you have "deviceready".
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {
    console.log("We got device ready");
    cordova.exec(null, null, "SplashScreen", "hide", []);
    // Soon to be
    // navigator.splashscreen.hide();
}
That's all for now.

78 comments:

jax said...

Nice! thanks for this.

jax said...

Ps. you might want to change:

showSplashScreen(R.drawable.splash, 10000);
super.loadUrl("file:///android_asset/www/index.html")

into this

super.setIntegerProperty("splashscreen",R.drawable.splash);
super.loadUrl("file:///android_asset/www/index.html",10000)

Unless the next 1.7 release adds a showsplashscreen method of course. :-) (the 1.7.0rc1 doesn't have it in any case)

Simon MacDonald said...

@jax

No, you are so right! I copied and pasted the wrong two lines into the blog post. I was helping a guy back port this to a 1.6 application. I'm going to fix the blog post right now!

אבידן חן said...

Thank you so much for this fix! I've been waiting a long time for this...

Perhaps you could shed some light as to what are the resolutions my splash screen needs to be in order to support all of the device densities available. I tried using an 800*480 image in the drawable-hdpi folder, but on phones which have a resolution of 854*480, there's a white bar at the bottom (since the aspect ratio is different). I would also like to support phones such as Galaxy Nexus which has a 1280*720 resolution and phones like the Galaxy Gio which has a resolution of 480*320.

So I'm not really sure which resolutions my splash screen needs to be, in order to look good on each and every phone.

Simon MacDonald said...

n order to have a splash screen in a PhoneGap Android application you need to put your splash.png file into res/drawable-ldpi, res/drawable-mdpi, res/drawable-hdpi, res/drawable-xdpi. Where those directories represent low, medium, high and extra large dots per inch. You'll need to resize you slash.png for each directory or Android will stretch it for you.

The sizes of each image should be:

* xlarge: at least 960 x 720
* large: at least 640 x 480
* medium: at least 470 x 320
* small: at least 426 x 320

אבידן חן said...

Thanks a lot for your help and your quick reply!

Martina said...

Hi,

I just rebuilt my application with phonegap 1.7.0 and my splash screen image doesn't work on iOS anymore. It works on android. It's all set up the same way it was, the image is specified in config.xml and it worked fine on both iOS and android before.
Could you please give me some advice?
Do I need to use splash screen in a different way now?

Btw. I'm not using xCode only javascript and html, so this article didn't help me with this (unless the code is usable within javascript), but I thought you might know the answer.

Simon MacDonald said...

@Martina

I didn't touch the iOS splash screen code. You'll have to come over to the PhoneGap Google Group and ask your question.

Mike said...

I had that issue with the splash screens, it had to do with changes to how config works with splash screens and how you set the device type per splash screen. you may want to google the config updates once updates that everything worked fine, again this was using build. hope that helps

Simon MacDonald said...

@Mike

Can you give me more details? I'm not exactly sure what you are saying in your comment.

themightychris said...

I've been looking forward to the 1.8 release since reading this comment but I wasn't able to find anything about splash screens in the release notes. Could you update your post to indicate the status of the recommended syntax in 1.8?

Simon MacDonald said...

@themightychris

It's exactly as I say in the post, now that 1.8.0 is out you can call navigator.splashscreen.hide() to make the splash screen go away. Best to do that in your "deviceready" event handler.

JK said...

Do you have any clue as to when certain properties, like the duration the splash screen is visible, can be set in the config.xml for the phonegap build service?

Simon MacDonald said...

@JK

I have no control over what happens on PhoneGap build as that is all Adobe. Although we are trying to push items into config.xml for 2.0 so hopefully soon.

JK said...

Thanks Simon, for your feedback. Two questions come to mind: 1. I can find some roadmaps for Phonegap regarding 1.7 and 1.8, but do you have any insight as to when 2.0 is planned to be released? 2. I implemented your solution mentioned above and it works flawless. However, is there a way to set some minimum duration the splash screen is displayed? My app loads to fast now, which obviously is a luxury problem :-))

Simon MacDonald said...

@JK

1) 2.0 will be released July 20th on PhoneGap Day in Portland, OR. https://wiki.apache.org/cordova/RoadmapProjects
2) That is not necessarily a bad thing, perhaps you don't need a splash screen in that case. Although if you want to keep it around for a bit just add a setTimeout() wrapper around your call to hide the splash screen.

Lechuza said...

If I'm going to use the method : navigator.splashscreen.hide() to hide the splash screen, I need to add also the timeout value in the loadIrl method?
Which ones will have precedence?

Simon MacDonald said...

@Lechuza

navigator.splashscreen.hide() has precedence over the timeout in the loadUrl method. That way you can specify a long timeout in loadUrl but close it earlier using hide once you get a device ready event.

SK said...

When I used the dimensions you provided, the splash image is distorted on my android phone. It seems weird that the images are so wide and not tall. I tried flipping the dimensions but they are not tall enough for my phone now.

Simon MacDonald said...

@SK

Those dimensions are just suggestions from the Android site. Since there is so many different screen sizes on Android it is near impossible to account for them all. That is why Android supports 9 patch images which are scaled along the lines that you specify. They are the best solution for splash screens.

Junfeng Zeng said...

I use phonegap 1.9.0 ,when i call navigator.splashscreen.hide(); in onDeviceReady() , the log said ” 07-15 12:05:50.589: I/Web Console(3213): Error: SyntaxError: Unexpected token ‘ at file:///android_asset/www/cordova-1.9.0.js:1012



Is a bug in phonegap 1.9.0 ?

Simon MacDonald said...

@Junfeng Zeng

I have never seen that before. I sounds like there is a bad character in your .js file.

Junfeng Zeng said...

thank you for your reply. I forget add " " into the "plugin.xml" . Now it works,but has some problem.It has 2 seconds black interval when the splashscreen has gone.I try setTimeout(navigator.splashscreen.hide(),3000); ,but this cann't make things better.

Simon MacDonald said...

@Junfeng Zeng

Sounds like you need to check into the way your HTML is displayed. If you are listening to deviceready before calling hide() it should happen after the on body load has completed.

Tre Stange said...

Hi guys,

I'm using Dreamweaver CS6 & PhoneGap Build [which now uses 1.9 to package the app] to generate my iOS and Android App. I am having the screen go black after a couple of seconds on the Android platform. I want to keep the splash screen up on the Android version but I'm not sure how the code you're showing works in conjunction with PhoneGap Build and Dw.

Specifically this:
super.setIntegerProperty("splashscreen",R.drawable.splash);
super.loadUrl("file:///android_asset/www/index.html",10000)

The config.xml deals with the splashpages, I'm wondering if I should remove that in the config for the android version and try what you're doing here...?

I've no www folder that house my files, so do I reference the file path as the root:
super.loadUrl(file:///index.html",10000)

I"m sorry if all this seems redundant - I know 2.0 is now out, but the build service isn't using it yet and I'm not sure you'll have those new things you mention added to the config.xml for version 2.0, so any help or thoughts would be extremely appreciated.

Simon MacDonald said...

@Tre

I have nothing to do with the Build service so I can't help you there. You may want to try hitting up @alunny on twitter.

Steren said...

Hi, thanks a lot for this article. I was able to set up my splashscreen on Phonegap 2.0.0.

Unfortunately, I don't find the user experience perfect: When the app opens, for one or two seconds, I can see an empty black application and a topbar with the name of my app. It's only after these seconds that the splashscreen is displayed, and after the splashscreen that my app is displayed.

Would it be possible to display the splashscreen immediately at startup ? and skip the empty application screen ?
To my mind, a splashscreen is the first thing that is loaded, we shoudln't see anything before.
What do you think ?

Simon MacDonald said...

@Steren

That is a tough one as Android doesn't really provide a splash screen class. We have to fake up our own. The app has to get loaded in order for us to show the Dialog that is our splash screen. I'm not sure how to fix it right now but if you open an enhancement on JIRA I'll look into it.

https://issues.apache.org/jira/browse/CB

unimity said...

can we display the splash screen for 5secs instead of 10secs?

unimity said...

Black screen is displayed before the splash screen in phonegap 2

Simon MacDonald said...

@unimity

Yes, and issue has been filed for the titlebar issue on startup.

https://issues.apache.org/jira/browse/CB-1126

If you only want to see the splash for 5 seconds pass 5000 as the second parameter to loadUrl.

Lic. Mariano Salvetti said...

Hi Simon,

I'm using PhoneGap 1.2 version with the Salesforce Mobile SDK. I need to create
a SplashScreen when my Android App is starting. But I have the problem, that I
don't can show the SplashScreen during some seconds,there is no way to show the
splash screen. Both the following lines don't work:
super.setIntegerProperty("splashscreen", R.drawable.splash);
super.loadUrl(BOOTSTRAP_START_PAGE, 9000); // 09 seg

My App CRASH if I use this ---> super.loadUrl(BOOTSTRAP_START_PAGE, 9000); // 09
seg
And If I use this ---> super.loadUrl(BOOTSTRAP_START_PAGE); // use default
the App run great, but the SplashScren is shows only 3 seconds, and next the app
show the white screen.

Can you help me? I need to implements my SplashScreen plugin? Can I use the
source code of the Phonegap 1.8+ ? One of the problems is that I can't change
the Phonegap 1.2 version, because I'm using the Salesforce Mobile SDK.

Thanks for your time and help,
Best Regards,
Mariano Salvetti, from Rosario - Argentina.

unimity said...

when i use
super.setIntegerProperty("splashscreen", R.drawable.splash);
super.loadUrl("file:///android_asset/www/index.html", 10000);

these lines for splash screen, it works on my android4.0.3 emulator but getting as "unfortunately app has stopped" and exits the app in android4.0.3 device.

kctang said...

Excellent stuff!! My Android app "feels" so much faster now! :D

In case anyone was wondering to how make this "load index.html, js, css in the background" to work on iPhone/XCode... check out this blog entry:

http://shazronatadobe.wordpress.com/2011/09/15/ios-phonegap-splash-screen-control/

Now the apps feels right on both platforms. Posting here just in case someone needs it.

Simon MacDonald said...

@unimity

What does the stack trace look like in "adb logcat"?

Walid Fekkak said...

hey it s nice like solution , but me i have the same problem but i can resolve it with this truk ,with my app phonegap i use jquery mobile , not java and any cordova ? how can u help me to resolv it ?

Simon MacDonald said...

@Walid Fekkak

Dude it is just 2 lines in Java.

Simon MacDonald said...

@Lic. Mariano Salvetti

What does the crash look like in "adb logcat"?

Ariona, Rian said...

Hi simon, this is work for me, another question is "how to add transition effect when closing the splash screen" ?

Simon MacDonald said...

@Ariona, Rian

It's not possible right now unless you recompile the PhoneGap source code. YOu can do something like this:

http://stackoverflow.com/questions/4817014/animate-a-custom-dialog/5591827#5591827

Actually, add an enhancement request on JIRA.

https://issues.apache.org/jira/browse/CB

Fabrizio Bottino said...

Thank you for your work!

I opened a new request for dual splaschscreen (landscape and portrait):
https://issues.apache.org/jira/browse/CB-1263

Is there a way to have a splashscreen centered and a background color, instead of having it stretched to fit display size?

Fabrizio Bottino said...
This comment has been removed by a blog administrator.
Simon MacDonald said...

@Fabrizio Bottino

Yes, I just saw that and I answered it as a won't fix. The proper way to do what you want is to use a 9-patch image.

https://developer.android.com/tools/help/draw9patch.html

Fabrizio Bottino said...

@Simon

unluckily the 9 patch image won't work in my case: I absolutely don't want the center area to be stretched!

I simply need to show a logo centered in the page, but the logo must not pixelate by scaling.

Any other way to show a splash screen centered with a backgroudn color?

Possibly not in an HTML way, I need to keep the project HTML source cross device (online pages used from both iOS and android)

Simon MacDonald said...

@Fabrizio Bottino

Yes, and that is exactly what a 9-patch is as you can specify a non-stretchable area. Typically the center where your logo would be.

https://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch

Did you really think I would steer you wrong? ;)

Kenneth said...

Hello Simon.

I've tried and tried to get this to work, but I'm still experiencing major problems getting it to do as it's supposed to. I'm trying to use navigator.splashscreen.hide(); with PhoneGap 2.0 inside a call to DeviceReady. Isn't Phonegap supposed to be 100% ready when DeviceReady is called? My call looks like this:

function onLoad() {
document.addEventListener("deviceready", onDeviceReady(), true);
};
function onDeviceReady() {
navigator.splashscreen.hide();
}

with a call to onLoad in the < body onLoad="OnLoad()" >. When it's called it gives me: Result of expression 'navigator.splashscreen' [undefined] is not an object.

I've tried to simply call navigator.splashscreen.hide(); inside a function on a button on my page, and this seems to be working? (Indicating that the problem might be that the object simply isn't loading when the call to DeviceReady happens?). This means that my splashscreen is showing for the entire timer duration, and not being cancelled by the navigator command.

My config.xml file is loading the following plugins:






Any suggestions?

Simon MacDonald said...

@Kenneth

The reason you are seeing this error is that you are calling the onDeviceReady method too early. You see when you have "onDeviceReady()" it will execute immediately since you have the (). Your event listener line should be written:

document.addEventListener("deviceready", onDeviceReady, true);

Kenneth said...

Thanks Simon! That did the trick! :)

gudluri siva said...

Hi Simon,

I followed your doc splash screen, but I didn't see that image while loading url.

Do i need configure any where.

Thank you in advance.

Simon MacDonald said...

@gudluri siva

That's not enough info for me to help you out.

venes said...

Hi,

Simon,

From India.

I need to resolve black screen in Android phonegap.First splash screen coming after that back screen than content loading.
My Code :
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

super.loadUrl("file:///android_asset/www/book3/index.html");

Thank you

Simon MacDonald said...

@venes

Nowhere in the code you posted do you setup the splash screen to be shown. You need to set the image and add a timeout value to loadUrl.

Sixto said...

To remove the black screen after splash, i use:

@Override
public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

//add splash
super.setIntegerProperty("splashscreen",R.drawable.splash);

//to remove the black screen after splash
root.setBackgroundColor(0);
root.setBackgroundResource(R.drawable.splash);
super.loadUrl("file:///android_asset/www/index.html", 15000); //set timeout to 15 seg



}

Simon MacDonald said...

@Sixto

That is one way of doing it but you are probably better off making your loading timeout longer and then using navigator.splashscreen.hide() in your device ready event handler to make it go away.

pcasa.net said...

Hi Simon,
I've tried it with Cordova 2.1 and it doesn't work apparently.
- using the 9-patch image directly, the splashscreen is stretched (and not scaled as 9-patch - the image is splashimg.9.png, correctly defined)
- using an xml defining the 9-patch (with ) the app crashes (resource not found exception - very strange).

Kind regards
Paolo

pcasa.net said...

Hi Simon,
please forget my last question. The splashscreen works both with direct image reference and xml resource file, in cordova 2.1. I define the scalable area only; however defining the fill area too causes app crash for some reason. The image is a classic centered logo with borders to be expanded.
Thank you ;-)

nicoprofe said...

Hi Simon, which is the best way to fit all my images in all kind of device? Of course I am talking about the different screens resolution, density, etc. With out changing the images resolution.
Could phonegap resize it for me?
Thanks!

Simon MacDonald said...

@nicoprofe

Well you could try setting the viewport so that the screen size is the same no matter what the actual resolution is:

https://developer.android.com/guide/webapps/targeting.html#Metadata

A better way would be to have multiple images with different resolutions that are displayed based on a CSS media query.

kamalesh kumar Chauhan said...

Hi Simon,

Nice post, worked for me. Do u know how to add animated image (.gif)as splash screen

Simon MacDonald said...

@kamalesh kumar Chauhan

To the best of my knowledge animated gif's are not supported on Android. If you want to do an animated splash screen you will need to do some Java coding. Check out this link:

http://www.codeproject.com/Articles/113831/An-Advanced-Splash-Screen-for-Android-App

Abulafia said...

Hi Simon,

I love your blog. Here is my problem:
I am using Cordova 2.2.0,

my onCreate look like:

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setIntegerProperty("splashscreen", R.drawable.splash);
super.loadUrl(getWebMainFilePath(),3000);
}
and

function onDeviceReady() {
navigator.splashscreen.hide();
}

When I start the App the splash screen showsup for 3sec. after that I get a blackscreen. With cordova 1.9.3 (same code) it works fine.

Do you have a idea?

thx

Simon MacDonald said...

@Abulafia

Sounds like you need to wait a bit longer before hiding the splash screen as things are taking longer to setup.

Abulafia said...

Hi Simon,

thank you for your answer. I increased the time to 60000, but no change. It looks like that navigator.splashscreen.hide();
is not working. Any alternatives to hide the splash screen. What is the best point too hide the screen?

thx

Simon MacDonald said...

@Abulafia

Well once you get the deviceready event is usually the best time to hide the splashscreen. If the splashscreen is going away after 3 seconds then deviceready must be getting reached. However, it doesn't sound like your html is drawn yet. Try putting the call to hide the splashscreen in a setTimeout to delay it a bit.

Andrew said...

Simon,

I hope this topic isn't too old because you seem to be the expert on the subject...

I got basic Android splash screen function going in no time at all by following your instructions. That includes hiding it after onLoad(). Really nice. The problem I have is that even after carefully making 9-patch images in all four sizes, I am still seeing a small, but noticeable stretch of the image. I've taken a screen shot, and measured the result in GIMP, and there is a vertical elongation of 30%. I've only done the screen shot on one device, but visually, the effect appears the same on three other devices.

I've tried everything I can find written about this issue, and nothing seems to make a difference.

+ My 9-patch images have only a very small center area that is not stretchable. The draw9patch utility has a preview pane, and everything looks good there, which seems to indicate I have created the patches correctly. (Before defining the patches, the images are badly distorted in the preview pane.)

+ I have also tried following all variations of the answers in this thread.

(I notice you posted a comment there, but it is old, short, and I couldn't figure it out.)

Do you have any ideas for fixing or trouble-shooting my problem? Since the distortion appears to at least be consistent on different devices, my fallback is going to be to create original .png files that are horizontally distorted to compensate. But that feels like an ugly solution, potentially prone to failure on some device I haven't tested.

Andrew said...

Ouch, this is really embarrassing, but the problem turned out to be the source files themselves. I did not create them, they were given to me, so I mistakenly assumed that they were correct. (Actually, two of the sizes were correct, but two were not.) The problem was small enough that I must have overlooked it while using the draw9patch utility. And since I was focused on learning to use the tool, it was easy to miss anything subtle in the graphics.

I hope you will accept my apologies for bothering you with an off-base question.

Someone among you.. said...

Well for me 9-patch is not working at all
i tried everything...
it doesn't get 9-streched in any way just streching the whole image

I use this
super.setIntegerProperty("splashscreen",R.drawable.splash);
super.loadUrl("file:///android_asset/www/index.html",10000)


i used the splash.xml also and:




in where i reference the splashimg.9.png

also doesn't work it streches the whole image.. :(


i have made the png with draw9patch tool from sdk

i have placed and tried both 320 x 470 and 470 x 320 for mdbi resolution they both strech in different direction.!

i have also tried to fill the whole right and lower border with black in draw9patch
no effect..
in draw9patch tool my pngs show perfect.. in the app nothing ...

pls help..

Someone among you.. said...

i also forgot to mention that i am using phonegap 2.5 and 2.7

Simon MacDonald said...

@Someone among you

I think you are running into a bug which was fixed by this commit:

https://git-wip-us.apache.org/repos/asf?p=cordova-android.git;a=commit;h=dbeb252feec836539c84b53030b197b33e2f915d

jayesh patil said...

hello, Please Can anyone help me out
i have been stuck for more than 2 days.

when i Use this code to hide splashscreen it give me error

code:-

setTimeout(function() {
navigator.splashscreen.hide();
}, 5000);


error:-

TypeError: navigator.splashscreen is undefined

Simon MacDonald said...

@jayesh patil

Are you making sure you are waiting for the deviceready event before you call hide?

jayesh patil said...

yup i have mention this code in ondeviceready section.

then too show same error !
Even if i try with device.uuid it gives me device is undefined.

m not getting whats the problem is !

Simon MacDonald said...

@jayesh patil

If device is not defined then PhoneGap is not setup correctly. Alternatively if you are using PhoneGap 3.0 did you add the SplashScreen and Device plugins?

jayesh patil said...

Hey i am building my application on phonegap 2.5.0 version !!

I have also mentioned



In config.xml. Still not working fine....

this my code written in application.js

var app = {
views: {},
models: {},
routers: {},
utils: {},
adapters: {},

// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
// document.addEventListener('splashscreen',this.splashhide,false);
document.addEventListener('backbutton', this.backKeyDown, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicity call 'app.receivedEvent(...);'
onDeviceReady: function() {
// console.log("onDeviceReady");
//app.receivedEvent('deviceready');
var cid = navigator.userAgent;
console.log(cid);

ga('create', 'UA-42747220-1', {
'storage': 'none',
'clientId': cid
});

app.router = new app.routers.AppRouter();
app.utils.templates.load(
[
"homeview",
"aboutview",
"mapview",
"bloodbankhomeview",
"bloodbanklistitemview",
"bloodbankview",
"bloodbanksearchview",
"drugview",
"drugdetailsview",
"druglistitemview",
"hospitalsearchview",
"hospitallistitemview",
"hospitalview",
"hospitalhomeview",
"commingsoon"],
function () {
app.router = new app.routers.AppRouter();
Backbone.history.start();

});
setTimeout(function() {
navigator.splashscreen.hide();
}, 5000);
// console.log(device);

},
backKeyDown: function() {
console.log("going back.")
},

};

$(document).ready(function() {
app.onDeviceReady();
});


Simon MacDonald said...

@jayesh patil

Whatever you posted as part of the config file did not show up as it was in brackets.

Sandeep Taneja said...

Hi Smon, Not sure if i get it right. I want to keep the splash screen shown until the HTML page is loaded. Right now what happens is that
- It shows a white screen for split second.
- then it shows the splash scren or 2 seconds.
- then I see black screen for 2-4 seconds and then my app loads.

Last step his happening as my application (HTML) is loading and when LoadURL is called, splash screen is of.

Any suggestions

Simon MacDonald said...

@Sandeep Taneja

Well some of this depends on what version of PhoneGap you are using. As well what timeout value are you supplying to loadUrl and do you call splashscreen.hide() once you get a "deviceready" event?

Stratos Nikolaidis said...

Amazing! So simple and soooooo undocumented!

Thanks!