Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Desarrollo en Delphi para Android (https://www.clubdelphi.com/foros/forumdisplay.php?f=57)
-   -   Teclado virtual se superpone a los controles de entrada (https://www.clubdelphi.com/foros/showthread.php?t=92397)

MAXIUM 16-10-2017 03:52:59

¿Como saber la dimensión del teclado virtual?
 
Esa es la pregunta, ¿como puedo obtener las dimensiones del teclado virtual en Android.

La idea es que los Edit no queden debajo del teclado. Ya se que hay un ejemplo de Embarcadero, pero a veces ese ejemplo no funciona y al implementarlo en mi APP no hace nada.

También quisiera que funcionará en un Memo cada vez que hay salto de línea :)

AgustinOrtu 16-10-2017 16:55:48

No debería ser siempre el mismo ancho que el de la pantalla?

MAXIUM 16-10-2017 18:09:06

Cita:

Empezado por AgustinOrtu (Mensaje 521761)
No debería ser siempre el mismo ancho que el de la pantalla?

¿Y el alto?

MAXIUM 19-10-2017 17:32:05

Teclado virtual se superpone a los controles de entrada
 
La solución sería agregar esta línea al AndroidManifest.xml del proyecto. Por IDE no se donde encontrarlo, pero si lo agrego al archivo, al compilar se borra.

android:windowSoftInputMode="adjustPan"

Por código sería

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

pero no se como implementarlo en Delphi.

jhonalone 20-10-2017 12:32:01

Hola MAXIUM.

A ver si te sirve este hilo.

Si funciona, me lo cuentas. A mí me va a interesar en un futuro. Gracias.

Saludos

MAXIUM 20-10-2017 14:01:11

Cita:

Empezado por jhonalone (Mensaje 521867)
Hola MAXIUM.

A ver si te sirve este hilo.

Si funciona, me lo cuentas. A mí me va a interesar en un futuro. Gracias.

Saludos

Es demasiado código para algo que se soluciona con una línea usando Android Studio.

Además, el ejemplo a veces funciona y no logro aplicarlo a un Memo.

jhonalone 20-10-2017 21:46:54

A ver MAXIUM, en un TMemo, cuando abres una línea con ENTER y no cabe en el espacio que tú has reservado para el componente en tiempo de diseño, el mismo componente te habilita una nueva línea y desplaza el resto hacia arriba. Aquí no tienes problema.

Por otro lado, todos sabemos que Android Studio funciona distinto de Delphi.
Las Apps creadas con este programa, ocupan menos recursos que las compiladas con Delphi.
Tienes razón en que este programa necesita menos código que que Delphi (en bastantes acciones)
Claro, el código se genera directamente en Java. Delphi tiene que emular Java de alguna manera.

También me extraña que, si vas a programar la App en Android Studio, hagas esta pregunta en el foro de Delphi.

Saludos.

MAXIUM 21-10-2017 00:03:12

Cita:

Empezado por jhonalone (Mensaje 521898)
A ver MAXIUM, en un TMemo, cuando abres una línea con ENTER y no cabe en el espacio que tú has reservado para el componente en tiempo de diseño, el mismo componente te habilita una nueva línea y desplaza el resto hacia arriba. Aquí no tienes problema.

Por otro lado, todos sabemos que Android Studio funciona distinto de Delphi.
Las Apps creadas con este programa, ocupan menos recursos que las compiladas con Delphi.
Tienes razón en que este programa necesita menos código que que Delphi (en bastantes acciones)
Claro, el código se genera directamente en Java. Delphi tiene que emular Java de alguna manera.


Lamento no hacerme entender. Me refiero al código requerido para que el teclado virtual no se solape sobre los controles de entrada.

Ejemplo, a la izquierda un programa en DELPHI para Android, con un button, label y un par de edit al pie.

Al seleccionar los edit, el teclado virtual se superpone.

----

Este es el código Delphi sugerido por Embarcadero para evitar que se superponga: http://docwiki.embarcadero.com/CodeE...bleForm_Sample
Código Delphi [-]
procedure TFormMain.FormCreate(Sender: TObject);
begin
  VKAutoShowMode := TVKAutoShowMode.Always;
  VertScrollBox1.OnCalcContentBounds := CalcContentBoundsProc;
end;

procedure TFormMain.CalcContentBoundsProc(Sender: TObject;
                                       var ContentBounds: TRectF);
begin
  if FNeedOffset and (FKBBounds.Top > 0) then
  begin
    ContentBounds.Bottom := Max(ContentBounds.Bottom,
                                2 * ClientHeight - FKBBounds.Top);
  end;
end;

procedure TFormMain.RestorePosition;
begin
  VertScrollBox1.ViewportPosition := PointF(VertScrollBox1.ViewportPosition.X, 0);
  MainLayout1.Align := TAlignLayout.Client;
  VertScrollBox1.RealignContent;
end;

procedure TFormMain.UpdateKBBounds;
var
  LFocused : TControl;
  LFocusRect: TRectF;
begin
  FNeedOffset := False;
  if Assigned(Focused) then
  begin
    LFocused := TControl(Focused.GetObject);
    LFocusRect := LFocused.AbsoluteRect;
    LFocusRect.Offset(VertScrollBox1.ViewportPosition);
    if (LFocusRect.IntersectsWith(TRectF.Create(FKBBounds))) and
       (LFocusRect.Bottom > FKBBounds.Top) then
    begin
      FNeedOffset := True;
      MainLayout1.Align := TAlignLayout.Horizontal;
      VertScrollBox1.RealignContent;
      Application.ProcessMessages;
      VertScrollBox1.ViewportPosition :=
        PointF(VertScrollBox1.ViewportPosition.X,
               LFocusRect.Bottom - FKBBounds.Top);
    end;
  end;
  if not FNeedOffset then
    RestorePosition;
end;

procedure TFormMain.FormFocusChanged(Sender: TObject);
begin
  UpdateKBBounds;
end;

procedure TFormMain.FormVirtualKeyboardHidden(Sender: TObject;
                                           KeyboardVisible: Boolean;
                                           const Bounds: TRect);
begin
  FKBBounds.Create(0, 0, 0, 0);
  FNeedOffset := False;
  RestorePosition;
end;

procedure TFormMain.FormVirtualKeyboardShown(Sender: TObject;
                                          KeyboardVisible: Boolean;
                                          const Bounds: TRect);
begin
  FKBBounds := TRectF.Create(Bounds);
  FKBBounds.TopLeft := ScreenToClient(FKBBounds.TopLeft);
  FKBBounds.BottomRight := ScreenToClient(FKBBounds.BottomRight);
  UpdateKBBounds;
end;

En Android Studio solo coloco los componentes y ya

----

Y este es el código requerido para la misma función usando Android Studio:
Código Delphi [-]
NADA

¿Cual es el objetivo de la comparación?

Simplemente encontrar el equivalente en código o configuración del IDE en Delphi que realice lo mismo y que debería ser automático.

Cita:

Empezado por jhonalone (Mensaje 521898)
También me extraña que, si vas a programar la App en Android Studio, hagas esta pregunta en el foro de Delphi.

Saludos.

Como trato de explicar, mi intención no es programar en Android Studio, sino en Delphi...

Casimiro Notevi 21-10-2017 00:17:53

Pero eso es algo automático en android, no hay que hacer nada para que se desplace la pantalla al salir el teclado.

MAXIUM 21-10-2017 00:30:50

Cita:

Empezado por Casimiro Notevi (Mensaje 521903)
Pero eso es algo automático en android, no hay que hacer nada para que se desplace la pantalla al salir el teclado.

Exacto, pero no ocurre en Delphi XE. Te obligan a escribir todo ese código para algo tan simple

https://www.youtube.com/watch?v=D1DsS1131_c


jhonalone 21-10-2017 18:39:56

Hola MAXIUM.

Cita:

Exacto, pero no ocurre en Delphi XE. Te obligan a escribir todo ese código para algo tan simple
En mi pueblo se dice que "no hay atajo sin trabajo". Cuando encuentras ése "atajo" en Android, es porque alguien se ha tomado el "trabajo" de implementarlo. Ojala todo estuviera ya preparado para usar, (¿dónde estaría nuestro trabajo? ¡Cualquiera podría programar sin ningún esfuerzo ni estudio!).

Algo tenemos que poner de nuestra parte... Bien es verdad que cuando podemos usar un trabajo que ya está hecho, sería de tontos repetirlo.

En el tema que nos ocupa, hay bastante gente que ha tratado de resolverlo en Delphi, tomándose un esfuerzo, (como es natural). Ya que (como muchas otras cosas que no serían muy útiles) Embarcadero todavía no ha hecho ése trabajo por nosotros. ¡Y creo que no es poco, que podamos programar para Android desde Delphi!. Seguro que en otras versiones venideras nos facilitará esto y muchas otras cosas. Eso sí, ¡habrá que pagarlo!

Buscando por ahí he encontrado esta unidad que, sólo con citarla en la cláusula uses, hace todo el trabajo por nosotros.

He leído que hay gente trabajando en ella, pero todavía no está perfeccionada por completo.

Yo hice una prueba y funciona "razonablemente" bien. Si tienes en la pantalla un Edit solamente, la primera vez que lo pulsas el teclado virtual se superpone... luego ya no... (¡?)

Bueno, como tenemos el código fuente, tal vez podamos resolver éste y otros inconvenientes que pueda presentarnos...

Ya tenemos un punto de partida... y, para nosotros, no es mucho código citar la unit vkbdhelper en el apartado uses

Seguiremos "trabajando en este atajo"

Perdona por haberme extendido tanto.

Saludos.

AgustinOrtu 21-10-2017 19:06:24

Cita:

Empezado por jhonalone (Mensaje 521914)
Hola MAXIUM.

En mi pueblo se dice que "no hay atajo sin trabajo". Cuando encuentras ése "atajo" en Android, es porque alguien se ha tomado el "trabajo" de implementarlo. Ojala todo estuviera ya preparado para usar, (¿dónde estaría nuestro trabajo? ¡Cualquiera podría programar sin ningún esfuerzo ni estudio!).

Estaba a punto de comentar lo mismo. Android Studio no es magico, es tal y como dice jhonalone, alguien programo ese comportamiento. El problema de muchas "soluciones" que aparecen en la comunidad, es que son chapuceras a falta de un mejor termino, es decir, funcionan bien, pero hay que pegar un choclazo de codigo y replicarlo en cada proyecto. No es tan sencillo llegar a la simpleza de que simplemente poniendo un componente, o incluyendo una simple unidad, ya se resuelve todo el trabajo

Ahora, por otra parte, en algunas situaciones se ha demostrado como Delphi permite hacer muy simple lo que en las herramientas nativas como Android Studio toma mas lineas:

https://www.youtube.com/watch?v=q-xJRw2H-ZU

jhonalone 21-10-2017 19:28:48

Hola Agustín.

Muy cierto lo que dices.
Cita:

Ahora, por otra parte, en algunas situaciones se ha demostrado como Delphi permite hacer muy simple lo que en las herramientas nativas como Android Studio toma mas lineas
En muchas tareas Delphi nos facilita mucho las cosas. Mucho más que otros lenguajes de desarrollo. En otras pues tal vez menos... Esto es lo que hay...

Saludos.

MAXIUM 21-10-2017 19:49:32

Hola a todos, gracias por responder el hilo.

Imagino que la idea de todo lenguaje de alto nivel, es de abstraernos y concentrarnos en otros puntos. Así nacen las bibliotecas (librerias) y componentes en general.

Pero también hay razón que detrás de eso hay un esfuerzo de otros y se debe agradecer junto con aportar cuando se puede.

Probaré la unit mencionada y les comento. Esa es la idea del foro, que podamos compartir soluciones para cualquiera que tenga la misma inquietud, tenga una solución y no se atasque en su proyecto.

MAXIUM 21-10-2017 23:46:44

Cita:

Empezado por jhonalone (Mensaje 521914)
Buscando por ahí he encontrado esta unidad que, sólo con citarla en la cláusula uses, hace todo el trabajo por nosotros.

Cita:

vkbdhelper.pas

Force focused control visible when Android/IOS Virtual Keyboard showed or hiden

How to use?

place vkdbhelper into your project uses section. No more code needed.


----

kurono 22-10-2017 01:48:36

y el ejemplo que motraste mas arriba de embarcadero que tal funciona

MAXIUM 22-10-2017 02:22:03

Cita:

Empezado por kurono (Mensaje 521923)
y el ejemplo que motraste mas arriba de embarcadero que tal funciona

No funciona :(

Tal parece que no resulta para todas las versiones de Firemonkey. Embarcadero tiene una tarea pendiente https://stackoverflow.com/questions/...eyboard-height

kurono 22-10-2017 02:25:47

acabo de bajar la unit vkdbhelper y ya la probe funciona a la primera creo que me quedo con esta

MAXIUM 22-10-2017 02:46:59

Cita:

Empezado por kurono (Mensaje 521925)
acabo de bajar la unit vkdbhelper y ya la probe funciona a la primera creo que me quedo con esta

¿Que versión de Delphi estas usando?

kurono 22-10-2017 04:18:06

delphi xe7 update 1

jhonalone 22-10-2017 17:59:28

1 Archivos Adjunto(s)
Hola MAXIUM.
Con tu mismo código, Samsung J5, Android 6.0.1, DX10 Seattle
Código Delphi [-]
unit HeaderFooterTemplate;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.Edit, FMX.Controls.Presentation, vkbdhelper;

type
  THeaderFooterForm = class(TForm)
    Header: TToolBar;
    Footer: TToolBar;
    HeaderLabel: TLabel;
    Edit1: TEdit;
    Button1: TButton;
    Edit2: TEdit;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  HeaderFooterForm: THeaderFooterForm;

implementation

{$R *.fmx}

end.
[IMG]http://www.clubdelphi.com/foros/atta...1&d=1508687402[/IMG]

Como te dije, la primera vez que pulsas el TEdit puede que se solape, pero después ya no.

Mis disculpas, por no saber subir la imagen de modo que salga directamente en el mensaje. Creo que es la primera vez que lo intentod y no conozco otro modo.

Saludos

hernandoh 27-10-2017 17:09:32

Hoal es mi primera vez en el foro, pero tengo algo así:

Código Delphi [-]
procedure TLoginForm.FormVirtualKeyboardShown(Sender: TObject;
  KeyboardVisible: Boolean; const Bounds: TRect);

  var
  alto_pantalla,alto_teclado,pos_obj,pos_focus : integer;
  total_pos : Extended;
  control : TCOntrol;
begin
  alto_pantalla := Screen.Height;
  alto_teclado := Bounds.Height;

  control:= LoginForm.GetFocused as TControl;
  pos_obj := round(control.Position.Y);

  total_pos := round(alto_pantalla - alto_teclado);
  pos_focus := pos_obj - alto_teclado;

  if pos_obj > total_pos then
  begin
    Login_contenedor.Align := TAlignLayout.None;
    login_contenedor.AnimateFloat('Position.Y',-(pos_focus),0.1,TAnimationType.InOut,TInterpolationType.linear);
  end;
end;

No está completo pero seguro funciona; le puedes cacharrear y mejorar. Si logras mejorarlo, lo compartes por fa, es la idea del foro. :P


saludos.

rdaniel2000 04-11-2017 00:08:31

La solucion es esta:

Esta libreria MoveControlForVK

Buscala en google, la probe y funciona muy bien...

jhonalone 04-11-2017 23:03:39

Cita:

Empezado por rdaniel2000 (Mensaje 522257)
La solucion es esta:

Esta libreria MoveControlForVK

Buscala en google, la probe y funciona muy bien...

Hola rdaniel2000.
He estado buscando en Google la solución que propones y he leído los comentarios que se han hecho a esta solución.

Personalmente, creo que es más sencillo y operativo la solución de vkbdhelper propuesta más arriba. (Salvo que no haya encontrado la misma página que tu)
Yo me refiero a esta página. No parece ser la "gran solución"
Saludos a todos.

AgustinOrtu 13-11-2017 15:18:47

Creo que no lo mencionamos en este hilo, podrian probar esto

MAXIUM 13-11-2017 15:34:45

De todo lo mencionado el único que me ha dado "resultados" es qdac_fmx_vkhelper descrito aquí https://www.experts-exchange.com/que...l-showing.html con el inconveniente de que no funciona a la primera. Es decir, si le dan a un Edit, este se oculta bajo el teclado y hay que bajar el teclado y volver a darle al Edit...

Este es un problema descrito hasta en páginas japonesas y que Embarcadero debería solucionar. Es como si en Delphi te dejaran el control Botton sin el evento OnClick y te dijesen a pero eso debes programarlo tú...

Código Delphi [-]
unit qdac_fmx_vkhelper;

{
  Force focused control visible when Android Virtual Keyboard showed or hiden
  How to use:
  place qdac_fmx_vkhelper into your project uses section.No more code needed.

  Changes
  =======
  2016.9.26
  ========
  * Fixed:When a edit has focused and the virtual keyboard hidden,click back on android will exit directly
  2016.8.24
  ========
  * Fixed:When the scrollbox is scrolling,the adjust is too early make position not correct
  2016.7.29
  ========
  * Fixed:Access volation when you set control return action to Next and the control is the last one
  * Fixed:Segment fatal in special devices because scrollbox adjust render
  * Optimize the speed when add the adjust layout

  2016.7.19
  ========
  * Fixed:Adjust is overload in specail scene
  * Fixed:Form fill style is working correct

  2016.7.18
  ========
  * Fixed:When FLastControl is removed ,FLastControl not set to nil

  2016.7.7
  ========
  * Add Fix for system iOS virtualkeyboard error,detail see:http://blog.qdac.cc/?p=4003 (Chinese)

  2016.6.8
  ========
  * Add support for automic change focus control when you set edit ReturnKeyType to Next(You can disable it by set EnableReturnKeyHook to false

  2016.6.1
  =========
  * rewrite the adjust code for speed and other bug fix
  * rename to qdac_fmx_vkhelper
  2016.4.15
  + Add support for iOS

  2016.4.14
  + Add support for TMemo

  2016.1.16
  * Remove the timer for fast response(Use Idle message replace)
  * Fix a bug when a focus control reshow virtualkeyboard(Thanks ثئث®ء÷ؤê)
  * Fix a bug when use hide virtual keyboard by use hardware back key(Thanks °¢±؟أ¨)
  * Fix a FMX bug :after virtual keyboard shown,can't use hardware back key to exit app
  2016.1.8
  * Fix a bug when user switch to other app
  2015.7.12
  * Fix space after hide ime and rotate
  * Fix rotate detection

}
interface

uses classes, sysutils, math, FMX.controls, FMX.Layouts,
  System.Types;

type
  TControlHelper = class helper for TControl
    function OffsetOf(AParent: TControl): TPointF;
    function LocalToParent(AParent: TControl; APoint: TPointF)
      : TPointF; overload;
    function LocalToParent(AParent: TControl; R: TRectF): TRectF; overload;
  end;

  TScrollBoxHelper = class helper for TCustomScrollBox
  public
    procedure ScrollInView(ACtrl: TControl);
  end;

var
  EnableReturnKeyHook: Boolean;

function IsVKVisible: Boolean;
function GetVKBounds: TRectF; overload;

implementation

uses System.UITypes, System.Messaging,
  FMX.Types, System.Rtti,
  FMX.text, FMX.scrollbox, FMX.VirtualKeyboard, FMX.Forms,
  FMX.Platform, typinfo
{$IFDEF ANDROID}, FMX.Platform.Android, FMX.Helpers.Android,
  FMX.VirtualKeyboard.Android, Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Embarcadero {$ENDIF}
{$IFDEF IOS}
    , Macapi.Helpers, FMX.Platform.iOS, FMX.VirtualKeyboard.iOS,
  iOSapi.Foundation, iOSapi.UIKit
{$ENDIF}
    ;

type
  PAdjustItem = ^TAdjustItem;

  TAdjustItem = record
    Prior: PAdjustItem;
    Control: TControl;
    LastMargin: TPointF;
    LastViewPos: TPointF;
    LastBounds: TRectF;
    LastAlign: TAlignLayout;
  end;

  TQAdjustStack = class
  private
    function GetLastCtrl: TControl;
  protected
    FLast: PAdjustItem;
    function GetAdjusted: Boolean;
    procedure RemoveLast;
  public
    procedure Save(ACtrl: TControl);
    procedure Restore;
    procedure Remove(ACtrl: TComponent);
    property Adjusted: Boolean read GetAdjusted;
    property LastControl: TControl read GetLastCtrl;
  end;

  TVKNextHelper = class(TFMXObject)
  protected
    FOriginEvent: TKeyEvent;
    procedure SetParent(const Value: TFMXObject); override;
    procedure DoFocusNext(Sender: TObject; var Key: Word; var KeyChar: Char;
      Shift: TShiftState);

  end;

  TVKStateHandler = class(TComponent)
  protected
    FVKMsgId: Integer; // TVKStateChangeMessage دûد¢µؤ¶©شؤID
    FSizeMsgId: Integer; // TSizeChangedMessage دûد¢µؤ¶©شؤID
    FIdleMsgId: Integer;
    FLastControl: TControl;
    FAdjustStack: TQAdjustStack; // ×î؛َز»´خµ÷صûµؤScrollBox
{$IFDEF ANDROID}
    FVKState: PByte;
{$ENDIF}
    procedure DoVKVisibleChanged(const Sender: TObject;
      const Msg: System.Messaging.TMessage);
    procedure DoSizeChanged(const Sender: TObject;
      const Msg: System.Messaging.TMessage);
    procedure DoAppIdle(const Sender: TObject;
      const Msg: System.Messaging.TMessage);
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
    function AdjustByLayout(ARoot: TFMXObject; AVOffset: Single): Single;
    function AdjustByScrollBox(AScrollBox: TCustomScrollBox;
      AVOffset: Single): Single;
    function AdjustByPresentedScrollBox(AScrollBox: TCustomPresentedScrollBox;
      AVOffset: Single): Single;
    procedure AdjustCtrl(ACtrl: TControl; AVKBounds, ACtrlBounds: TRectF;
      AVKVisible: Boolean);
    function NeedAdjust(ACtrl: TControl; var ACaretRect: TRectF): Boolean;
    function IsAnimating(AScrollBox: TCustomScrollBox): Boolean;
    procedure AdjustIfNeeded;
  public
    constructor Create(AOwner: TComponent); overload; override;
    destructor Destroy; override;
  end;

var
  VKHandler: TVKStateHandler;
{$IFDEF ANDROID}

function GetVKBounds(var ARect: TRect): Boolean; overload;
var
  ContentRect, TotalRect: JRect;
begin
  ContentRect := TJRect.Create;
  TotalRect := TJRect.Create;
  MainActivity.getWindow.getDecorView.getWindowVisibleDisplayFrame(ContentRect);
  MainActivity.getWindow.getDecorView.getDrawingRect(TotalRect);
  Result := TotalRect.Bottom <> ContentRect.Bottom;
  if Result then
  begin
    ARect.Left := TotalRect.Left;
    ARect.Top := ContentRect.Bottom;
    ARect.Right := TotalRect.Right;
    ARect.Bottom := TotalRect.Bottom;
  end;
end;

function GetVKBounds: TRectF; overload;
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)));
end;
{$ELSE}
{$IFDEF IOS}
  _IOS_VKBounds: TRectF;

