【问题标题】:Unable to hide Android Keyboard on Android 9无法在 Android 9 上隐藏 Android 键盘
【发布时间】:2020-02-26 16:10:43
【问题描述】:

创建一个点击 web 视图输入字段的应用程序,必须执行操作。捕获并启动选定的操作可以正常工作,但由于它是通过单击输入字段启动的,因此需要使用键盘。在 Android

我已经尝试了所有被认为是post 的最佳答案的庄园或组合,但没有一个适用于我在 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);
        }

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

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

重申我的问题:如何在 Android 9 上成功隐藏键盘。

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

【问题讨论】:

    标签: c# android xamarin.forms xamarin.android android-softkeyboard


    【解决方案1】:

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

    主项目中的接口

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

    电话专用代码

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

    用法

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

    让我知道它是否适合你。

    【讨论】:

    • 使用提供的电话特定代码既不适用于我之前运行良好的 Android 7.1,也不适用于我正在尝试修复的 Android 9。我所做的唯一细微更改是何时调用 ClearFocus 的条件,因为 CurrentFocus 在我的情况下永远不会是 EditText,因为我使用 WebViews。我的代码可以在这个 pastebin => pastebin.com/AGHndCPe 看到
    • 在你的代码中,你在哪里声明了IsKeyboardShown
    • 它显示在 KeyboardService 类中。它只是对 InputMethodManager 上名为 IsAcceptingText 的 get 属性的重命名。
    • 我找到了一个解决方案,虽然我不太喜欢它...通过将 JavaScript 注入我的 Web 视图来让它工作,我告诉它取消关注导致点击事件的元素首先发生。
    • 你能展示你尝试了什么吗?只是为了将来的帮助
    【解决方案2】:

    为了解决我的问题,我将一些 JavaScript 注入到 Webview 中,其中我没有聚焦输入字段,它被点击了。

    在我的 Webview 类中,我创建了一个方法,给定元素的字符串 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,然后我有一个在单击元素时运行的方法。要在单击 Web 视图时获得单击事件,请查看 link。在该方法中,我从事件参数中找出元素 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 中包含的字符串的格式,请查看之前提供的链接。

    如果我错过了什么,请随意问更多问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-17
      • 2016-05-04
      • 2017-02-16
      • 2014-08-07
      • 2016-11-21
      • 2011-10-26
      • 2012-05-18
      • 2017-09-27
      相关资源
      最近更新 更多