(****************************************************)
(*                         *)
(*     编写:爱吃猪头肉 & Flying Wang     *)
(*      上面的版权声明请不要移除。      *)
(*          2014-03-15          *)
(*                         *)
(****************************************************)

找到 XE5 安装的
FMX.VirtualKeyboard.Android.pas
将他们另存到(复制到)其他目录,例如您的工程目录。

将新复制出的文件加入到您的工程中。

【第一步】
打开 FMX.VirtualKeyboard.Android.pas 找到
function TVirtualKeyboardAndroid.GetVirtualKeyBoardState: TVirtualKeyBoardState;
begin
if FError then
Result := [vksError]
else
Result := [];
if IsAutoShow then
Result := Result + [vksAutoShow];
if not FError then
begin
if FState = vkbsVisible then
Result := Result + [vksVisible];
end;
end;

将上面的函数修改为
//Fix Error By 爱吃猪头肉 & Flying Wang
var
LastVirtualKeyboardHeight: Single = 0;
IsProcess_VisibleEvent: Boolean = False;
IsTimerRunning: Boolean = False;
LastTimerRunning: TDateTime = 0;

function GetIsTimerRunning: Boolean;
begin
if IsTimerRunning then
begin
//需要 uses System.DateUtils;
if MilliSecondsBetween(Now, LastTimerRunning) > 1000 then
begin
IsTimerRunning := False;
end;
end;
Result := IsTimerRunning;
end;

function ObtainKeyboardRect: TRect;
var
ContentRect, TotalRect: JRect;
begin
ContentRect := TJRect.Create;
TotalRect := TJRect.Create;
MainActivity.getWindow.getDecorView.getWindowVisibleDisplayFrame(ContentRect);
MainActivity.getWindow.getDecorView.getDrawingRect(TotalRect);
Result := TRectF.Create(ConvertPixelToPoint(TPointF.Create(TotalRect.left, TotalRect.top + ContentRect.height)),
ConvertPixelToPoint(TPointF.Create(TotalRect.right, TotalRect.bottom))).Truncate;
end;

function GetVirtualKeyboardHeight: Single;
var
KeyboardRect: TRect;
begin
Result := 0;
KeyboardRect := ObtainKeyboardRect;
//目前设置为 低于 30 就算隐藏。
if (KeyboardRect.Width < 30) or (KeyboardRect.Height < 30) then
begin
exit;
end;
Result := KeyboardRect.Height;
end;

procedure ProcessVirtualKeyboardEvent(Process_VisibleEvent: Boolean = False);
var
VirtualKeyboard: IFMXVirtualKeyboardService;
VirtualKeyboardAndroid: TVirtualKeyboardAndroid;
KeyboardRect: TRect;
VirtualKeyboardHeight: Single;
begin
IsProcess_VisibleEvent := Process_VisibleEvent;
IsTimerRunning := True;
LastTimerRunning := Now;
VirtualKeyboardHeight := GetVirtualKeyboardHeight;

KeyboardRect := ObtainKeyboardRect;
if TPlatformServices.Current.SupportsPlatformService(IFMXVirtualKeyboardService,
IInterface(VirtualKeyboard)) then
begin
VirtualKeyboardAndroid := TVirtualKeyboardAndroid(VirtualKeyboard);
end
else
begin
exit;
end;

//由于需要本代码完全接管消息发送。所以不能用 FState。
//if VirtualKeyboardAndroid.FState = vkbsVisible then

