如何在Android和iOS(Xamarin Forms)上获取键盘高度?

4

我想获取在Xamarin FormsAndroidiOS 上的 Keyboard 高度。

当屏幕以 Portrait 模式和 Landscape 模式显示时,如何获得键盘高度值?

Orientations

我已经找到了有关在 iOS 上使用 Swift 的参考文献:

What is the height of iPhone's onscreen keyboard?

How to get height of Keyboard?

但是能否提供在 Xamarin 上的源代码?

如何获取 Android 上键盘的高度呢?

请帮助我!

2个回答

5

iOS相关

有两种方法可以实现这个功能!

第一种方法:如果你的应用程序很小,只需为一个UIViewController实现以下步骤即可:

步骤1:添加用于显示和隐藏观察者的字段:

private NSObject _keyboardObserverWillShow;
private NSObject _keyboardObserverWillHide;

步骤2:更新ViewDidLoad和ViewDidUnload覆盖方法,以便在键盘显示或隐藏时添加/删除观察者:

public override void ViewDidLoad() {
    base.ViewDidLoad ();
    _keyboardObserverWillShow = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.DidShowNotification, KeyboardDidShowNotification);
    _keyboardObserverWillHide = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, KeyboardWillHideNotification);
}
    
public override void ViewDidUnload() {
    base.ViewDidUnload();
    NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardObserverWillShow);
    NSNotificationCenter.DefaultCenter.RemoveObserver(_keyboardObserverWillHide);
}

步骤三:然后您填充执行每个观察者的函数KeyboardDidShowNotificationKeyboardWillHideNotification。这段代码比较长,需要一些时间来解释每个部分,但您可以在评论区询问我。代码如下:

private void KeyboardWillHideNotification (NSNotification notification) 
{
    UIView activeView = View.FindFirstResponder();
    if (activeView == null)
        return;
    
    UIScrollView scrollView = activeView.FindSuperviewOfType (this.View, typeof(UIScrollView)) as UIScrollView;
    if (scrollView == null)
        return;
    
    // Reset the content inset of the scrollView and animate using the current keyboard animation duration
    double animationDuration = UIKeyboard.AnimationDurationFromNotification(notification);
    UIEdgeInsets contentInsets = new UIEdgeInsets(0.0f, 0.0f, 0.0f, 0.0f);
    UIView.Animate(animationDuration, delegate{
        scrollView.ContentInset = contentInsets;
        scrollView.ScrollIndicatorInsets = contentInsets;
    });
}   

private void KeyboardDidShowNotification (NSNotification notification) 
{
    UIView activeView = View.FindFirstResponder();
    if (activeView == null)
        return;

    ((UITextField)activeView).ShowDoneButtonOnKeyboard();

    UIScrollView scrollView = activeView.FindSuperviewOfType(this.View, typeof(UIScrollView)) as UIScrollView;
    if (scrollView == null)
        return;
    
    RectangleF keyboardBounds = UIKeyboard.BoundsFromNotification(notification);
    
    UIEdgeInsets contentInsets = new UIEdgeInsets(0.0f, 0.0f, keyboardBounds.Size.Height, 0.0f);
    scrollView.ContentInset = contentInsets;
    scrollView.ScrollIndicatorInsets = contentInsets;
    
    // If activeField is hidden by keyboard, scroll it so it's visible
    RectangleF viewRectAboveKeyboard = new RectangleF(this.View.Frame.Location, new SizeF(this.View.Frame.Width, this.View.Frame.Size.Height - keyboardBounds.Size.Height));
    
    RectangleF activeFieldAbsoluteFrame = activeView.Superview.ConvertRectToView(activeView.Frame, this.View);
    // activeFieldAbsoluteFrame is relative to this.View so does not include any scrollView.ContentOffset
    
    // Check if the activeField will be partially or entirely covered by the keyboard
    if (!viewRectAboveKeyboard.Contains(activeFieldAbsoluteFrame)) {
        // Scroll to the activeField Y position + activeField.Height + current scrollView.ContentOffset.Y - the keyboard Height
        PointF scrollPoint = new PointF(0.0f, activeFieldAbsoluteFrame.Location.Y + activeFieldAbsoluteFrame.Height + scrollView.ContentOffset.Y - viewRectAboveKeyboard.Height);
        scrollView.SetContentOffset(scrollPoint, true);
    }
}

