WebView ukrywa miękką klawiaturę podczas loadurl(), co oznacza, że klawiatura nie może pozostać otwarta podczas wywoływania javascript

Ponieważ sposób, w jaki wywołujesz javascript w WebView jest przez loadUrl ("javascript:... "); Klawiatura nie może pozostać otwarta.

Metoda loadUrl() wywołuje loadUrlImpl(), która wywołuje metodę clearHelpers(), która następnie wywołuje clearTextEntry(), która następnie wywołuje hideSoftKeyboard() i wtedy stajemy się tak samotni, jak klawiatura odchodzi.

Z tego co widzę wszystkie są prywatne i nie mogą być overridden.

Czy ktoś znalazł rozwiązanie tego problemu? Czy istnieje sposób, aby zmusić klawiaturę do pozostania otwartą lub aby wywołać javascript bezpośrednio bez przechodzenia przez loadurl ()?

Czy istnieje i tak nadpisanie widoku sieci Web w sposób uniemożliwiający wywołanie (prywatnej metody) metody clearTextEntry ()?

Author: cottonBallPaws, 2012-02-16

3 answers

Update

KitKat dodał publiczną metodę bezpośredniego wywoływania javascript: evaluateJavascript()

Dla starszych API, można spróbować rozwiązania jak poniżej, ale gdybym miał to zrobić jeszcze raz spojrzę na tylko budowanie metody kompatybilności, że na KitKat używa powyższej metody i na starszych urządzeniach, używa refleksji do drążenia w dół do wewnętrznej prywatnej metody: BrowserFrame.stringByEvaluatingJavaScriptFromstring()

Wtedy możesz zadzwonić javascript bezpośrednio bez konieczności radzenia sobie z loadurlem i dodawania "javascript: " do skryptu.

Stara ODPOWIEDŹ

Zgodnie z życzeniem Aloka Kulkarniego, podam przybliżony przegląd możliwego obejścia, które wymyśliłem. Nie próbowałem, ale teoretycznie powinno zadziałać. Ten kod będzie szorstki i ma służyć jako przykład.

Zamiast wysyłać wywołania przez loadurl (), ustawiasz w kolejce swoje wywołania javascript, a następnie javascript je ściąga. Coś jak:

private final Object LOCK = new Object();
private StringBuilder mPendingJS;

public void execJS(String js) {
    synchronized(LOCK) {
        if (mPendingJS == null) {
            mPendingJS = new StringBuilder();
            mPendingJS.append("javascript: ");
        }
        mPendingJS
            .append(js)
            .append("; ");
    }
}

Zamiast wywoływać loadurl() wywołuje tę metodę. (Aby to uprościć użyłem zsynchronizowanego bloku, ale może to być lepiej dostosowane do innej trasy. Ponieważ javascript działa na własnym wątku, będzie to musiało być bezpieczne w jakiś sposób).

Wtedy twój WebView będzie miał taki interfejs:

public class JSInterface {

    public String getPendingJS() {
        synchronized(LOCK) {
            String pendingCommands = mPendingJS.toString();
            mPendingJS.setLength(0);
            mPendingJS.append("javascript: ");
            return pendingCommands;
        }
    }

}

To zwraca ciąg znaków z oczekującymi poleceniami i czyści je, aby nie były zwracane ponownie.

Dodałbyś go do WebView tak:

mWebView.addJavascriptInterface(new JSInterface(), "JSInterface");

Następnie w Twoim javascript ustawisz interwał, w którym spłukasz oczekujące polecenia. W każdym interwale wywoła JSInterface.getPendingJS(), które zwraca ciąg wszystkich oczekujących poleceń, a następnie można je wykonać.

Możesz to jeszcze poprawić, dodając sprawdzanie w metodzie execJS, aby sprawdzić, czy pole EditText istnieje w widoku sieci Web i jest w centrum uwagi. Jeśli istnieje, to można użyć tej metody kolejkowania, ale jeśli nie było jeden w centrum uwagi wtedy można po prostu wywołać loadUrl() jak normalne. W ten sposób wykorzystuje to obejście tylko wtedy, gdy faktycznie tego potrzebuje.

 12
Author: cottonBallPaws,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-07 17:10:03

Jeśli chodzi o starsze API (pre 19), zastosowałem podobną metodę do wyłączonej odpowiedzi, ale nieco inną.

Po pierwsze, śledzę, czy klawiatura jest wyświetlana za pomocą javascript w webview mniej więcej tak:

document.addEventListener( "focus", function(e){        
    var el = e.target;
    reportKeyboardDisplayedToJava( isInputElement( el ) );
}, true);

document.addEventListener( "blur", function(e){        
    reportKeyboardDisplayedToJava( false );
}, true);

Jeśli klawiatura jest wyświetlana, a warstwa Java Androida próbuje wykonać wstrzyknięcie js- "odraczam" to wstrzyknięcie. Dodaję go do listy ciągów, pozwalam użytkownikowi dokończyć wprowadzanie, a po zniknięciu klawiatury wykrywam to i wykonaj zaległości zastrzyków.

 0
Author: BuvinJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 15:22:56

Mogłem zaimplementować pomysł cottonBallPaws, aby korzystać z wewnętrznych WebView z odbiciem, i dostałem go do pracy dla mojego urządzenia 4.2. Istnieją łaskawe zamienniki dla wersji Androida starszych niż KitKat.

Kod jest napisany w Xamarin, ale powinien być łatwo przystosowany do natywnego kodu Javy.

/// <summary>
/// Executes a JavaScript on an Android WebView. This method offers fallbacks for older
/// Android versions, to avoid closing of the soft keyboard when executing JavaScript.
/// </summary>
/// <param name="webView">The WebView to run the JavaScript.</param>
/// <param name="script">The JavaScript code.</param>
private static void ExecuteJavaScript(Android.Webkit.WebView webView, string script)
{
    if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat)
    {
        // Best way for Android level 19 and above
        webView.EvaluateJavascript(script, null);
    }
    else
    {
        try
        {
            // Try to do with reflection
            CompatExecuteJavaScript(webView, script);
        }
        catch (Exception)
        {
            // Fallback to old way, which closes any open soft keyboard
            webView.LoadUrl("javascript:" + script);
        }
    }
}

private static void CompatExecuteJavaScript(Android.Webkit.WebView androidWebView, string script)
{
    Java.Lang.Class webViewClass = Java.Lang.Class.FromType(typeof(Android.Webkit.WebView));
    Java.Lang.Reflect.Field providerField = webViewClass.GetDeclaredField("mProvider");
    providerField.Accessible = true;
    Java.Lang.Object webViewProvider = providerField.Get(androidWebView);

    Java.Lang.Reflect.Field webViewCoreField = webViewProvider.Class.GetDeclaredField("mWebViewCore");
    webViewCoreField.Accessible = true;
    Java.Lang.Object mWebViewCore = webViewCoreField.Get(webViewProvider);

    Java.Lang.Reflect.Method sendMessageMethod = mWebViewCore.Class.GetDeclaredMethod(
        "sendMessage", Java.Lang.Class.FromType(typeof(Message)));
    sendMessageMethod.Accessible = true;

    Java.Lang.String javaScript = new Java.Lang.String(script);
    Message javaScriptCodeMsg = Message.Obtain(null, 194, javaScript);
    sendMessageMethod.Invoke(mWebViewCore, javaScriptCodeMsg);
}
 0
Author: martinstoeckli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-15 08:00:14