function GetVKBounds: TRectF; overload;
var
  ATop: Integer;
begin
  Result := _IOS_VKBounds;
  ATop := Screen.WorkAreaTop;
  Result.Top := Result.Top - ATop;
  Result.Bottom := Result.Bottom - ATop;
end;

function GetVKBounds(var ARect: TRect): Boolean; overload;
var
  ATemp: TRectF;
  AService: IFMXScreenService;
  AScale: Single;
begin
  ATemp := GetVKBounds;
  Result := not ATemp.IsEmpty;
  if Result then
  begin
    if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService,
      AService) then
    begin
      AScale := AService.GetScreenScale;
      ARect.Left := Trunc(ATemp.Left * AScale);
      ARect.Top := Trunc(ATemp.Top * AScale);
      ARect.Right := Trunc(ATemp.Right * AScale);
      ARect.Bottom := Trunc(ATemp.Bottom * AScale);
    end;
  end;
end;
{$ELSE}

function GetVKBounds: TRectF; overload;
begin
  Result := TRectF.Empty;
end;

function GetVKBounds(var ARect: TRect): Boolean; overload;
begin
  Result := false;
end;
{$ENDIF}
{$ENDIF}

/// ¸ù¾فMainActivityµؤ؟ةتساّسٍ؛ح»وح¼اّسٍ´َذ،ہ´ب·¶¨تا·ٌدشت¾ءثذéؤâ¼üإج
function IsVKVisible: Boolean;
var
  R: TRect;
