• XE5 修复 安卓 输入法隐藏 后 无法退出的问题 3.1


    (****************************************************)
    (*                         *)
    (*     编写:爱吃猪头肉 & 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;

  • 相关阅读:
    RFC-RTSP
    ISDN简记
    mysql:Cannot proceed because system tables used by Event Scheduler were found damaged at server start
    Linux下svn常用命令
    嵌入式开发者技能
    Lua和C的语法差别
    CubeMX使用及感受
    海康、大华IPC的rtsp格式
    环境小硕的转行之路-15-小作业、闭包、迭代器
    环境小硕的转行之路-14-动态传参、命名空间、nonlocal和global
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/3638062.html
Copyright © 2020-2023  润新知