无法在 Android 9 上隐藏 Android 键盘

Unable to hide Android Keyboard on Android 9

创建一个点击 webview 输入字段必须执行操作的应用程序。捕获并启动选定的操作工作正常,但由于它是通过单击输入字段启动的,因此需要键盘。在 Android < 版本 9 上,我当前的代码可以很好地隐藏键盘,但在 Android 版本 9 上,它不能。

我已经尝试了在这个 post 上被认为是最佳答案的所有庄园或组合,但 none 在 Android 9

上为我的应用程序工作

下面是我的 MainActivity 中的一些代码,我的键盘服务实现的实例是在其中创建的。 MainActivity 代码之后是我为 android.

制作的键盘服务实现
[Activity(Label = "Dental.App", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, 
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, WindowSoftInputMode = SoftInput.StateAlwaysHidden) ]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
protected override void OnCreate(Bundle savedInstanceState)
        {
            ...
            DependencyService.Get<IServiceCollection>().SetKeyboardService(new KeyboardService(this, GetInputMethodManager()));            
            ...
        }

public InputMethodManager GetInputMethodManager()
        {
            return (InputMethodManager)GetSystemService(Context.InputMethodService);
        }
    }
public class KeyboardService : IKeyboardService
    {
        private InputMethodManager inputMethodManager;
        private readonly object mainActivity;
        public KeyboardService(object activity, InputMethodManager methodManager)
        {
            mainActivity = activity;
            inputMethodManager = methodManager;
        }
        public bool IsKeyboardShown => inputMethodManager.IsAcceptingText;

        public void HideKeyboard()
        {
            if (inputMethodManager == null || !(mainActivity is Activity activity)) return;

            Logging.Log(LogType.Information, $"Attempting to Hide Keyboard via 1st method...");

            //var view = activity.CurrentFocus;
            var view = activity.FindViewById(Android.Resource.Id.Content).RootView;
            if (view == null) Logging.Log(LogType.Warning, $"Failed to get View from Activity...");

            var token = view?.WindowToken;
            if (token == null) Logging.Log(LogType.Warning, $"Failed to get Token from View...");

            var success = inputMethodManager.HideSoftInputFromWindow(token, HideSoftInputFlags.None);
            Logging.Log(LogType.Information,
                $"{nameof(inputMethodManager.HideSoftInputFromWindow)} returned => {success}");

            if(success) view?.ClearFocus();
            if (!IsKeyboardShown)
            {
                view?.ClearFocus();
                return;
            }

            Logging.Log(LogType.Warning,
                $"Failed to Hide Keyboard via {nameof(inputMethodManager.HideSoftInputFromWindow)}...");
            HideKeyboardAttemptTwo(activity);
        }

        private void HideKeyboardAttemptTwo(Activity activity)
        {
            Logging.Log(LogType.Information, $"Attempting to Hide Keyboard via 2nd method...");

            //var view = activity.CurrentFocus;
            var view = activity.FindViewById(Android.Resource.Id.Content).RootView;
            if (view == null) Logging.Log(LogType.Warning, $"Failed to get View from Activity...");

            var token = view?.WindowToken;
            if (token == null) Logging.Log(LogType.Warning, $"Failed to get Token from View...");

            inputMethodManager.ToggleSoftInputFromWindow(token, ShowSoftInputFlags.None, HideSoftInputFlags.None);

            if (!IsKeyboardShown)
            {
                view?.ClearFocus();
                return;
            }

            Logging.Log(LogType.Warning, $"Failed to Hide Keyboard via {nameof(inputMethodManager.ToggleSoftInputFromWindow)}...");
        }

        public void ReInitializeInputMethod()
        {
            inputMethodManager = InputMethodManager.FromContext((Context) mainActivity);
        }

None 的 null 检查返回 true,即没有任何东西是 null。在 android 版本 9 上调用方法 HideKeyboard 中名为 success 的变量在 99% 的所有情况下返回 false。在 1% 的情况下为 true,键盘仍然打开。如果键盘仍显示在 HideKeyboard 的末尾,则代码会尝试通过在方法 HideKeyboardAttemptTwo 中切换来关闭键盘。在 Android 9 上执行这些方法中的任何一种都行不通,但是 运行 在 Android 7.1 上使用完全相同的代码就可以了。

我不完全确定我是否正确实现了 ToggleSoftInputFromWindow 的使用,它只能在键盘打开时 运行,即总是用于隐藏键盘。

重申我的问题:它如何成功隐藏 Android 9.

上的键盘

如果需要任何其他信息,请尽管询问,我会尽力查找并提供。

我将它用于我的应用程序,试试看吧

Interface in main project

namespace *.Services.Interfaces
{
    public interface IForceKeyboardDismissalService
    {
        void DismissKeyboard();
    }
}

Phone specific code

using Plugin.CurrentActivity;  //Nugget used to get activity

[assembly: Xamarin.Forms.Dependency(typeof(AndroidForceKeyboardDismissalService))]
namespace *.Droid.PhoneSpecific
{
    public class AndroidForceKeyboardDismissalService : IForceKeyboardDismissalService
    {
        public void DismissKeyboard()
        {
            var imm = InputMethodManager.FromContext(CrossCurrentActivity.Current.Activity.ApplicationContext);
            imm?.HideSoftInputFromWindow(CrossCurrentActivity.Current.Activity.Window.DecorView.WindowToken, HideSoftInputFlags.NotAlways);

            var currentFocus = CrossCurrentActivity.Current.Activity.CurrentFocus;
            if (currentFocus != null && currentFocus is EditText)
                currentFocus.ClearFocus();
        }
    }
}

Usage

DependencyService.Get<IForceKeyboardDismissalService>().DismissKeyboard();

让我知道它是否适合你。

为了解决我的问题,我在 Webview 中注入了一些 JavaScript,其中我取消了输入字段的焦点,该输入字段被单击。

在我的 Webview class 上,我创建了一个方法,给定一个元素的字符串 id,将切换该元素是否被聚焦。作为第二个输入,可以提供一个布尔值,但默认为 True,以指示您是否只想取消对元素的关注。

public class AdvancedWebView : HybridWebView
{
...
public void ToggleElementFocus(string elementId, bool onlyUnFocus = true)
        {
            var js = GetJsInvertFocus(elementId, onlyUnFocus);

            InjectJavaScript(js);

            // Logging.Logging.Log(LogType.Information, $"Injected Javascript => {js}");
        }
...
private string GetJsInvertFocus(string elementId, bool onlyUnFocus)
        {
            var builder = new StringBuilder();

            builder.Append($"if (document.getElementById('{elementId}'))");
            builder.Append("{");
            builder.Append($"var element = document.getElementById('{elementId}');");
            builder.Append($"if (element === document.activeElement)");
            builder.Append("{");
            builder.Append($"element.blur();");
            builder.Append("}");
            builder.Append($"else if({onlyUnFocus} == False)");
            builder.Append("{");
            builder.Append($"element.focus();");
            builder.Append("}");
            builder.Append("}");

            return builder.ToString();
        }
...
}

我正在扩展 XLabs 的 HybridWebview,因为它已经具有将 JavaScript 注入 Webview 的功能。这就是我从中获得 InjectJavaScript 方法的地方。

在我的应用程序页面上,使用 Webview,我有一个在单击元素时运行的方法。要在单击 Webview 时获取单击事件,请查看此 。在该方法中,我从事件参数中找出元素 id 是什么,然后使用此 id 注入上面显示的 JavaScript,以取消对元素的关注,导致键盘根本不出现。下面可以看到我的OnClicked方法。

public partial class DentalWebPage : AdvancedTabbedPage
{
...
 private void DentalWebView_OnClicked(object sender, ClickEvent e)
        {
            try
            {
                if (LogUserPosition(sender, e)) return;

                SwapToScanningTap();
            }
            catch (Exception ex)
            {
                Logging.Log(LogType.Exception,
                    ex.GetType().Namespace == typeof(BaseException).Namespace
                        ? $"{ex.GetType()} => {ex}"
                        : $"{ex.GetType()} => {ex.Message}; Stacktrace => {ex.StackTrace}");
            }
        }

private bool LogUserPosition(object sender, ClickEvent e)
        {
            if (Config.DebugMode) Logging.Log(LogType.Debug, $"WebView was clicked...");

            if (Config.DebugMode) Logging.Log(LogType.Debug, $"Element that was clicked is the following one => {e.Element}");

            var success = Enum.TryParse(e.Element.Split(' ')[1].Split('=')[1], out clickedInputId);

            if (!success && !(clickedInputId == InputId.MainContent_TextBoxInputStr ||
                              clickedInputId == InputId.MainContent_TextBoxScanOrder ||
                              clickedInputId == InputId.MainContent_TextBoxSelectProd ||
                              clickedInputId == InputId.MainContent_TextBoxStockReturn))
                return true;

            if (Config.DebugMode && webPageEnding == WebsiteControllers.Stock.ToString().ToLowerInvariant())
                Logging.Log(LogType.Debug, $"WebView was clicked while on the stock page...");

            return false;
        }

private void SwapToScanningTap()
        {
            PerformOnMainThread(() =>
            {
                CurrentPage = Children[1];

                ScanningToggle.IsToggled = true;

                try
                {
                    var isKeyboardShown = services.KeyboardService.IsKeyboardShown;
                    if (Config.DebugMode) Logging.Log(LogType.Debug, $"IsKeyboardShown returns => {isKeyboardShown}");

                    DentalWebView.ToggleElementFocus(clickedInputId.ToString());
                }
                catch (ObjectDisposedException)
                {
                    if (DisposedReattempt) throw;

                    if (Config.DebugMode)
                        Logging.Log(LogType.Debug,
                            $"Input Method has been Disposed; Attempting to reinitialize it and rerun the {nameof(SwapToScanningTap)} method ones again");

                    DisposedReattempt = true;
                    services.KeyboardService.ReInitializeInputMethod();
                    SwapToScanningTap();
                }
            });
        }
...
private void PerformOnMainThread(Action action)
        {
            try
            {
                Device.BeginInvokeOnMainThread(action);
            }
            catch (Exception ex)
            {
                Logging.Log(LogType.Exception,
                    ex.GetType().Namespace == typeof(BaseException).Namespace
                        ? $"{ex.GetType()} => {ex}"
                        : $"{ex.GetType()} => {ex.Message}; Stacktrace => {ex.StackTrace}");
            }
        }
}

如果你想了解e.Element中包含的字符串的格式,那就去看看前面提供的link。

如果我遗漏了什么,请随意提问。