日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

unity android输入法,Unity输入法相关(IME)

發布時間:2023/12/29 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 unity android输入法,Unity输入法相关(IME) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在UI上的InputField中, 中文輸入法的備選框不會跟隨在光標旁邊, 造成輸入不方便.

看到有一個相似的, 可是是WebGL的 :?https://blog.csdn.net/Rowley123456/article/details/103726927/

它通過添加Html的Input控件的方式來修改備選框位置, 直接跟平臺相關了, 不具有泛用性.

按照這個思路, 直接找Windows的輸入控制模塊:

[DllImport("imm32.dll")]public static externIntPtr ImmGetContext(IntPtr hWnd);

[DllImport("imm32.dll")]public static extern intImmReleaseContext(IntPtr hWnd, IntPtr hIMC);

[DllImport("imm32.dll")]public static extern bool ImmSetCompositionWindow(IntPtr hIMC, refCOMPOSITIONFORM lpCompForm);

[System.Runtime.InteropServices.DllImport("user32.dll")]private static extern System.IntPtr GetActiveWindow();

然后獲取窗口句柄, 設置位置的返回都是正確的, 可是結果并沒有改變備選框位置:

voidSetInputPos()

{

IntPtr hImc=ImmGetContext(GetWindowHandle());

COMPOSITIONFORM cf= newCOMPOSITIONFORM();

cf.dwStyle= 2;

cf.ptCurrentPos.X= 500;

cf.ptCurrentPos.Y= 500;bool setcom = ImmSetCompositionWindow(hImc, ref cf); //setcom == true

ImmReleaseContext(GetWindowHandle(), hImc);

}//結構體略

這就比較尷尬了, 設置沒有反應沒有報錯......

考慮到Unity應該有各個平臺的底層接口的, 以實現標準化的輸入(IME接口), 所以在BaseInputModule里面去找一找, 發現它下面有個BaseInput組件:

//StandaloneInputModule : PointerInputModule//PointerInputModule : BaseInputModule

public abstract classBaseInputModule : UIBehaviour

{protectedBaseInput m_InputOverride;//

//摘要://The current BaseInput being used by the input module.

public BaseInput input { get; }

......

}

這個跟輸入貌似有關系, 看到里面的變量跟Windows的API有點像:

public classBaseInput : UIBehaviour

{publicBaseInput();//

//摘要://Interface to Input.imeCompositionMode. Can be overridden to provide custom input//instead of using the Input class.

public virtual IMECompositionMode imeCompositionMode { get; set; }//

//摘要://Interface to Input.compositionCursorPos. Can be overridden to provide custom//input instead of using the Input class.

public virtual Vector2 compositionCursorPos { get; set; }

......

}

估計只要繼承它自己設置compositionCursorPos就能達到效果了, 直接創建一個繼承類型, 然后通過反射的方式給StandaloneInputModule設定BaseInput:

[RequireComponent(typeof(InputField))]public classIME_InputFollower : BaseInput

{publicInputField inputField;public overrideVector2 compositionCursorPos

{get{return base.compositionCursorPos;

}set{base.compositionCursorPos = new Vector2(200,200);  //test

}

}private static voidSetCurrentInputFollower(IME_InputFollower target)

{var inputModule =EventSystem.current.currentInputModule;if(inputModule)

{var field = inputModule.GetType().GetField("m_InputOverride", BindingFlags.Instance |BindingFlags.NonPublic);if(field != null)

{

field.SetValue(inputModule, target);if(target)

{

target.inputField.OnPointerDown(newPointerEventData(EventSystem.current));int caretPosition = string.IsNullOrEmpty(target.inputField.text) == false ? target.inputField.text.Length : 0;

target.inputField.caretPosition=caretPosition;

}

}

}

}

}

當InputField被focus的時候, SetCurrentInputFollower使用反射的方式設定BaseInput到當前的InputModule中, 然后手動觸發一下OnPointerDown和設定光標位置, 這樣就能刷新輸入法備選框了, 不會因為切換InputField而窗口不跟隨.?還有就是在編輯器下窗口的大小為Game窗口的大小, 而不是渲染部分的大小, 所以在編輯器下窗口大小與渲染不同的時候計算位置是不對的.

PS : 在測試時發現在Windows下compositionCursorPos的計算方法是窗口坐標, 并且起始坐標為窗口坐上角(0, 0), 不知道是不是DX平臺的特點.

填滿窗口看看原始的輸入法備選框在哪:

已經超出界面范圍了, 現在添加IME_InputFollower組件, 來計算一下位置讓備選框出現在輸入框的左下角:

public overrideVector2 compositionCursorPos

