WebView скрывает программную клавиатуру во время loadUrl (), что означает, что клавиатура не может оставаться открытой во время вызова JavaScript - PullRequest
15 голосов
/ 16 февраля 2012

Поскольку вы вызываете javascript в WebView через loadUrl ("javascript: ..."); Клавиатура не может оставаться открытой.

Метод loadUrl () вызывает loadUrlImpl () , который вызывает метод с именем clearHelpers () , который затем вызывает clearTextEntry () , который затем вызывает hideSoftKeyboard () , и тогда мы становимся ох, как одиноки, когда клавиатура исчезает.

Насколько я понимаю, все они являются частными и не могут быть переопределены.

Кто-нибудь нашел обходной путь для этого? Есть ли способ заставить клавиатуру оставаться открытой или вызвать javascript напрямую, не используя loadUrl ()?

Есть ли способ переопределить WebView таким образом, чтобы предотвратить (частный метод) clearTextEntry () от вызова?

Ответы [ 3 ]

12 голосов
/ 02 августа 2012

Обновление

KitKat добавил публичный метод для непосредственного вызова javascript: valuJavascript ()

Для более старых apis вы можете попробовать решение, подобное приведенному ниже, но если бы мне пришлось сделать это снова, я бы посмотрел просто на создание метода совместимости, который на KitKat использует вышеупомянутый метод, а на старых устройствах использует отражение для детализации. к внутреннему приватному методу: BrowserFrame.stringByEvaluatingJavaScriptFromString ()

Тогда вы можете напрямую вызывать javascript, не имея дело с loadUrl и добавляя "javascript: " к сценарию.

Старый ответ

По просьбе Алока Кулькарни я дам приблизительный обзор возможного обходного пути, о котором я подумал. Я на самом деле не пробовал, но в теории это должно работать. Этот код будет грубым и приведен только в качестве примера.

Вместо того, чтобы отправлять вызовы через loadUrl (), вы ставите в очередь свои вызовы javascript, а затем заставляете их опускать javascript. Что-то вроде:

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("; ");
    }
}

Вместо вызова loadUrl () вызовите этот метод. (Для упрощения я использовал синхронизированный блок, но он мог бы лучше подходить для другого маршрута. Поскольку javascript запускается в своем собственном потоке, он должен так или иначе быть безопасным для потоков).

Тогда ваш WebView будет иметь такой интерфейс:

public class JSInterface {

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

}

Возвращает строку с ожидающими командами и очищает их, чтобы они больше не возвращались.

Вы бы добавили его в WebView следующим образом:

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

Тогда в вашем javascript вы установили некоторый интервал для сброса ожидающих команд. В каждом интервале он будет вызывать JSInterface.getPendingJS(), который будет возвращать строку всех ожидающих команд, а затем вы сможете их выполнить.

Вы можете еще улучшить это, добавив проверку в метод execJS, чтобы увидеть, существует ли поле EditText в WebView и находится ли оно в фокусе. Если он есть, вы бы использовали этот метод организации очередей, но если бы его не было в фокусе, вы могли бы просто вызвать loadUrl () как обычно. Таким образом, он использует этот обходной путь только тогда, когда это действительно необходимо.

0 голосов
/ 14 мая 2018

Я мог бы реализовать идею cottonBallPaws использовать внутреннюю часть WebView с отражением и заставить ее работать на моем устройстве 4.2. Для версий Android, более старых, чем KitKat, есть любезные отступления.

Код написан на Xamarin, но он должен легко адаптироваться к нативному Java-коду.

/// <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 голосов
/ 13 марта 2015

Что касается более старых API (до 19), я использовал метод, аналогичный исключенному ответу, но немного другой.

Во-первых, я отслеживаю, отображается ли клавиатура с помощью javascript в веб-представлении примерно так:

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

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

Если отображается клавиатура и Java-слой Android пытается выполнить инъекцию js - я «откладываю» эту инъекцию. Я добавляю его в список строк, позволяю пользователю завершить ввод, а затем, после исчезновения клавиатуры, я обнаруживаю это и выполняю заделы инъекций.

...