begin
{$IFDEF NEXTGEN}
  Result := GetVKBounds(R);
{$ELSE}
  Result := false;
{$ENDIF}
end;
{ TControlHelper }

function TControlHelper.LocalToParent(AParent: TControl;
  APoint: TPointF): TPointF;
var
  AOffset: TPointF;
begin
  AOffset := OffsetOf(AParent);
  Result.X := APoint.X + AOffset.X;
  Result.Y := APoint.Y + AOffset.Y;
end;

function TControlHelper.LocalToParent(AParent: TControl; R: TRectF): TRectF;
var
  AOffset: TPointF;
begin
  AOffset := OffsetOf(AParent);
  Result := R;
  Result.Offset(AOffset.X, AOffset.Y);
end;

function TControlHelper.OffsetOf(AParent: TControl): TPointF;
var
  ACtrl: TControl;
begin
  ACtrl := Self;
  Result.X := 0;
  Result.Y := 0;
  while (ACtrl <> nil) and (ACtrl <> AParent) do
  begin
    Result.X := Result.X + ACtrl.Position.X;
    Result.Y := Result.Y + ACtrl.Position.Y;
    ACtrl := ACtrl.ParentControl;
  end;
  if not Assigned(ACtrl) then
    raise Exception.CreateFmt('ض¸¶¨µؤ؟ط¼‏ %s ²»تا %s µؤ×س؟ط¼‏', [Name, AParent.Name]);