{get{return base.compositionCursorPos;

}set{#if UNITY_STANDALONE

var size = newVector2(Screen.width, Screen.height);

Vector3[] coners= new Vector3[4];

(inputField.transformasRectTransform).GetWorldCorners(coners);

Vector2 leftBottom= coners[0];var compositionCursorPos = new Vector2(leftBottom.x, size.y -leftBottom.y);base.compositionCursorPos =compositionCursorPos;#else

base.compositionCursorPos =value;#endif}

}

證明確實可行,?這樣這個邏輯應該就是可以在全部平臺中跑了, 只要添加compositionCursorPos的set邏輯就行了, 而平臺的差異只要在計算坐標中注意即可(不過除了Windows也沒其他需要的平臺了).

全部代碼貼一下:

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.UI;usingUnityEngine.EventSystems;usingSystem.Reflection;namespaceUIModules.UITools

{

[RequireComponent(typeof(InputField))]public classIME_InputFollower : BaseInput

{private static IME_InputFollower _activeFollower = null;private staticIME_InputFollower activeFollower

{get{return_activeFollower;

}set{if(_activeFollower !=value)

{

_activeFollower=value;

SetCurrentInputFollower(value);

}

}

}publicInputField inputField;public Vector2 imeOffset = new Vector2(-20f, -20f);private Common.Determinator m_determin = new Common.Determinator(Common.Determinator.Logic.All, false);public overrideVector2 compositionCursorPos

{get{return base.compositionCursorPos;

}set{#if UNITY_STANDALONE

var size = newVector2(Screen.width, Screen.height);

Vector3[] coners= new Vector3[4];

(inputField.transformasRectTransform).GetWorldCorners(coners);

Vector2 leftBottom= coners[0];

Vector2 leftBottomOffset= leftBottom +imeOffset;var compositionCursorPos = new Vector2(leftBottomOffset.x, size.y -leftBottomOffset.y);base.compositionCursorPos =compositionCursorPos;#else

base.compositionCursorPos =value;#endif}

}protected override voidAwake()

{base.Awake();if(inputField == false)

{

inputField= GetComponent();

}

m_determin.AddDetermine("Selected", () => { return inputField &&inputField.isFocused; });

m_determin.changed+= (_from, _to) =>{if(_to)

{

activeFollower= this;

}else{

CancelSelection();

}

};

}protected override voidOnDisable()

{base.OnDisable();

CancelSelection();

}voidUpdate()

{

m_determin.Tick();

}private voidCancelSelection()

{if(this ==activeFollower)

{

activeFollower= null;

}

}private static voidSetCurrentInputFollower(IME_InputFollower target)

{var inputModule =EventSystem.current.currentInputModule;if(inputModule)

{var field = inputModule.GetType().GetField("m_InputOverride", BindingFlags.Instance |BindingFlags.NonPublic);if(field != null)

{

field.SetValue(inputModule, target);if(target)

{

target.inputField.OnPointerDown(newPointerEventData(EventSystem.current));int caretPosition = string.IsNullOrEmpty(target.inputField.text) == false ? target.inputField.text.Length : 0;

target.inputField.caretPosition=caretPosition;

}

}

}

}

}

}

Determinator 就是一個簡單決策器:

usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;namespaceCommon

{public classDeterminator

{public enumLogic

{

All,

One,

}private bool_defaultValue;private bool_lastResult;public Logic logic { get; private set; }private Dictionary> m_determines = new Dictionary>();public System.Action changed = null;public boolResult

{get{var newResult =GetResult();if(_lastResult !=newResult)

{

ApplyChanged(newResult);

}returnnewResult;

}set{if(value !=_lastResult)

{

ApplyChanged(value);

}

}

}public string FailedReason { get; private set; }public string SuccessedReason { get; private set; }public Determinator(Logic logic, booldefaultVal)

{this.logic =logic;

_defaultValue=defaultVal;

_lastResult=_defaultValue;

}public void AddDetermine(string name, System.Funcfunc)

{

m_determines[name]=func;

}public void DeleteDetermine(stringname) { m_determines.Remove(name); }public boolGetResult()

{if(m_determines.Count > 0)

{switch(logic)

{caseLogic.All:

{foreach(var func inm_determines)

{if(func.Value.Invoke() == false)

{

FailedReason=func.Key;return false;

}

}

FailedReason= null;return true;

}break;caseLogic.One:

{foreach(var func inm_determines)

{if(func.Value.Invoke())

{

SuccessedReason=func.Key;return true;

}

}

SuccessedReason= null;return false;

}break;default:return_defaultValue;

}

}else{return_defaultValue;

}

}private void ApplyChanged(boolnewResult)

{var tempLast =_lastResult;

_lastResult=newResult;if(changed != null)

{

changed.Invoke(tempLast, newResult);

}

}public boolTick()

{returnResult;

}

}

}

總結

以上是生活随笔為你收集整理的unity android输入法,Unity输入法相关(IME)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。