Unity3d聊天视图适应手机键盘视图

时间:2024-04-14 18:01:05

我们实际开发出来的聊天系统,有时候会出现这样一个现象,点击InputField会弹出手机系统的键盘,键盘的弹出会遮住InputField甚至频道里面的消息。为了解决游戏聊天视图被这个系统键盘遮挡的问题,最直接的思路就是,输入框根据键盘的高度来做适应,意思就是说,根据键盘的高度,动态改变游戏聊天视图的高度。

目前市面上比较有名的游戏,如手游《龙珠》是这么做的。我参与的项目同样遇到这样的需求,采用了同样的方法。接下来就介绍下我的实现。 

Unity3d聊天视图适应手机键盘视图


如何获取键盘的高度?
安卓上实现
获取到unity这个主Activity,然后getView获取到安卓层的view组件信息,getWindowVisibleDisplayFrame的结果

就是这个键盘视图的高度

   /// <summary>
    /// 获取安卓平台上键盘的高度
    /// </summary>
    /// <returns></returns>
    public int GetKeyboardHeight() {
        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);
                return Screen.height - Rct.Call<int>("height");
            }
        }
    }

IOS上实现
IOS上就比较简单了,直接利用Unity的一个API,叫TouchScreenKeyboard,相关的API信息在这里查看:https://docs.unity3d.com/ScriptReference/TouchScreenKeyboard.html 。文档中明确说明了这个接口是安卓、IOS等移动平台获取键盘的Native层接口,TouchScreenKeyboard.area这个静态属性返回的是键盘覆盖区域部分的rect信息。所以我们直接利用这个信息就可以获取到这个键盘的高度,起始点等信息。

public float IOSGetKeyboardHeight() {
   return TouchScreenKeyboard.area.height;
}

为什么安卓上不直接用TouchScreenKeyboard.area呢?
答案是这个area在安卓上不可用。 
官方文档上明确说了,“Returns zero-Rect on Android.”。我看了一下,一直到5.6版本这个接口还是说的返货的是zero-Rect。

完整功能脚本
有一点需要注意的是不同平台的Screen.height不一样,注意适配这个问题,注意这一句 float keyboardHeight = AndroidGetKeyboardHeight()*RESOULUTION_HEIGHT/Screen.height;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 移动设备输入框的自适应组件
/// </summary>
public class ChatViewAdaptMobileKeyBoard : MonoBehaviour {
    public InputField _inputField;

    /// <summary>
    /// 自适应(弹出输入框后整体抬高)的面板的初始位置
    /// </summary>
    private Vector2 _adaptPanelOriginPos;
    private RectTransform _adaptPanelRt;
    private float RESOULUTION_HEIGHT = 1280F;

    public static ChatViewAdaptMobileKeyBoard Create(GameObject attachRoot, InputField inputField) {
        ChatViewAdaptMobileKeyBoard instance = null;
        instance = attachRoot.AddComponent<ChatViewAdaptMobileKeyBoard>();
        instance._inputField = inputField;
        return instance;
    }

    private void Start() {
        Debuger.Log("ChatViewAdaptMobileKeyBoard.start()");
        _inputField.onEndEdit.AddListener(OnEndEdit);
        _inputField.onValueChanged.AddListener(OnValueChanged);
        _adaptPanelRt = transform.Find("TabControl/Panels").GetComponent<RectTransform>(); 
        _adaptPanelOriginPos = _adaptPanelRt.anchoredPosition;
    }

    private void Update() {
        if (_inputField.isFocused) {

            if (Application.platform == RuntimePlatform.Android) {
                float keyboardHeight = AndroidGetKeyboardHeight()*RESOULUTION_HEIGHT/Screen.height;
                Debug.LogFormat("安卓平台检测到InputField.isFocused为真,获取键盘高度:{0}, Screen.height:{1}", keyboardHeight, Screen.height);
                _adaptPanelRt.anchoredPosition = Vector3.up * (keyboardHeight);
            }
            else if (Application.platform == RuntimePlatform.IPhonePlayer) {
                 float keyboardHeight = IOSGetKeyboardHeight() * RESOULUTION_HEIGHT / Screen.height;   
                 Debuger.LogFormat("IOS平台检测到键盘高度:{0},Screen.height: {1}", keyboardHeight, Screen.height);
                _adaptPanelRt.anchoredPosition = Vector3.up * keyboardHeight;
            }
            else {
                //Editor或其他平台,测试用!
                _adaptPanelRt.anchoredPosition = Vector3.up * 300f;
            }
        }
    }

    private void OnValueChanged(string arg0) { }


    /// <summary>
    /// 结束编辑事件,TouchScreenKeyboard.isFocused为false的时候
    /// </summary>
    /// <param name="currentInputString"></param>
    private void OnEndEdit(string currentInputString) {
        //Debuger.LogFormat("OnEndEdit, 输入内容:{0}, 结束时间:{1}", currentInputString, Time.realtimeSinceStartup);
        _adaptPanelRt.anchoredPosition = _adaptPanelOriginPos;
    }

    /// <summary>
    /// 获取安卓平台上键盘的高度
    /// </summary>
    /// <returns></returns>
    public int AndroidGetKeyboardHeight() {
        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);
                return Screen.height - Rct.Call<int>("height");
            }
        }
    }


    public float IOSGetKeyboardHeight() {
        return TouchScreenKeyboard.area.height;
    }
}