end;
{ TScrollBoxHelper }

procedure TScrollBoxHelper.ScrollInView(ACtrl: TControl);
var
  R, LR: TRectF;
  dx, dy: Single;
begin
  R := ACtrl.LocalToParent(Self, ACtrl.LocalRect);
  LR := LocalRect;
  if not LR.Contains(R) then
  begin
    if R.Left > LR.Right then
      dx := LR.Right - R.Right
    else if R.Right < R.Left then
      dx := R.Left
    else
      dx := 0;
    if R.Top > LR.Bottom then
      dy := LR.Bottom - R.Bottom
    else if R.Bottom < LR.Top then
      dy := R.Top
    else
      dy := 0;
    ScrollBy(dx, dy);
  end;
end;

{ TVKStateHandler }
/// Adjust by layout,return the real adjust offset
function TVKStateHandler.AdjustByLayout(ARoot: TFMXObject;
  AVOffset: Single): Single;
var
  ALayout: TControl;
  // زئ¶¯ض¸¶¨¸¸ةدµؤ×س¶شدَµ½ذآµؤ¸¸¶شدَ
  procedure BeginUpdate(AObj: TFMXObject);
  begin
    if AObj is TControl then
      (AObj as TControl).BeginUpdate
    else if AObj is TCustomForm then
      (AObj as TCustomForm).BeginUpdate;
  end;

  procedure EndUpdate(AObj: TFMXObject);
  begin
    if AObj is TControl then
      (AObj as TControl).EndUpdate
    else if AObj is TCustomForm then
      (AObj as TCustomForm).EndUpdate;
  end;

  procedure MoveCtrls(AOldParent, ANewParent: TFMXObject);
  var
    I: Integer;
    AChild: TFMXObject;
  begin
    I := 0;
    BeginUpdate(AOldParent);
    BeginUpdate(ANewParent);
    try
      while I < AOldParent.ChildrenCount do
      begin
        AChild := AOldParent.Children[i];
        if AChild <> ANewParent then
        begin
          if AChild.Parent = AOldParent then
          begin
            AChild.Parent := ANewParent;
            Continue;
          end;
        end;
        Inc(I);
      end;
    finally
      EndUpdate(AOldParent);
      EndUpdate(ANewParent);
    end;
  end;

  function RootLayout(ARoot: TFMXObject): TControl;
  var
    ACtrl: TFMXObject;
    I: Integer;
    ALastRootStyle: String;
  begin
    Result := nil;
    if (ARoot.ComponentCount > 0) then
    begin
      for I := 0 to ARoot.ComponentCount - 1 do
      begin
        if ACtrl is TLayout then
        begin
          Result := ACtrl as TLayout;
          if Result.TagObject <> Self then
            Result := nil;
        end;
      end;
    end;
    if Result = nil then
    begin
      if ARoot is TCustomForm then
      begin
        with ARoot as TCustomForm do
        begin
          ALastRootStyle := StyleLookup;
          StyleLookup := '';
        end;
      end
      else
        ALastRootStyle := '';
      Result := TLayout.Create(ARoot);
      Result.Parent := ARoot;
      Result.TagObject := Self;
      with ARoot as IContainerObject do
        Result.SetBounds(0, 0, ContainerWidth, ContainerHeight);
      MoveCtrls(ARoot, Result);
      // ذقص‎´°جهرùت½±»´يخَة¾³‎شى³ةµؤختجâ
      if Length(ALastRootStyle) > 0 then
        (ARoot as TCustomForm).StyleLookup := ALastRootStyle;
    end;
  end;

