草庐IT

UE4中对移动端键盘弹出/监听的处理

Yuk丶 2023-08-22 原文

UE4中对移动端键盘弹出/监听的处理

1.简述

背景:现有UE4的EditableText控件实现了输入文本并发送的基本功能。但是,点击输入框才可以弹出系统键盘,如果需要达到比如微信朋友圈的功能,比如自定义时机弹出,或对键盘弹出时监听等操作,则还需要一定的改造。可以发散思路,对可任意编辑文本框的控件进行改造,实现对获取文本、文本修改、键盘弹出、弹出监听等的功能来达到以上效果。

UE4中支持使用标准弹出对话框输入框或操作系统的虚拟键盘,启用方式可以参考
UE4Android虚拟键盘

2.处理方法

继承和调用关系:
UChatWidget(项目实际使用的Widget,访问公开API接口)— XXXInputPanel(包含输入框的面板)—SEditableText(输入框)— 基类SWidget

需要注意的是:
UWidget:是Widget Blueprint里面的可视化编辑控件,解决了SWidget编辑困难的问题,在运行时会转换为SWidget
SWidget:是UE运行时的真正控件,我们可以直接通过代码New进行创建

如果迷惑改哪个类,为何改,可以参考这篇科普文章:Slate/UMG/UWidget/SWidget的差别

具体实现:

以下实现对获取文本、文本修改、键盘弹出、弹出监听等的功能新增部分(可参考注释理解):

UChatWidget.h 声明委托和定义委托变量/函数

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTextFocus);

class EMOTIONPLUGIN_API UChatWidget: public UWidget
{
public:
	UPROPERTY(BlueprintAssignable, Category="InputBox|Event")
	FTextFocus TextFocusEvent;
	...//函数定义略
}

UChatWidget.cpp 主要处理对外接口

TSharedRef<SWidget> UChatWidget::RebuildWidget()
{
	SAssignNew(InputPanel, XXXInputPanel)
	.OnSend(BIND_UOBJECT_DELEGATE(FOnSend, OnInputPanelSend))//发送的绑定委托
	.OnTextFocus(BIND_UOBJECT_DELEGATE(FOnTextFocus, OnTextFocus))//键盘弹出的绑定委托
	return InputPanel.ToSharedRef();
}

//键盘弹出监听
void UChatWidget::OnTextFocus() const
{
   TextFocusEvent.Broadcast();
}
//设置键盘主动弹出
void UChatWidget::SetInputFocus()
{
   if (InputPanel.IsValid())
   {
      InputPanel->SetInputFocus();
   }
}
//设置编辑框内文本
void UChatWidget::SetInputText(FText InText)
{
   if ( InputPanel.IsValid() )
   {
      //与 Slate数据绑定
      const TAttribute<FText> TextBinding = TAttribute<FText>(InText);
      InputPanel->SetInputText(TextBinding);
   }
}
//获取编辑框内文本
FString UChatWidget::GetInputText()
{
   if (InputPanel.IsValid())
   {
      return InputPanel->GetInputText();
   }
   else
   {
      return "";
   }
}

XXXInputPanel.h

DECLARE_DELEGATE(FOnTextFocus)

class XXX_API XXXInputPanel: public SXXWidget
{
	SLATE_EVENT(FOnTextFocus, OnTextFocus)
public: //定义func,略
	...
private:
	FOnTextFocus OnTextFocusEvent;
}

XXXInputPanel.cpp 主要在输入框构造函数内做如下处理

void XXXInputPanel::Construct(const FArguments& InArgs)
{
OnTextFocusEvent = InArgs._OnTextFocus;
...
ChildSlot
[
SAssignNew(InputBox, SEditableText)
.ReturnKeyType(EReturnKeyType::ReturnKey_Go)
.VirtualKeyboardTrigger(EVirtualKeyboardTrigger::OnAllFocusEvents)  //将键盘触发方式改成所有,只有这样才可以接收我们代码控制的SetFocus事件,默认只接收点击事件
.VirtualKeyboardDismissAction(EVirtualKeyboardDismissAction::TextCommitOnAccept)
.HintText(InArgs._HintText)
.OnTextChanged(this, &XXXInputPanel::OnInputTextChange)
.OnTextCommitted(this, &XXXInputPanel::OnInputTextCommitted)
.OnEditableTextFocus(this, &XXXInputPanel::HandleOnFocus) //这里绑定处理键盘打开后的广播,后由Lua处建立监听
]
...
}

