January 19, 2011

Android + Phonegap: Scale the WebView to fit the device

I was porting an iPad app to Android for the new Samsung tablet, and I had some trouble getting my web view to scale to the size of the device screen so that I wouldn't have to resize any of my assets. Obviously this is a questionable tactic, but I was experimenting and wanted to see how it would look :)

Here's the meat of my main App.java class:
public class App extends DroidGap {
 
 // declare the original size of the iPad app
 protected float ORIG_APP_W = 768;
 protected float ORIG_APP_H = 1004;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.loadUrl("file:///android_asset/www/index.html");
        
     // set some defaults
     this.appView.setBackgroundColor(0x000000);
     this.appView.setHorizontalScrollBarEnabled(false);
     this.appView.setHorizontalScrollbarOverlay(false);
     this.appView.setVerticalScrollBarEnabled(false);
     this.appView.setVerticalScrollbarOverlay(false);
     
     // get actual screen size
     Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
     int width = display.getWidth(); 
     int height = display.getHeight(); 
     
     // calculate target scale (only dealing with portrait orientation)
     double globalScale = Math.ceil( ( width / ORIG_APP_W ) * 100 );
     
     // make sure we're all good
     Log.v( "ORIG_APP_W", " = " + ORIG_APP_W );
     Log.v( "ORIG_APP_H", " = " + ORIG_APP_H );
     Log.v( "width", " = " + width );
     Log.v( "this.appView.getMeasuredHeight()", " = " + height );
     Log.v( "globalScale", " = " + globalScale );
     Log.v( "this.appView.getScale()", "index=" + this.appView.getScale() );
    
     // set some defaults on the web view
     this.appView.getSettings().setBuiltInZoomControls( false );
     this.appView.getSettings().setSupportZoom( false );
     this.appView.getSettings().setGeolocationEnabled( true );
     this.appView.getSettings().setLightTouchEnabled( true );
     this.appView.getSettings().setRenderPriority( RenderPriority.HIGH );
     
     // set the scale
     this.appView.setInitialScale( (int)globalScale );
   }
}
I also updated the AndroidManifest.xml file to lock the app into portrait orientation, work on tablet-sized devices, have a nice app name, and give the device access to the Internet and geolocation:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.phonegap.testapp"
      android:versionCode="1"
      android:versionName="1.0">     
     
    <application android:icon="@drawable/icon" 
        android:label="@string/app_name"
        android:debuggable="true">
        <activity android:name=".App" 
                  android:label="Test App" 
                  android:configChanges="orientation|keyboardHidden"
                  android:noHistory="true" 
                  android:stateNotNeeded="true" 
                  android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <!-- allows access to phonegap hardware features -->
 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />-->
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> 
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />


   <supports-screens
     android:largeScreens="true"
     android:normalScreens="false"
     android:smallScreens="false"
     android:resizeable="true"
     android:anyDensity="true"
     />

</manifest> 
And finally my res/layout/main.xml file, though I'm not sure if this is different from the Phonegap default:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >    
            <WebView android:id="@+id/appView"
            android:layout_height="fill_parent"
            android:layout_width="fill_parent"
            /> 
</LinearLayout>
I hope this helps someone port their hybrid html5 iPad app to Android tablets.

21 comments:

  1. Thanks bro!!

    It will surely help, but one thing i need to ask is , why u need to withdraw support of small-screen and normal-screen and also lock orientation to portrait, aint andriod detetcs it(orientation) automatically.

    ReplyDelete
  2. I locked the orientation and targeted tablet-size devices for a specific project I was working on. Feel free to modify and take it in any direction you want! I was specifically sharing the ability to scale the web view to the device size, based on the original html app size. Hope this helps.

    ReplyDelete
  3. import android.util.Log;
    import android.view.Display;
    import android.webkit.WebSettings.RenderPriority;
    import android.view.WindowManager;
    import android.content.Context;

    you need to import these in your main App.java class.

    and add this meta element head element in index.html
    meta name="viewport" content="target-densitydpi=device-dpi"

    Thank you for this code.

    ReplyDelete
  4. Do you need to ask for all this permissions even if you are not using them?
    I mean, is this a requirement for PhoneGap to work?

    ReplyDelete
  5. You don't need all of them, but we needed them in our app.

    ReplyDelete
  6. First of all thanks for your great work.

    I use this for an application, but detect a problem when user focus on a text field, the webview makes zoom and destroy the page aspect.

    I propose this solution on another site, I hope it will be usefull for some.


    <"I found the answer!!!, at least for me. It works on HTC desire and Sansung 3 low res.

    Remove meta viewport en html

    in java apply solution in http://uihacker.blogspot.com/2011/01/android-phonegap-scale-webview-to-fit.html THANKS!!!!

    add

    this.appView.getSettings().setSupportZoom( true ); //Modify this this.appView.getSettings().setDefaultZoom(WebSettings.ZoomDensity.FAR);//Add this

    Remenber to test if WebSettings.ZoomDensity.FAR is available to your sdk version (7 and above).">

    Tanks again.

    ReplyDelete
  7. Thanks for the update Ricardo!

    ReplyDelete
  8. I suppose instead of android:configChanges="orientation|keyboardHidden"
    if we do android:configChanges="keyboardHidden"

    That itself avoids orientation

    ReplyDelete
  9. it is working .. but when i click on some link, then all get wrong .. where can be a problem?

    ReplyDelete
  10. thanx justin.
    I've tried your code but the scaling worked only for the index page.When I navigated to my home page,it was not scaled.Can u suggest some solution?

    ReplyDelete
  11. I only had this working for an Ajax site that never refreshed. And it honestly wasn't a great solution for what I needed to do, so I never followed through with really using it.

    ReplyDelete
  12. this worked for me: this.appView.setInitialScale(0);

    package com.exsyshost.android;

    import android.os.Bundle;
    import com.phonegap.*;

    public class App extends DroidGap {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setIntegerProperty("splashscreen", R.drawable.splash);

    super.loadUrl("file:///android_asset/www/index.html");
    this.appView.addJavascriptInterface(this, "android");
    this.appView.setInitialScale(0);

    this.appView.setBackgroundColor(0);

    }
    }

    ReplyDelete
  13. When testing a 800 x 440 sized app on a 480 x 320 screen, I got better results using:
    double globalScale = Math.floor( ( width / ORIG_APP_W ) * 100 );
    Instead of:
    double globalScale = Math.ceil( ( width / ORIG_APP_W ) * 100 );
    But the problem I still have is that the screen refocuses (sharpens) on a screen smaller or larger than the width of the original app when it receives a touch event. So on a 800 x 480 screen it will work fine, but not on a 480 x 320 screen. Does anyone know how to fix this? Btw I'm using landscape orientation.

    ReplyDelete
  14. Hello all.

    It´s works for my index.html, but no for others html documents.
    I open my documents in this way:





    How to manage it from Java ??

    Thanks !!

    ReplyDelete
  15. hi
    Friend i need to play the html file in webview but i can`t manage this
    b`cause of this size how enable the all script in this.
    have you any idea plz say me thank in Advance

    ReplyDelete
  16. My WebView is zooming on input field focus. Any suggestions?

    ReplyDelete
  17. Was anyone capable of disable that zoom when the input got focus? I've tried many ways, but got 0 positive results.

    Thanks in advance!

    ReplyDelete
  18. have you managed to accomplish that? i got the same issue, and it's driving me insane.

    ReplyDelete
  19. when i select an input box (brings up the keyboard) the webview zooms the input and then its stuck zoomed in at that point..n when it go to other documents it resizes.. please some1 help me please :( :(

    ReplyDelete