begin
  ALayout := RootLayout(ARoot); // ب·بد´وشعسأسعµ÷صûµؤ¸ù²¼¾ض
  FAdjustStack.Save(ALayout);
  ALayout.Position.Y := ALayout.Position.Y + AVOffset;
  Result := AVOffset;
end;

function TVKStateHandler.AdjustByPresentedScrollBox
  (AScrollBox: TCustomPresentedScrollBox; AVOffset: Single): Single;
var
  ALastY: Single;
begin
  FAdjustStack.Save(AScrollBox);
  ALastY := AScrollBox.ViewportPosition.Y;
  AScrollBox.ViewportPosition.Offset(0,AVOffset+ALastY);
//  AScrollBox.ScrollBy(0, AVOffset);
  Result := ALastY - AScrollBox.ViewportPosition.Y;
end;

function TVKStateHandler.AdjustByScrollBox(AScrollBox: TCustomScrollBox;
  AVOffset: Single): Single;
var
  ALastY: Single;
begin
  FAdjustStack.Save(AScrollBox);
  ALastY := AScrollBox.ViewportPosition.Y;
  AScrollBox.ScrollBy(0, AVOffset);
  Result := ALastY - AScrollBox.ViewportPosition.Y;
end;

procedure TVKStateHandler.AdjustCtrl(ACtrl: TControl;
  AVKBounds, ACtrlBounds: TRectF; AVKVisible: Boolean);
