16

I have an application with just a visible WebView component to it which is used to display some dynamically generated HTML (can run Javascript too). It's enabled for the WebView.

For a few pages I am trying to set the focus of one of the input text boxes after the page load has finished - not through Body onLoad(), but instead by a JS call after the page has finished loading i.e. in onPageFinished(). Most of the javascript executes fine

    webview.loadURL("javascript:JSMethod();");

But when I use the same call to do a document.getElementById('text1').focus() - the cursor does reach the element but the Soft Keyboard won't pop out. The same javascript code when executed from a button on the page does have the desired effect.

I am attaching the source code which has 3 buttons

  1. Focus Text1 - Put the cursor on the text1 and pops up the Softkeyboard.
  2. Java Focus Text1 - Calls Java to execute the same JS. Only shows the cursor there and doesn't pop out the keyboard
  3. Java Focus Text1 And Keyboard Open - Calls JS from Java and Forces Keyboard open.

My understanding is that the JS execution should work the same whether executed from the browser using a button/event or as sent from Java through WebView.loadURL().

Here's my Queries

  1. Am I missing something when using Button#2? That's how my current code is and I only see the cursor is set but the SoftKeyboard won't open.
  2. When I use logic as written in Button#3, I some times don't see the cursor on the field but gives me the desired effect and the cursor becomes visible when I type something in the popped up keyboard.
  3. Could the behavior I see in Button#2 be a bug in Android JS execution? Or could it be that the WebView doesn't have focus that's why only the cursor is displayed? I also tried webview.requestFocus() - I can't write requestFocusOnTouch() as it's the only View I have and am expecting it's focused automatically.

The Java code which demos the behavior is

    import android.app.Activity;
    import android.content.Context;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.inputmethod.InputMethodManager;
    import android.webkit.JsResult;
    import android.webkit.WebChromeClient;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;

    public class WebViewProjectTestJSHTMLFocusActivity extends Activity {

        Activity _current = null;
        WebView wv = null;

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            _current = this;
            //setContentView(R.layout.main);
            String data = "<html><body>" +
                                "<script>function focusser() { document.getElementById(\"text1\").focus(); } </script>" +
                                "<script>function javaFocusser() { javautil.javaFocus(false); } </script>" +
                                "<script>function javaFocusserKeyboard() { javautil.javaFocus(true); } </script>" +
                                "Text 1<input type='text' id='text1'/><br/>" +
                                "Text 2<input type='text' id='text2'/><br/>" +
                                "<input type='button' value='Focus Text1' onClick='focusser()'/>" +
                                "<input type='button' value='Java Focus Text1' onClick='javaFocusser()'/>" +
                                "<input type='button' value='Java Focus Text1 And Keyboard Open' onClick='javaFocusserKeyboard()'/>" +
                            "</body></html>";
            wv = new WebView(this);
            wv.getSettings().setJavaScriptEnabled(true);

            // Set some HTML 
            wv.loadDataWithBaseURL("file:///android_asset/", data, "text/html", "UTF-8", null);

            // Call back required after page load finished
            wv.setWebViewClient(new CustomWebViewClient());

            // Enable Alert calls  
            wv.setWebChromeClient(new CustomWebChromeClient());

            // For JS->Java calls
            wv.addJavascriptInterface(this, "javautil");

            setContentView(wv);
        }


        /**
         * Calls the same javascript and forces the keyboard to open 
         */
        public void javaFocus(final boolean shouldForceOpenKeyboard) {

            Thread t = new Thread("Java focusser thread") {
                public void run() {
                    wv.loadUrl("javascript:focusser();");
                    if(shouldForceOpenKeyboard) {
                        InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                        mgr.showSoftInput(wv, InputMethodManager.SHOW_IMPLICIT);
                    }
                }
            };

            // Run on the View Thread.
            _current.runOnUiThread(t);
        }

        /**
         * Calls focus method after the page load is complete.
         */
        final class CustomWebViewClient
        extends WebViewClient {
            @Override
            public void onPageFinished(WebView view, String url) {
                // javaFocus(true);
                Log.d("TestExamples", "focusser call complete");
            }
        }

        final class CustomWebChromeClient
        extends WebChromeClient {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Log.d("TestExamples", "JS Alert :: " + message);
                return false;
            }
        }
    }

Solution Update 24-06-2011 To make this work, you need to use wv.requestFocus(View.FOCUS_DOWN) just before the actual JS focus call. I modified the javaFocus() method above to the correct version below. Earlier when I mentioned that I was using requestFocus(), I was using that when the WebView was initialized in the method onCreate(). The primary difference is now we're forcing the WebView to get focus each time just before the Javascript document.getElementById("text1").focus(); is executed.

public void javaFocus(final boolean shouldForceOpenKeyboard) {
    Thread t = new Thread("Java focusser thread") {
        public void run() {
            wv.requestFocus(View.FOCUS_DOWN);
            wv.loadUrl("javascript:focusser();");
            if(shouldForceOpenKeyboard) {
                InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                mgr.showSoftInput(wv, InputMethodManager.SHOW_IMPLICIT);
            }
        }
    };

    // Run on the View Thread.
    _current.runOnUiThread(t);
}

Also to ensure that this issue wasn't fixed because of focus triggered through touch etc, I am using a background thread to initiate the javaFocus() after 5 seconds of WebView Displayed. The modified onCreate() is below.

..... More onCreate code before....
// Enable Alert calls  
wv.setWebChromeClient(new CustomWebChromeClient());

// For JS->Java calls
wv.addJavascriptInterface(this, "javautil");

setContentView(wv);

new Thread("After sometime Focus") {
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        javaFocus(true);
    }
}.start();
.... onCreate() ends after this.... 
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Swaroop
  • 908
  • 1
  • 12
  • 25
  • Thanks for the thorough explanation. I had the same problem but this solution did not work for me on Android 3.2. In the end I had to write a function that simulated an actual tap on the field. I don't know why your solution didn't work. The WebView is just chock full of quirks. – Pre101 Feb 20 '12 at 05:49
  • I didn't test this code in Android 3.x/4.x. But did you try to run this sample program in either of them? – Swaroop May 21 '12 at 02:46
  • I'd love to know if you can use this approach to make sure that a type=number input actually gets a numeric keyboard. As far as I am getting, InputMethodManager can only open the standard keyboard layout (Qwerty) since it doesn't know which HTML element currently has focus. Using wv.requestFocus didn't help. – Wytze Jun 29 '12 at 20:01

2 Answers2

10

It could be that the webview doesn't have the application focus. Try executing;

wv.requestFocus(View.FOCUS_DOWN);
Jivings
  • 22,834
  • 6
  • 60
  • 101
  • 2
    This actually worked. The trick is to use `wv.requestFocus(View.FOCUS_DOWN);` right before requesting for Focus. Modifying the question so that the code is well formatted. – Swaroop Jun 24 '11 at 10:46
  • anyone knows dose the answer wv.requestFocus(View.FOCUS_DOWN) work on ios? Thanks – star Aug 30 '17 at 12:40
1

Here is the complete solution.

protected void onCreate(Bundle savedInstanceState) {

    // setfocus on page load finished
    myWebView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(myWebView, url);
            myWebView.requestFocus(View.FOCUS_DOWN);
            Toast.makeText(getApplicationContext(), "Done!", Toast.LENGTH_SHORT).show();
        }
    });
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103