【问题标题】:Automatically resize ScrollView when keyboard is opened on Android/iOS在 Android/iOS 上打开键盘时自动调整 ScrollView 的大小
【发布时间】:2019-12-06 14:51:34
【问题描述】:

我已经看到了无数关于此的主题,但就目前而言,我找不到任何有用的东西。我有一个包含大部分输入字段的表单的肖像应用程序。

我已经创建了一个 ScrollView,并且在里面我已经添加了所有必要的字段。我添加了垂直布局组和内容大小调整器。现在,当我运行应用程序时,内容会很好地移动,但是当我打开键盘时,它会与内容重叠,并且在编辑它们时我看不到较低的输入字段。看起来不太好。因此,我正在寻找一个脚本/插件来为 android 和 iOS 启用此功能。我只看到了 iOS 特定的解决方案和通用的解决方案,但没有一个对我有用。我可以链接我找到的所有代码,但我认为它只会造成不必要的混乱。这些主题中的大多数都是旧的,以前可能有用,但现在不起作用。

理想情况下,我更喜欢全局解决方案,它只会在打开键盘时缩小整个应用程序,并在关闭键盘时将其展开。

顺便说一句。我正在使用 Unity 2019.2.15f1 并在搭载 Android 10 的 Pixel 3XL 上运行,如果这很重要的话。

编辑:

我创建了一个小型演示项目,您可以在其中测试键盘大小脚本:

https://drive.google.com/file/d/1vj2WG2JA1OHPc3uI4PNyAeYHtuHTLUXh/view?usp=sharing

它包含 3 个脚本: ScrollContent.cs - 它以编程方式将 InputH.cs 脚本附加到每个输入字段。 InputH.cs - 它通过从 KeyboardSize 脚本调用 OpenKeyboard/CloseKeyboard 来处理编辑单个输入字段的开始 (OnPointerClick) 和结束 (onEndEdit)。 KeyboardSize.cs - 来自@Remy_rm 的脚本,稍作修改(添加了一些日志、IsKeyboardOpened 方法和我调整键盘滚动位置的尝试)。

这个想法看起来不错,但问题很少:

1) “添加”的高度似乎可以工作,但是滚动的内容也应该被移动(我尝试解决这个问题是在 GetKeyboard 高度方法的最后一行,但它不起作用)。如果你滚动到底部的输入字段并点击它,在键盘打开后这个字段应该就在它上面。

2) 当我第二次点击另一个输入字段时,在编辑第一个输入字段时,会调用 onEndEdit 并关闭键盘。

布局层次结构如下所示:

【问题讨论】:

  • 查看我的编辑......
  • 我看了你的演示项目。你的 scrollRect 没有滚动到选定的输入字段的原因是你试图设置scrollRect.content.anchoredPosition。你需要设置它向下滚动的是scrollRect.verticalNormalizedPositiondocs.unity3d.com/2017.3/Documentation/ScriptReference/…,它采用0到1之间的值来设置滚动量。我已经更新了我的答案以反映这一点。

标签: android ios unity3d


【解决方案1】:

这个答案是假设你使用的是原生的TouchScreenKeyboard

您可以做的是添加一个图像作为垂直布局组的最后一个条目(我们称之为“缓冲区”),它的 alpha 设置为 0(使其不可见)并且它的高度设置为 0。这个将使您的布局组看起来不变。

然后,当您打开键盘时,将此“缓冲区”图像的高度设置为键盘的高度。由于内容大小调整器和垂直布局组,表单的输入字段将被推到键盘上方,而键盘“后面”将是空图像。

在 iOS 上很容易获得键盘的高度TouchScreenKeyboard.area.height 应该可以解决问题。然而,在 Android 上,这将返回一个空矩形。 This answer 答案解释了如何获取 Android 键盘的高度。