//当上次是显示,当本次不显示的时候。
if (LastVirtualKeyboardHeight >=1) and (VirtualKeyboardHeight < 1) then
begin
TThread.Synchronize(nil,
procedure
begin
VirtualKeyboardAndroid.SetState(TVirtualKeyboardAndroid.TvkbState.vkbsHidden);
TMessageManager.DefaultManager.SendMessage(VirtualKeyboardAndroid, TVKStateChangeMessage.Create(False, KeyboardRect), True);
end);
end
//当上次是不显示,本次是显示的时候
else if (LastVirtualKeyboardHeight < 1) and (VirtualKeyboardHeight >= 1) then
begin
TThread.Synchronize(nil,
procedure
begin
VirtualKeyboardAndroid.SetState(TVirtualKeyboardAndroid.TvkbState.vkbsVisible);
if IsProcess_VisibleEvent then
begin
TMessageManager.DefaultManager.SendMessage(VirtualKeyboardAndroid, TVKStateChangeMessage.Create(True, KeyboardRect), True);
end;
end);
//目前不建议支持高度变化。
//只有横竖切换才发生。
//各位可以在 Form 的 Resize 事件中处理。
// end
// //显示中,高度变了。
// else if
// (VirtualKeyboardAndroid >= 1) and (LastVirtualKeyboardHeight >=1) and
// (LastVirtualKeyboardHeight <> VirtualKeyboardAndroid)
// then
// begin
// TThread.Synchronize(nil,
// procedure
// begin
// VirtualKeyboardAndroid.SetState(TVirtualKeyboardAndroid.TvkbState.vkbsVisible);
// if IsProcess_VisibleEvent then
// begin
// TMessageManager.DefaultManager.SendMessage(VirtualKeyboardAndroid, TVKStateChangeMessage.Create(True, KeyboardRect), True);
// end;
// end);
end;
LastVirtualKeyboardHeight := VirtualKeyboardHeight;
end;

function HideInputForFixVirtualKeyboradEvent :Boolean;
var
TextView: JFMXTextEditorProxy;
begin
Result := False;
try
Screen.ActiveForm.Focused := nil;
TextView := MainActivity.getTextEditorProxy;
CallInUIThread(
procedure
begin
TextView.setFocusable(false);
TextView.setFocusableInTouchMode(false);
end);
Result := True;
except
Application.HandleException(Screen.ActiveForm);
end;
end;

function TVirtualKeyboardAndroid.GetVirtualKeyboardState: TVirtualKeyboardState;
var
KeyboardRect: TRect;
begin
if FError then
Result := [vksError]
else
Result := [];
if IsAutoShow then
Result := Result + [vksAutoShow];
if not FError then
begin
if (FState = vkbsVisible) then
begin
if GetVirtualKeyboardHeight < 1 then
begin
KeyboardRect := ObtainKeyboardRect;
TThread.Synchronize(nil,
procedure
begin
SetState(TVirtualKeyboardAndroid.TvkbState.vkbsHidden);
//当使用 Timer 之后,就不需要这边的处理了。
if not GetIsTimerRunning then
TMessageManager.DefaultManager.SendMessage(Self, TVKStateChangeMessage.Create(False, KeyboardRect), True);
end);
end;
end;
if FState = vkbsVisible then
Result := Result + [vksVisible];
end;
end;

缺点:我的山寨机上 26 的高度就没有输入法了。但是不知道其他的机器是多少。
事实上,只检查高度就可以,为了安全起见,才 高度 宽度 都检查的。

【第二步】
找到
procedure TVKListener.onVirtualKeyboardShown; 和
procedure TVKListener.onVirtualKeyboardHidden; 这两个函数
分别修改为下面的代码。
procedure TVKListener.onVirtualKeyboardShown;
begin
TThread.Synchronize(nil,
procedure
begin
FKeyboardService.SetState(TVirtualKeyboardAndroid.TvkbState.vkbsVisible);
//当使用 Timer 之后,就不需要这边的处理了。
if (not IsProcess_VisibleEvent) or (not GetIsTimerRunning) then
TMessageManager.DefaultManager.SendMessage(Self, TVKStateChangeMessage.Create(true, ObtainKeyboardRect), True);
end);
FEvent.SetEvent;
end;

procedure TVKListener.onVirtualKeyboardHidden;
begin
TThread.Synchronize(nil,
procedure
begin
FKeyboardService.SetState(TVirtualKeyboardAndroid.TvkbState.vkbsHidden);
//当使用 Timer 之后,就不需要这边的处理了。
if not GetIsTimerRunning then
TMessageManager.DefaultManager.SendMessage(Self, TVKStateChangeMessage.Create(false, ObtainKeyboardRect), True);
end);
FEvent.SetEvent;
end;

注意:这样改完之后,输入框之间切换,将不会有消息。

【第三步】
然后到 文件的前面(implementation 之前),定义

/// <summary>
/// When &lt; 1, it means VirtualKeyBoard Hided.
/// </summary>
function GetVirtualKeyBoardHeight: Single;

