无法在 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。
如果我遗漏了什么,请随意提问。
创建一个点击 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 时获取单击事件,请查看此
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。
如果我遗漏了什么,请随意提问。