//执行委托触发相应的对象的函数
void XXXInputPanel::HandleOnFocus()
{
   OnTextFocusEvent.ExecuteIfBound();
}

//主动打开键盘
void XXXInputPanel::SetInputFocus()
{
   FSlateApplication::Get().SetKeyboardFocus(InputBox, EFocusCause::SetDirectly);
   FSlateApplication::Get().SetUserFocus(0, InputBox, EFocusCause::SetDirectly);
}

//设置编辑框内文本
void XXXInputPanel::SetInputText(TAttribute<FText> Text)
{
   InputBox.Get()->SetText(Text);
}

//获取编辑框内文本
FString XXXInputPanel::GetInputText()
{
   SEditableText* EditableTextPtr = InputBox.Get();
   return EditableTextPtr->GetText().ToString();
}

SEditableText.cpp :需监听键盘打开时,在父类SEditableText添加绑定

//这里由 Slate 事件返回给系统,通知并处理接收到事件Focus
FReply SEditableText::OnFocusReceived( const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent )
{
   EditableTextLayout->HandleFocusReceived(InFocusEvent);
   OnEditableTextFocus(); //这里添加调用委托,与上文使用套路一致
   return FReply::Handled();
}

void SEditableText::OnEditableTextFocus()
{
   OnEditableTextFocusCallback.ExecuteIfBound();
}

有关UE4中对移动端键盘弹出/监听的处理的更多相关文章

  1. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. ruby - 在 Ruby 中用键盘诅咒数组浏览 - 2

    我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作

  4. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  5. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  6. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  7. Ruby-vips 图像处理库。有什么好的使用示例吗? - 2

    我对图像处理完全陌生。我对JPEG内部是什么以及它是如何工作一无所知。我想知道,是否可以在某处找到执行以下简单操作的ruby​​代码:打开jpeg文件。遍历每个像素并将其颜色设置为fx绿色。将结果写入另一个文件。我对如何使用ruby​​-vips库实现这一点特别感兴趣https://github.com/ender672/ruby-vips我的目标-学习如何使用ruby​​-vips执行基本的图像处理操作(Gamma校正、亮度、色调……)任何指向比“helloworld”更复杂的工作示例的链接——比如ruby​​-vips的github页面上的链接,我们将不胜感激!如果有ruby​​-

  8. ruby - Faye WebSocket,关闭处理程序被触发后重新连接到套接字 - 2

    我有一个super简单的脚本,它几乎包含了FayeWebSocketGitHub页面上用于处理关闭连接的内容:ws=Faye::WebSocket::Client.new(url,nil,:headers=>headers)ws.on:opendo|event|p[:open]#sendpingcommand#sendtestcommand#ws.send({command:'test'}.to_json)endws.on:messagedo|event|#hereistheentrypointfordatacomingfromtheserver.pJSON.parse(event.d

  9. ruby - 如何使用 Ruby HTTP::Net 处理 404 错误? - 2

    我正在尝试解析网页,但有时会收到404错误。这是我用来获取网页的代码:result=Net::HTTP::getURI.parse(URI.escape(url))如何测试result是否为404错误代码? 最佳答案 像这样重写你的代码:uri=URI.parse(url)result=Net::HTTP.start(uri.host,uri.port){|http|http.get(uri.path)}putsresult.codeputsresult.body这将打印状态码和正文。

  10. ruby-on-rails - 使用 Ruby 正确处理 Stripe 错误和异常以实现一次性收费 - 2

    我查看了Stripedocumentationonerrors,但我仍然无法正确处理/重定向这些错误。基本上无论发生什么,我都希望他们返回到edit操作(通过edit_profile_path)并向他们显示一条消息(无论成功与否)。我在edit操作上有一个表单,它可以POST到update操作。使用有效的信用卡可以正常工作(费用在Stripe仪表板中)。我正在使用Stripe.js。classExtrasController5000,#amountincents:currency=>"usd",:card=>token,:description=>current_user.email)

随机推荐