var
  ACaretRect: TRectF;
  function TryByScrollBox(AParent: TFMXObject; var AOffset: Single): TFMXObject;
  begin
    Result := AParent.Parent;
    // ¸¸سذ¹ِ¶¯؟ٍ£¬شٍ³¢تش¹ِ¶¯½â¾ِ
    while Assigned(AParent) and (AOffset < 0) do
    begin
      if AParent is TCustomScrollBox then
      begin
        // ص‎شع¹ِ¶¯ت±²»ذèزھµ÷صû
        if IsAnimating(AParent as TCustomScrollBox) then
        begin
          AOffset := 0;
          Exit;
        end;
        AOffset := AOffset - AdjustByScrollBox
          (AParent as TCustomScrollBox, AOffset)
      end
      else if AParent is TCustomPresentedScrollBox then
        AOffset := AOffset - AdjustByPresentedScrollBox
          (AParent as TCustomPresentedScrollBox, AOffset);
      Result := AParent;
      AParent := AParent.Parent;
    end;
  end;
/// ½«ض¸¶¨µؤاّسٍزئ¶¯؟ةتساّ
  procedure ScrollInToRect;
  var
    AParent: TFMXObject;
    AOffset: Single;
  begin
    AOffset := AVKBounds.Top - ACaretRect.Bottom;
    AParent := TryByScrollBox(ACtrl, AOffset);
    if AOffset < 0 then
      AdjustByLayout(AParent, AOffset);
  end;
  procedure AddNextHelper;
  var
    AHelper: TVKNextHelper;
    I: Integer;
    AVKCtrl: IVirtualKeyboardControl;
  begin
    if Supports(ACtrl, IVirtualKeyboardControl, AVKCtrl) then
    begin
      for I := 0 to ACtrl.ComponentCount - 1 do
      begin
        if ACtrl.Components[i] is TVKNextHelper then
          Exit;
      end;
      AHelper := TVKNextHelper.Create(ACtrl);
      AHelper.SetParent(ACtrl);
    end;
  end;

begin
  if AVKVisible then
  begin
    if NeedAdjust(ACtrl, ACaretRect) then
    begin
      if FLastControl <> ACtrl then
      begin
        if Assigned(FLastControl) then
          FLastControl.RemoveFreeNotification(Self);
        FLastControl := ACtrl;
        FLastControl.FreeNotification(Self);
      end;
      ScrollInToRect;
    end;
    if EnableReturnKeyHook then
      AddNextHelper;
  end
  else
  begin
    FAdjustStack.Restore;
    FLastControl := nil;
  end;
end;

procedure TVKStateHandler.AdjustIfNeeded;
{$IFDEF ANDROID}
  procedure FMXAndroidFix;
  var
    AService: IFMXVirtualKeyboardService;
    AVK: TVirtualKeyboardAndroid;
    AListener: TVKListener;
    AContext: TRttiContext;
    AType: TRttiType;
    AField: TRttiField;
  begin
    if TPlatformServices.Current.SupportsPlatformService
      (IFMXVirtualKeyboardService, AService) then
    begin
      AVK := AService as TVirtualKeyboardAndroid;
      AContext := TRttiContext.Create;
      AType := AContext.GetType(AVK.ClassType);
      if Assigned(AType) then
      begin
        AField := AType.GetField('FVKListener');
        if Assigned(AField) then
        begin
          AListener := AField.GetValue(AVK).AsObject as TVKListener;
          AListener.onVirtualKeyboardHidden;
        end;
      end;
      Screen.ActiveForm.Focused := nil;
    end;
  end;

  procedure UpdateAndroidKeyboardServiceState;
  var
    ASvc: IFMXVirtualKeyboardService;
    AContext: TRttiContext;
    AType: TRttiType;
    AField: TRttiField;
    AInst: TVirtualKeyboardAndroid;
  begin
    if not Assigned(FVKState) then
    begin
      if (not Assigned(Screen.FocusControl)) and
        TPlatformServices.Current.SupportsPlatformService
        (IFMXVirtualKeyboardService, ASvc) then
      begin
        AInst := ASvc as TVirtualKeyboardAndroid;
        AContext := TRttiContext.Create;
        AType := AContext.GetType(TVirtualKeyboardAndroid);
        AField := AType.GetField('FState');
        if AField.GetValue(AInst).AsOrdinal <> 0 then
        begin
          FVKState := PByte(AInst);
          Inc(FVKState, AField.Offset);
        end;
      end;
    end;
    if Assigned(FVKState) and (FVKState^ <> 0) then
      FVKState^ := 0;
  end;
{$ENDIF}
{$IFDEF IOS}
  procedure VKHide;
  var
    ASvc: IFMXVirtualKeyboardService;
  begin
    if TPlatformServices.Current.SupportsPlatformService
      (IFMXVirtualKeyboardService, ASvc) then
      ASvc.HideVirtualKeyboard;
  end;
{$ENDIF}
  procedure RestoreCtrls;
  begin
{$IFDEF ANDROID}
    if not Assigned(Screen.FocusControl) then
      UpdateAndroidKeyboardServiceState;
{$ENDIF}
    if Assigned(FLastControl) then
      AdjustCtrl(FLastControl, RectF(0, 0, 0, 0),
        FLastControl.AbsoluteRect, false);
  end;
  procedure CheckHidden;
  var
    ACaretRect: TRectF;
    ACtrl: TControl;
  begin
    if Assigned(Screen.FocusControl) then
    begin
      ACtrl := Screen.FocusControl.GetObject as TControl;
      if Assigned(ACtrl) then
      begin
        if NeedAdjust(ACtrl, ACaretRect) then
          AdjustCtrl(ACtrl, GetVKBounds, ACtrl.AbsoluteRect, True);
      end;
    end
    else
    begin
{$IFDEF IOS}
      VKHide;
{$ENDIF}
      RestoreCtrls;
    end;
  end;

