【第一步】 打开 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; VirtualKeyboardAndroid: TVirtualKeyboardAndroid; LastTimerRunning: TDateTime = 0;
function GetIsTimerRunning: Boolean; begin if IsTimerRunning then begin 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;
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 HideInputForFixVirtualKeyboardEvent :Boolean; var VirtualKeyboard: IFMXVirtualKeyboardService; VirtualKeyboardAndroid: TVirtualKeyboardAndroid; TextView: JFMXTextEditorProxy; begin Result := False; try Screen.ActiveForm.Focused := nil; if TPlatformServices.Current.SupportsPlatformService(IFMXVirtualKeyboardService, IInterface(VirtualKeyboard)) then begin VirtualKeyboardAndroid := TVirtualKeyboardAndroid(VirtualKeyboard); end else begin exit; end; 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;
【第二步】 找到 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 < 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;
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;
----------------------------------------------
(C)(P)Flying Wang
function HideInputForFixVirtualKeyboardEvent :Boolean; 函数里头有垃圾代码。应该删掉多余的。
function HideInputForFixVirtualKeyboardEvent :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; 这样就可以了。
----------------------------------------------
(C)(P)Flying Wang
if Focused = Edit1.AsIControl then exit; 这个写反了,SORRY 。
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;
----------------------------------------------
(C)(P)Flying Wang