第二种方式:创建一个键盘处理程序,通过您的BaseViewController可在每个页面上使用。

对于Android:

您可以按照StackOverflow提供的解决方案Xamarin solution here((Activity)Forms.Context).Window.SetSoftInputMode(SoftInput.AdjustPan);进行操作。有时这不起作用,所以您可以使用此方法进行修复。PS:没有复制这些答案,因为重新编写答案没有意义。


1
你基本上是在寻找“当键盘出现时调整Android屏幕大小”的解决方案。这里有一个解决方案https://stackoverflow.com/a/31452896/11104068,另一个解决方案在这里https://dev59.com/BIXca4cB1Zd3GeqPCggY#27208118。 - Saamer
1
非常感谢您的回答!对我来说帮助很大! - jnel899
1
因为原帖作者要求提供 Android 实现,但是却没有提供,所以被踩了。 - jfmg
1
@Saamer 对不起给你投了反对票,但我认为在StackOverflow上始终给问题提供完整的答案非常重要。所以如果你只是在你的回答中添加Android解决方案,我会撤销反对票。 - jfmg
@jfmg已添加,这就是您需要的 :) - Saamer
显示剩余4条评论

3
要在Xamarin.Forms项目中以编程方式获取键盘的高度,您需要注册一个平台服务。
接口:
namespace YourCoreProject.Services
{
    using System.Reactive.Subjects;

    /// <summary>
    /// A service to interact with the software keyboard.
    /// </summary>
    public interface IKeyboardInteractionService
    {
        /// <summary>
        /// Gets a <see cref="Subject{T}" /> which notifies about changes of the keyboard height.
        /// </summary>
        public Subject<float> KeyboardHeightChanged { get; }
    }
}

请注意:我使用了一个Subject(包含在System.Reactive.Subjects命名空间中)来通知键盘高度变化,但是如果这个概念更适合您的项目,您也可以使用事件。如果您选择使用Subject,您可能需要安装System.Reactive NuGet包。
iOS实现
namespace YourProject.iOS.Services
{
    using System.Reactive.Subjects;
    using UIKit;

    /// <inheritdoc cref="IKeyboardInteractionService" />
    public class KeyboardInteractionService : IKeyboardInteractionService
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="KeyboardInteractionService" /> class.
        /// </summary>
        public KeyboardInteractionService()
        {
            UIKeyboard.Notifications.ObserveWillShow((_, uiKeyboardEventArgs) =>
            {
                var newKeyboardHeight = (float)uiKeyboardEventArgs.FrameEnd.Height;
                this.KeyboardHeightChanged.OnNext(newKeyboardHeight);
            });

            UIKeyboard.Notifications.ObserveWillHide((_, uiKeyboardEventArgs) =>
            {
                this.KeyboardHeightChanged.OnNext(0);
            });
        }

        /// <inheritdoc cref="IKeyboardInteractionService.KeyboardHeightChanged" />
        public Subject<float> KeyboardHeightChanged { get; } = new Subject<float>();
    } 
}

Android实现

namespace YourProject.Droid.Services
{
    using System;
    using System.Reactive.Subjects;
    using Android.App;
    using Android.Views.InputMethods;
    using Xamarin.Essentials;

    /// <inheritdoc cref="IKeyboardInteractionService" />
    public class KeyboardInteractionService : IKeyboardInteractionService
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="KeyboardInteractionService" /> class.
        /// </summary>
        public KeyboardInteractionService()
        {
            var globalLayoutListener = new GlobalLayoutListener();
            this.KeyboardHeightChanged = globalLayoutListener.KeyboardHeightChanged;
        }

        /// <inheritdoc cref="IKeyboardInteractionService.KeyboardHeightChanged" />
        public Subject<float> KeyboardHeightChanged { get; }
    }
}

在Android中,您还需要一个监听器。这是实现方式:
namespace YourProject.Droid.Services
{
    using System;
    using System.Reactive.Subjects;
    using Android.App;
    using Android.Content.Res;
    using Android.Graphics;
    using Xamarin.Essentials;
    using static Android.Views.ViewTreeObserver;