begin
  if IsVKVisible then // ½â¾ِµôذéؤâ¼üإجز‏²ط؛َµؤختجâ
    CheckHidden
  else
    RestoreCtrls;
end;

// ¹¹شى؛¯ت‎£¬¶©شؤدûد¢
constructor TVKStateHandler.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FAdjustStack := TQAdjustStack.Create;
  FVKMsgId := TMessageManager.DefaultManager.SubscribeToMessage
    (TVKStateChangeMessage, DoVKVisibleChanged);
  FSizeMsgId := TMessageManager.DefaultManager.SubscribeToMessage
    (TSizeChangedMessage, DoSizeChanged);
  FIdleMsgId := TMessageManager.DefaultManager.SubscribeToMessage(TIdleMessage,
    DoAppIdle);
end;

/// خِ¹¹؛¯ت‎£¬ب،دûدûد¢¶©شؤ
destructor TVKStateHandler.Destroy;
begin
  TMessageManager.DefaultManager.Unsubscribe(TVKStateChangeMessage, FVKMsgId);
  TMessageManager.DefaultManager.Unsubscribe(TSizeChangedMessage, FSizeMsgId);
  TMessageManager.DefaultManager.Unsubscribe(TIdleMessage, FIdleMsgId);
  FAdjustStack.Restore;
  inherited;
end;

/// شعس¦سأ؟صدذت±£¬¼ى²éذéؤâ¼üإجتا·ٌز‏²ط»ٍتا·ٌ¸²¸ا×،ءثµ±ا°»ٌµأ½¹µمµؤ؟ط¼‏
procedure TVKStateHandler.DoAppIdle(const Sender: TObject;
  const Msg: System.Messaging.TMessage);
begin
  AdjustIfNeeded;
end;

/// شع؛لتْئءاذ»»ت±£¬´¦ہي؟ط¼‏خ»ضأ
procedure TVKStateHandler.DoSizeChanged(const Sender: TObject;
const Msg: System.Messaging.TMessage);
var
  ASizeMsg: TSizeChangedMessage absolute Msg;
  R: TRect;
begin
  if Sender = Screen.ActiveForm then
  begin
    FAdjustStack.Restore;
    if GetVKBounds(R) then
    begin
      TMessageManager.DefaultManager.SendMessage(Sender,
        TVKStateChangeMessage.Create(True, R));
    end
  end;
end;

/// ذéؤâ¼üإج؟ة¼ûذش±ن¸üدûد¢£¬µ÷صû»ٍ»ض¸´؟ط¼‏خ»ضأ
procedure TVKStateHandler.DoVKVisibleChanged(const Sender: TObject;
const Msg: System.Messaging.TMessage);
var
  AVKMsg: TVKStateChangeMessage absolute Msg;
  ACtrl: TControl;
begin
  if AVKMsg.KeyboardVisible then // ¼üإج؟ة¼û
  begin
{$IFDEF IOS}
    _IOS_VKBounds := TRectF.Create(AVKMsg.KeyboardBounds);
{$ENDIF}
    if Screen.FocusControl <> nil then
    begin
      ACtrl := Screen.FocusControl.GetObject as TControl;
      AdjustCtrl(ACtrl, GetVKBounds, ACtrl.AbsoluteRect, True);
    end;
  end
  else
  begin
{$IFDEF IOS}
    _IOS_VKBounds := TRectF.Empty;
{$ENDIF}
    FAdjustStack.Restore;
  end;
end;

function TVKStateHandler.IsAnimating(AScrollBox: TCustomScrollBox): Boolean;
var
  AContext: TRttiContext;
  AType: TRttiType;
  AField: TRttiField;
begin
  AContext := TRttiContext.Create;
  AType := AContext.GetType(AScrollBox.AniCalculations.ClassType);
  if Assigned(AType) then
  begin
    AField := AType.GetField('FStarted');
    if Assigned(AField) then
      Result := AField.GetValue(AScrollBox.AniCalculations).AsBoolean
    else
      Result := false;
  end
  else
    Result := false;
end;

/// دىس¦×é¼‏تح·إح¨ضھ£¬زش±ـأâ·أختخقذ§µطض·
function TVKStateHandler.NeedAdjust(ACtrl: TControl;
var ACaretRect: TRectF): Boolean;
var
  ACaret: ICaret;
  ACaretObj: TCustomCaret;
  ACtrlBounds, AVKBounds: TRectF;
  function GetRootTop: Single;
  var
    ALayout: TLayout;
    AObj: TFMXObject;
  begin
    AObj := ACtrl.Root.GetObject;
    Result := 0;
    if AObj is TForm then
    begin
      if TForm(AObj).ChildrenCount > 0 then
      begin
        AObj := TForm(AObj).Children[0];
        if AObj is TLayout then
        begin
          ALayout := AObj as TLayout;
          Result := ALayout.Position.Y;
        end;
      end;
    end;
  end;

begin
  if Supports(ACtrl, ICaret, ACaret) then
  begin
    AVKBounds := GetVKBounds;
    ACtrlBounds := ACtrl.AbsoluteRect;
    ACaretObj := ACaret.GetObject;
    ACaretRect.TopLeft := ACtrl.LocalToAbsolute(ACaretObj.Pos);
    ACaretRect.Right := ACaretRect.Left + ACaretObj.Size.cx + 1;
    ACaretRect.Bottom := ACaretRect.Top + ACaretObj.Size.cy + 1; // دآأو¼سµمسàء؟
    if ACaretRect.Bottom > ACtrlBounds.Bottom then
    begin
      if ACtrl is TCustomPresentedScrollBox then
      begin
        AdjustByPresentedScrollBox(ACtrl as TCustomPresentedScrollBox,
          ACtrlBounds.Bottom - ACaretRect.Bottom);
        Result := false;
        Exit;
      end;
    end;
    Result := ACaretRect.IntersectsWith(AVKBounds) or (ACaretRect.Top < 0) or
      (ACaretRect.Top > AVKBounds.Bottom);
  end
  else
    Result := false;