完全实现它看起来像这样(仅在 Android 上测试过,但也应该适用于 iOS)。请注意,我正在使用之前链接的答案中的方法来获取 Android 键盘高度。

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class KeyboardSize : MonoBehaviour
{
    [SerializeField] private RectTransform bufferImage;

    private float height = -1;

    /// <summary>
    /// Open the keyboard and start a coroutine that gets the height of the keyboard
    /// </summary>
    public void OpenKeyboard()
    {
        TouchScreenKeyboard.Open("");
        StartCoroutine(GetKeyboardHeight());
    }

    /// <summary>
    /// Set the height of the "buffer" image back to zero when the keyboard closes so that the content size fitter shrinks to its original size
    /// </summary>
    public void CloseKeyboard()
    {
        bufferImage.GetComponent<RectTransform>().sizeDelta = Vector2.zero;
    }

    /// <summary>
    /// Get the height of the keyboarding depending on the platform
    /// </summary>
    public IEnumerator GetKeyboardHeight()
    {
        //Wait half a second to ensure the keyboard is fully opened
        yield return new WaitForSeconds(0.5f);

#if UNITY_IOS
        //On iOS we can use the native TouchScreenKeyboard.area.height
        height = TouchScreenKeyboard.area.height;

#elif UNITY_ANDROID
        //On Android TouchScreenKeyboard.area.height returns 0, so we get it from an AndroidJavaObject instead.
        using (AndroidJavaClass UnityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            AndroidJavaObject View = UnityClass.GetStatic<AndroidJavaObject>("currentActivity").Get<AndroidJavaObject>("mUnityPlayer").Call<AndroidJavaObject>("getView");

            using (AndroidJavaObject Rct = new AndroidJavaObject("android.graphics.Rect"))
            {
                View.Call("getWindowVisibleDisplayFrame", Rct);

                height = Screen.height - Rct.Call<int>("height");
            }
        }
#endif
        //Set the height of our "buffer" image to the height of the keyboard, pushing it up.
        bufferImage.sizeDelta = new Vector2(1, height);
    }

    StartCoroutine(CalculateNormalizedPosition());

}

private IEnumerator CalculateNormalizedPosition()
{
    yield return new WaitForEndOfFrame();
    //Get the new total height of the content gameobject
    var newContentHeight = contentParent.sizeDelta.y;
    //Get the local y position of the selected input
    var selectedInputHeight = InputH.lastSelectedInput.transform.localPosition.y;
    //Get the normalized position of the selected input
    var selectedInputfieldHeightNormalized = 1 - selectedInputHeight / -newContentHeight;
    //Assign the button's normalized position to the scroll rect's normalized position
    scrollRect.verticalNormalizedPosition = selectedInputfieldHeightNormalized;
}

我已经编辑了您的InputH,通过添加一个静态游戏对象来跟踪最后选择的输入,该对象在单击输入字段时分配给如下:

public class InputH : MonoBehaviour, IPointerClickHandler
{
    private GameObject canvas;

    //We can use a static GameObject to track the last selected input
    public static GameObject lastSelectedInput;

    // Start is called before the first frame update
    void Start()
    {
        // Your start is unaltered
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        KeyboardSize ks = canvas.GetComponent<KeyboardSize>();
        if (!ks.IsKeyboardOpened())
        {
            ks.OpenKeyboard();

            //Assign the clicked button to the lastSelectedInput to be used inside KeyboardSize.cs
            lastSelectedInput = gameObject;
        }
        else
        {
            print("Keyboard is already opened");
        }
    }

为了(至少我的解决方案)正常工作,还必须做的另一件事是,我必须将“内容”游戏对象上的锚点更改为 top center,而不是您使用的 stretch

【讨论】:

  • 我试过这样计算:float newVerticalNormalizedPosition = (scrollVerticalPos + keyboardHeight) / newContentHeight;但它似乎不起作用。
  • @ makalele我已经编辑了我的答案,以包括计算归一化位置的方法,这将在选择输入场时将所选输入滚动到键盘上方。它可能需要一些偏移调整,但它应该可以工作。请注意,我还在您的 inputH 脚本中做了一个小编辑,并更改了 Content GameObject 上的锚点。如果需要,我可以上传我的演示项目版本。由于您的输入字段位于负 Y 位置,某些值(例如 newContentHeight)需要反转。
  • 它几乎可以工作了。我假设 contentParent 是 scrollRect.content。当我单击最后一个输入字段时,它会在其中心滚动,因此下半部分被截断。您可以上传您的演示项目版本吗?
  • 我不知道为什么,但是在您的项目版本中,内容有点太高了。在 Pixel 3 XL 上测试。这总比没有好,我想:) 只是一个想法:对我来说,无法想象没有内置的东西可以处理这个问题。这只是一件普通的事情。
猜你喜欢
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 2017-05-03
  • 2016-10-11
  • 2015-11-12
相关资源
最近更新 更多