    /// <inheritdoc cref="IOnGlobalLayoutListener"/>
    public class GlobalLayoutListener : Java.Lang.Object, IOnGlobalLayoutListener
    {
        private readonly Activity activity;

        /// <summary>
        /// Initializes a new instance of the <see cref="GlobalLayoutListener" /> class.
        /// </summary>
        public GlobalLayoutListener()
        {
            this.activity = Platform.CurrentActivity;

            if (this.activity?.Window?.DecorView?.ViewTreeObserver == null)
            {
                throw new InvalidOperationException($"{this.GetType().FullName}.Constructor: The {nameof(this.activity)} or a follow up variable is null!");
            }

            this.activity.Window.DecorView.ViewTreeObserver.AddOnGlobalLayoutListener(this);
        }

        /// <summary>
        /// Gets a <see cref="Subject{T}" /> which notifies about changes of the keyboard height.
        /// </summary>
        public Subject<float> KeyboardHeightChanged { get; } = new Subject<float>();

        /// <inheritdoc cref="IOnGlobalLayoutListener.OnGlobalLayout" />
        public void OnGlobalLayout()
        {
            if (!this.KeyboardHeightChanged.HasObservers)
            {
                return;
            }

            var screenSize = new Point();
            this.activity.WindowManager?.DefaultDisplay?.GetSize(screenSize);
            var screenHeight = screenSize.Y;

            var rootView = this.activity.FindViewById(Android.Resource.Id.Content);

            if (rootView == null)
            {
                return;
            }

            var screenHeightWithoutKeyboard = new Rect();
            rootView.GetWindowVisibleDisplayFrame(screenHeightWithoutKeyboard);

            int keyboardHeight;

            // Android officially supports display cutouts on devices running Android 9 (API level 28) and higher.
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.P)
            {
                var displayCutout = rootView.RootWindowInsets?.DisplayCutout;

                // Displays with a cutout need to be handled different due to the cutout type:
                //
                // Default cutout:
                // A display has no cutout. The screen height can be used used as usual.
                //
                // Corner cutout:
                // A display has a cutout in a corner on the top of the display. The screen height must add the safe area of the top to get the total screen height.
                //
                // Double cutout:
                // A display has a cutout in the middle on the top and in the middle on the bottom of the display.
                // The screen height must add the safe area of the bottom only to get the total screen height.
                // Adding the screen height of the top as well, would lead to false results.
                //
                // Tall cutout:
                // A display has a cutout in the middle on the top of the display. The screen height must add the safe area of the top to get the total screen height.
                keyboardHeight = displayCutout == null ?
                    screenHeight - screenHeightWithoutKeyboard.Bottom :
                    displayCutout.SafeInsetBottom <= 0 ?
                        screenHeight + displayCutout.SafeInsetTop - screenHeightWithoutKeyboard.Bottom :
                        screenHeight + displayCutout.SafeInsetBottom - screenHeightWithoutKeyboard.Bottom;
            }
            else
            {
                keyboardHeight = screenHeight - screenHeightWithoutKeyboard.Bottom;
            }

            var keyboardHeightInDip = keyboardHeight / Resources.System?.DisplayMetrics?.Density ?? 1;

            if (keyboardHeightInDip < 0.0f)
            {
                keyboardHeightInDip = 0.0f;
            }

            this.KeyboardHeightChanged.OnNext(keyboardHeightInDip);
        }
    }
}

请注意:我已经使用了包含在Xamarin.Essentials命名空间中的Platform.CurrentActivity来获取当前活动。如果您选择使用此解决方案,可能需要安装Xamarin.Essentials NuGet软件包。
希望这段代码能帮助仍然面临这个挑战的每一个人。希望将来会有Xamarin.Essentials的解决方案可用 :-)

1
当通过“Entry”进行制表时,高度会在0和200之间快速跳动(例如),因此我已经为“height == 0”添加了延迟,而“> 0”则立即生效。感谢解决方案! - Martin
Android的键盘高度计算不准确,我是这样修复的 - var keyboardHeight = rootView.Height - screenSizeWithoutKeyboard.Bottom + screenSizeWithoutKeyboard.Top; - Niro
此回答已经过时...请注意 - user1034912
注意...我已经更新了答案,甚至改进了它,以便考虑到切口的情况 :-) - jfmg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接