/// <summary>
/// <para>
/// Use a timer to call me. it will fix VirtualKeyboard Hide Message.
/// </para>
/// <para>
/// 用一个 TIMER 调用本函数,可以修复虚拟键盘的隐藏消息。
/// </para>
/// </summary>
/// <param name="Process_VisibleEvent">
/// 是否处理虚拟键盘的显示事件
/// </param>
procedure ProcessVirtualKeyboardEvent(Process_VisibleEvent: Boolean = False);

/// <summary>
/// 强制输入控件隐藏输入。
/// </summary>
function HideInputForFixVisualKeyboradEvent :Boolean;

下面是【使用方法】。

1. 放一个 Timer 例如叫 TimerForVKeyborad。300ms 一次。
procedure TForm1.TimerForVKeyboradTimer(Sender: TObject);
begin
{$IFDEF ANDROID}
ProcessVisualKeyboradEvent;
{$ENDIF}
end;

2. 完成如下事件。其实完全可以对每个输入框的按下事件处理。参考 4 。
procedure TForm1.FormVirtualKeyboardHidden(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect);
begin
Memo1.Lines.Add('键盘隐藏了');
Memo1.GoToTextEnd;
if Focused <> Edit1.AsIControl then //这是为了配合 Edit1 的按下事件做的判断。
Focused := nil; //这个代码其实也可以。
//{$IFDEF ANDROID}
// HideInputForFixVisualKeyboradEvent;
//{$ENDIF}
end;

3. 完成如下事件。
procedure TForm1.FormVirtualKeyboardShown(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect);
begin
Memo1.Lines.Add('键盘显示了');
Memo1.GoToLineEnd;
end;

4 或者对每个输入框的按下事件处理。
uses
FMX.Platform,
FMX.VirtualKeyboard;
procedure TForm1.Edit1Click(Sender: TObject);
var
VirtualKeyboard: IFMXVirtualKeyboardService;
begin
{$IFDEF ANDROID}
//当没有选中自己的时候不自动弹出。
if Focused <> Edit1.AsIControl then exit;
if GetVirtualKeyboardHeight < 1 then
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXVirtualKeyboardService,
IInterface(VirtualKeyboard)) then
begin
if not (vksVisible in VirtualKeyboard.VirtualKeyBoardState) then
begin
if (vksAutoShow in VirtualKeyboard.VirtualKeyBoardState) then
VirtualKeyboard.ShowVirtualKeyboard(Edit1);
end;
end;
end;
{$ENDIF}
end;

最新文章

  1. Js实现string.format
  2. &lt;s:property value=&quot;&quot;/&gt; 怎么截取返回值的固定长度的字符串
  3. UVa 103 - Stacking Boxes(dp求解)
  4. 通过VBA实现checkbox的全选和反选
  5. Java ArrayList的使用方法
  6. 编写可维护的JavaScript之事件处理
  7. Unable to load DLL &#39;rasapi32.dll&#39;: 动态链接库(DLL)初始化例程失败。
  8. 3573: [Hnoi2014]米特运输 - BZOJ
  9. 【Slickflow学习】.NET开源工作流环境搭建(三)
  10. shell中条件判断if中的-z到-d的意思【转载】
  11. 使用AWS的EC2服务器,进行跳墙
  12. NetBeans 7.2 or 8.0 编辑文件时不显示文件路径。
  13. C++模板之类型与数据绑定
  14. https post
  15. 微信小程序,前端大梦想(七)
  16. python条件判断与循环
  17. Window服务与Quartz.NET
  18. shell随笔
  19. Swapping eth0 and eth1 on OK335xS board
  20. 重置HTML标签样式

热门文章

  1. innobackupex做MySQL增量备份及恢复【转】
  2. crontab每10秒钟执行一次
  3. Parameters.Add和Parameters.AddWithValue
  4. zabbix报警Too many processes on zabbix server
  5. Python 3之str类型、string模块学习笔记
  6. 关于NotificationListenerService监听时有失败的处理
  7. SSD安装记录
  8. [USACO17FEB]Why Did the Cow Cross the Road I G
  9. 【AtCoder】Dwango Programming Contest V题解
  10. Spring中AOP实现