不完美-FMX 程序中避免输入焦点被虚拟键盘遮挡

【更新】

2016.8.24

  • 修正了 ScrollBox 滚动过程中调整位置造成错位的问题

2016.7.29

  • 修正了当设置了最后一个焦点控件的ReturnType 为 Next 时,回车会出现 Access Volation 的问题
  • 修正了在特定设备中添加调整层时,引发对齐控件时出错的问题
  • 优化了将控件移动到调整层时的效率,避免重复对齐控件的问题

2016.7.19

  • 修正了特定场景下,调整焦点的位置过头的问题
  • 修正了设置窗体的填充设置会失效的问题

2016.7.18

  • 修复了在特定场景下出现 AV 错误的问题

2016.7.7

  • 修复了 Delphi 在 iOS 下特定场景下处理输入法状态不正确的问题,详细描述

2016.6.28:针对滚动条优化

  • 在滚动条惯性滚动过程中,不去尝试调整从而避免惯性滚动时的抖动现象(SVN 已更新)。我能说坑爹的动画中的 FStarted 没有公开,造成我不得不去用RTTI来检查是否处于动画中嘛:(

2016.6.1:重新设计调整算法

  • 新算法考虑了滚动框和Memo等控件的支持,优先使用滚动类控件内部的滚动来调整位置,在不能满足需要时,才会整体上移布局
  • 新版本已经加入 QDAC 的 SVN 源,并命名为 qdac_fmx_vkhelper.pas,以后更新直接检出即可
  • 后面的下载仍是老版本,保持原样不再更新,以便供已经使用老版本,但不愿意改名的朋友下载。其它朋友推荐使用新版本。

2016.4.15:更完美

  • 加入 iOS 支持和 Memo 控件的支持(Memo 控件的效果还不算完美,但可以满足日常使用要求)

2016.1.17:进一步修正

  • 修正了还有输入焦点时,按返回直接退出而不是去除焦点的问题
  • 修正了键盘隐藏时,未检查 FLastControl 是否为空造成 AV 错误的问题

2016.1.16:更完美

  • 移除 Timer,改在收到 TIdleMessage 时进行检查,从而提高响应速度
  • 修正了FMX系统自身在虚拟键盘自身隐藏时,没有将虚拟键盘服务内部的 FState 的值修改为TvkbState.Hidden,造成按返回键无法退出应用的问题(FMX自身的 Bug,本方案通过 RTTI 来强制改了它的状态);
  • 修正了输入焦点控件再次点击时弹出虚拟键盘时,没有正确调整焦点控件位置的问题;

2016.1.8

  • 修正了输入法在特定的情况下,检查输入法隐藏的代码没有正确激活的问题(感谢阿笨猫报告)

2015.7.16:更完美

  • 修正了输入法隐藏后旋转屏幕时布局出现错乱的问题(感谢红鱼儿、多春鱼)
  • 修正了特定场景下出现多余空白的问题

2015.6.3 :更完美

  • 解决了虚拟键盘隐藏后无法退出的问题;
  • 对于没有ScrollBox做底的窗体也加入了支持,自动添加一个 TLayout 而不是 TVertScrollBox(参考恢弘想法)

Delphi 提供了一个 Demo,叫 ScrollableFormDemo,要实现避免输入焦点被虚拟键盘遮挡的效果,需要写一堆的代码。也许这一切已不再重要,但是,它却没有考虑虚拟键盘隐藏的问题,在你隐藏虚拟键盘时,它没有自动回复原位,所以这一切显得更加的不完美。

为了避免每次都得写一堆代码的麻烦,我尝试换种思路来解决这一个问题,于是有了下面 vkbdhelper.pas 单元。但这个单元依然是三个字:不完美!不过我抛出来块砖,是希望将来第三方或官方能够更完美的解决这一问题。

大概的思路是通过订阅三个消息来捕获和控制虚拟键盘的状态:

  • TVKStateChangeMessage 消息,以得到常规的虚拟键盘状态改变通知;
  • TSizeChangedMessage 消息,以得到屏幕方向改变的通知;
  • TIdleMessage 消息,以便检查用户是否手动隐藏了虚拟键盘;如果用户手动隐藏了虚拟键盘,则自己触发一个TVKStateChangeMessage消息,这样子就回归到第 1 步正常的处理过程。

Long long ago,FMX 在 Android 下就有一个 Bug,一旦输入法弹出来,按键盘上的返回键就无法正确退出程序。该问题是由于输入法隐藏时,没有正确的通知应用改变虚拟键盘服务的 FState 成员造成的。本程序同时修正了这一个问题。

好了,思路有了,那么接下来是考虑易用性的问题:

实际上,易用性的设计一直是让人头痛的问题。我写这个单元就是为了解决系统自带的实现的不完美,虽然目前依然不是那么完美,但至少易用性上做到了我能做到的最好。你只需要在程序中的任意一个单元的 uses 里加入vkbdhelper,剩下的一句代码也不需要你去写。

接下来,还需要说啥呢?代码代表我的心~~~~

下载地址:VKHelperVsDemoR20160415

为什么我说这个设计依然不完美?。

  • Memo 的支持仍有问题
  • 控件上移出可视区域时,没有处理它的位置
  • 需要引入这个单元。
分享到:
  1. this is the only problem i can see tell now every thing else works fine , if you release a fix to this small issue then the patch will work 100%

  2. this is the only problem i can see tell now every thing else works fine , if you release a fix to this small issue then the patch will work 100%

    • 实际上你看一下这个源码中的GetVKBounds就知道了,Android 随时可以获取,iOS 是通过记录键盘消息通知,Windows 下通过FindWindows,然后 GetWindowRect 就可以,但VKHelper没去支持。

  3. 找到这儿了,请问这个点虚拟键盘上的隐藏键用定时器来检测的方法在C++Builder中怎么用哈?偶不懂delphi啊

    • 现在已经改成在应用的Idle消息里处理了。实际上就是获取输入法键盘的区域,如果隐藏的话,就是空的。

  4. 大侠们,我是菜鸟! uses 里加入vkbdhelper 编译运行时提示找不到FMX.scrollbox.dcu 怎么解决?

  5. 大侠们,我是菜鸟! uses 里加入vkbdhelper 提示找不到FMX.scrollbox.dcu 怎么解决?

    • 请加群上传具体的测试用例,我目前的测试是正常的。另外,请先确认从SVN上检出最新版本的 QDAC 3 里的 qdac_fmx_vkhelper.pas.

  6. 这个Uint以前随便用个Form测试是不错的,现在不起作用了,123123的问题我这里也一样,使用了他的方法也解决不了,这个Unit对每个Form有特别的要求吗?

  7. 这里的版本在Form的Fill使用渐变模式时,使用该单元会导致效果失效,变成最原始的默认效果,在ScrollInToRect查找AParent时修改如下后解决,具体原因还没有去细究,可能form存在其他parent导致效果失效 begin AParent := ACtrl; ALastParent := AParent.Parent; AHasScrollBox := False; while Assigned(AParent) and ( (not (AParent is TForm)) and (not (AParent is TCustomForm)) )do begin if AParent is TCustomScrollBox then ……

    • 初步判断问题出在样式的处理上,窗体的样式中加入的子控件也被迁移到新的调整布局控件上引起的,但详细的原因还需要跟踪分析。暂时的解决办法:在窗体上放一个TRectangle,然后用它来实现渐变。

  8. 您好,用了您最新的pas解决了大问题啊,赞一个!
    但还碰到几个小问题:
    1.比如界面上有两edit,分别叫A和B,A在上面位置,B在下面位置,离的比较近,A的keyup里写遇到回车则B.setfocus,运行起来输入法的框就会上下颤抖,跳不到B的焦点,上一版可以跳过去,但输入框盖住B,这一版直接抽起风了。
    2.有一个form,设了透明,上面放个rectangle,填充黑色,半透明,在安卓下,像一个弹出对话框的效果,不完全遮挡上一个form。可是show出这个form后,只要一点edit,输入法一出,form的透明立即失效,背景色就出现了,很奇怪。
    大哥辛苦,有空帮小弟看看怎么解决呢,谢谢,谢谢!

  9. 非常好用的表单。有个小Bug,就是在使用实体键盘的时候,多数情况下,虚拟键盘会隐藏起来,虚拟键盘只剩下一个提示行在那里。现在的表单依旧使用全尺寸的虚拟键盘。

  10. 在华为EMUI3.0上发现问题,点MEMO虚拟键盘显示出来,整个屏幕上下不停的闪动,2016.1.17版本

  11. 关于到你更新了最新版本 迫不急待的尝试了一下, 效果很好。 但是有个不太完美的功能, 在EDit的 下面有一个 button 本来是 下对齐的 当edit获取焦点的时候 这个button 呗挤到edit的上面了。

  12. 如果Memo行数比较多会有问题的,键盘显示出来之前,Memo上部可见,点入焦点显示键盘后,反而把Memo推到上面屏幕之外,无法看到输入了,当再次点击屏幕或Memo控件时,控件的位置会来回切换,体验就很差了。也就是说,当前输入的光标位置显示在可视区才是关键,而不是以这个控件的上方或下方可见为参照。

  13. 被转到这里了:http://www.fmxexpress.com/keep-controls-visible-when-virtual-keyboard-opens-in-delphi-xe8-firemonkey-on-android/

  14. 王大侠,我试出上面的问题如何重显了:当键盘适应一个控件后,我按住这个控件一直向下划动,就出来了.

  15. 在XE8 Update1下,有时候会计算错误,键盘隐藏后,ScrollBox的高度没有恢复。

  16. 版主,遇到个问题,当用ListView显示SearchBox时,可能产生错误。我改了下: if AVKMsg.KeyboardVisible then // 键盘可见 begin if Screen.FocusControl <> nil then//这里加了个判断 begin ACtrl := Screen.FocusControl.GetObject as TControl; ACtrlBounds := ACtrl.AbsoluteRect; AVKBounds := TRectF.Create(AVKMsg.KeyboardBounds); if ACtrlBounds.Bottom > AVKBounds.Top then begin ATarget := ACtrlBounds; ATarget.Top := AVKBounds.Top – ACtrlBounds.Height; ATarget.Bottom := ATarget.Top + ACtrlBounds.Height; ScrollInToRect(ATarget); end; end; end

  17. 先赞一个再评论。试了下,版面排列不能太复杂,比如 Form->VertScrollBox->Layout->Label->edit 这样的层次布局下edit的位置还是会被键盘遮挡,如果去掉层次中的Layout就可以了,希望能再接再力,优化到底。