end;

procedure TVKStateHandler.Notification(AComponent: TComponent;
Operation: TOperation);
begin
  if Operation = opRemove then
  begin
    if AComponent = FLastControl then
    begin
      FLastControl.RemoveFreeNotification(Self);
      FLastControl := nil;
    end
    else
      FAdjustStack.Remove(AComponent);
  end;
  inherited;
end;

{ TQAdjustStack }

function TQAdjustStack.GetAdjusted: Boolean;
begin
  Result := Assigned(FLast);
end;

function TQAdjustStack.GetLastCtrl: TControl;
begin
  if Assigned(FLast) then
    Result := FLast.Control
  else
    Result := nil;
end;

procedure TQAdjustStack.Remove(ACtrl: TComponent);
var
  ANext, ACurrent: PAdjustItem;
begin
  if Assigned(ACtrl) then
  begin
    ACurrent := FLast;
    ANext := nil;
    while Assigned(ACurrent) do
    begin
      if ACurrent.Control = ACtrl then
      begin
        if Assigned(ANext) then
          ANext.Prior := ACurrent.Prior
        else
          FLast := ACurrent.Prior;
        Dispose(ACurrent);
        break;
      end;
      ANext := ACurrent;
      ACurrent := ANext.Prior;
    end;
    ACtrl.RemoveFreeNotification(VKHandler);
  end;
end;

procedure TQAdjustStack.RemoveLast;
var
  APrior: PAdjustItem;
begin
  if Assigned(FLast) then
  begin
    APrior := FLast.Prior;
    Dispose(FLast);
    FLast := APrior;
  end;
end;

procedure TQAdjustStack.Restore;
var
  APrior: PAdjustItem;
begin
  while Assigned(FLast) do
  begin
    APrior := FLast.Prior;
    with FLast^ do
    begin
      if Control is TCustomScrollBox then
      begin
        Control.Margins.Bottom := LastMargin.Y;
        Control.Margins.Left := LastMargin.X;
        (Control as TCustomScrollBox).ViewportPosition := LastViewPos;
      end
      else if Control is TCustomPresentedScrollBox then
      begin
        Control.Margins.Bottom := LastMargin.Y;
        Control.Margins.Left := LastMargin.X;
        (Control as TCustomPresentedScrollBox).ViewportPosition := LastViewPos;
      end
      else
      begin
        if LastAlign = TAlignLayout.None then
          Control.Position.Point := LastMargin
        else
        begin
          Control.BoundsRect := LastBounds;
          Control.Align := LastAlign;
        end;
      end;
    end;
    Dispose(FLast);
    FLast := APrior;
  end;
end;

procedure TQAdjustStack.Save(ACtrl: TControl);
var
  AItem: PAdjustItem;
begin
  New(AItem);
  AItem.Prior := FLast;
  AItem.Control := ACtrl;
  if ACtrl is TCustomScrollBox then
  begin
    AItem.LastViewPos := (ACtrl as TCustomScrollBox).ViewportPosition;
    AItem.LastMargin.X := ACtrl.Margins.Left;
    AItem.LastMargin.Y := ACtrl.Margins.Bottom;
  end
  else if ACtrl is TCustomPresentedScrollBox then
  begin
    AItem.LastViewPos := (ACtrl as TCustomPresentedScrollBox).ViewportPosition;
    AItem.LastMargin.X := ACtrl.Margins.Left;
    AItem.LastMargin.Y := ACtrl.Margins.Bottom;
  end
  else
  begin
    AItem.LastMargin := ACtrl.Position.Point;
    AItem.LastBounds := ACtrl.BoundsRect;
    AItem.LastAlign := ACtrl.Align;
  end;
  FLast := AItem;
  ACtrl.FreeNotification(VKHandler);
end;

{ TVKNextHelper }

procedure TVKNextHelper.DoFocusNext(Sender: TObject; var Key: Word;
var KeyChar: Char; Shift: TShiftState);
var
  AVKCtrl: IVirtualKeyboardControl;
  procedure FocusNext(ACtrl: TControl);
  var
    AParent: TControl;
    ANext: IControl;
    ATabList: ITabList;
  begin
    if Assigned(ACtrl) and Assigned(ACtrl.ParentControl) then
    begin
      AParent := ACtrl.ParentControl;
      ATabList := AParent.GetTabList;
      if Assigned(ATabList) then
      begin
        ANext := ATabList.FindNextTabStop(ACtrl, not(ssShift in Shift), True);
        if Assigned(ANext) then
          ANext.SetFocus
        else
          FocusNext(AParent);
      end;
    end;
  end;

begin
  if Assigned(FOriginEvent) then
    FOriginEvent(Sender, Key, KeyChar, Shift);
  if Supports(Sender, IVirtualKeyboardControl, AVKCtrl) then
  begin
    if (Key = vkReturn) and (AVKCtrl.ReturnKeyType = TReturnKeyType.Next) then
      FocusNext(Sender as TControl);
  end;
end;

procedure TVKNextHelper.SetParent(const Value: TFMXObject);
begin
  if Value <> Parent then
  begin
    inherited;
    with Parent as TControl do
    begin
      FOriginEvent := OnKeyDown;
      OnKeyDown := DoFocusNext;
    end;
  end;
end;

initialization

// ½ِض§³ضAndroid+IOS
{$IF DEFINED(ANDROID) OR DEFINED(IOS)}
  VKHandler := TVKStateHandler.Create(nil);
{$ENDIF}
EnableReturnKeyHook := True;

finalization

{$IF DEFINED(ANDROID)  OR DEFINED(IOS)}
  VKHandler.DisposeOf;
VKHandler := nil;
{$ENDIF}

end.


La franja horaria es GMT +2. Ahora son las 09:40:07.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi