FMX + Android + 旋转
FMX + Android + Rotation
尝试确定“180° 翻转”:all of the above methods 当设备快速翻转时(例如从“横向”到“反向横向”)毫无用处
但是有一种“原生方式”——通过OrientationEventListener。谁能帮助实现它?
unit android.view.OrientationEventListener;
interface
uses
AndroidAPI.JNIBridge, Androidapi.JNI.JavaTypes, Androidapi.JNI.GraphicsContentViewText;
type
JOrientationEventListener = interface;
JOrientationEventListenerClass = interface(JObjectClass)
['{6C04CBB1-63B1-45E1-9CBE-AE3F41A60064}']
function _GetORIENTATION_UNKNOWN: Integer; cdecl;
function canDetectOrientation : boolean; cdecl;
function init(context: JContext): JOrientationEventListener; cdecl; overload;
function init(context: JContext; rate: Integer): JOrientationEventListener; cdecl; overload;
procedure disable; cdecl;
procedure enable; cdecl;
procedure onOrientationChanged(Integerparam0: integer); cdecl;
property ORIENTATION_UNKNOWN : Integer read _GetORIENTATION_UNKNOWN;
end;
[JavaSignature('android/view/OrientationEventListener')]
JOrientationEventListener = interface(JObject)
['{ED4F435E-E48F-420E-A26E-75BFB8FCCB94}']
function canDetectOrientation: boolean; cdecl;
procedure disable; cdecl;
procedure enable; cdecl;
procedure onOrientationChanged(Integerparam0: integer); cdecl;
end;
TJOrientationEventListener = class(TJavaGenericImport<JOrientationEventListenerClass, JOrientationEventListener>)
end;
const
TJOrientationEventListenerORIENTATION_UNKNOWN = -1;
implementation
end.
我开始考虑创建 OrientationEventListener 的后代(目前只能在 Java 代码中完成),但是它有自己的一系列问题,即 canDetectOrientation 函数会总是 return 错误!
接下来我研究了为什么 activity 没有调用 TOrientationChangedMessage was not being sent when changing immediately from "normal" to "inverted" orientation - that appears to be because the onConfigurationChanged 方法(即 Delphi 无法解决这个问题)
然后我想也许最简单的方法是使用计时器 (ugh) 来检查屏幕旋转何时发生变化,所以我想出了以下 unit which is now in the Kastri repo:
unit DW.OrientationMonitor;
{*******************************************************}
{ }
{ Kastri }
{ }
{ Delphi Worlds Cross-Platform Library }
{ }
{ Copyright 2020-2021 Dave Nottage under MIT license }
{ which is located in the root folder of this library }
{ }
{*******************************************************}
{$I DW.GlobalDefines.inc}
interface
uses
// FMX
FMX.Types;
type
TOrientationChangedEvent = procedure(Sender: TObject; const Orientation: TScreenOrientation) of object;
TOrientationMonitor = class(TObject)
private
FHasOrientationChanged: Boolean;
FOrientation: TScreenOrientation;
FRotation: Integer;
FTimer: TTimer;
FOnOrientationChanged: TOrientationChangedEvent;
procedure DoOrientationChange;
procedure SetIsActive(const Value: Boolean);
procedure TimerHandler(Sender: TObject);
function GetIsActive: Boolean;
public
constructor Create;
destructor Destroy; override;
property IsActive: Boolean read GetIsActive write SetIsActive;
property Orientation: TScreenOrientation read FOrientation;
property OnOrientationChanged: TOrientationChangedEvent read FOnOrientationChanged write FOnOrientationChanged;
end;
implementation
uses
// DW
DW.UIHelper;
{ TOrientationMonitor }
constructor TOrientationMonitor.Create;
begin
inherited;
FOrientation := TUIHelper.GetScreenOrientation;
FTimer := TTimer.Create(nil);
FTimer.Interval := 100;
FTimer.OnTimer := TimerHandler;
end;
destructor TOrientationMonitor.Destroy;
begin
FTimer.Free;
inherited;
end;
procedure TOrientationMonitor.DoOrientationChange;
begin
if Assigned(FOnOrientationChanged) then
FOnOrientationChanged(Self, FOrientation);
end;
function TOrientationMonitor.GetIsActive: Boolean;
begin
Result := FTimer.Enabled;
end;
procedure TOrientationMonitor.SetIsActive(const Value: Boolean);
begin
FTimer.Enabled := Value;
end;
procedure TOrientationMonitor.TimerHandler(Sender: TObject);
var
LOrientation: TScreenOrientation;
begin
LOrientation := TUIHelper.GetScreenOrientation;
if LOrientation <> FOrientation then
begin
FOrientation := LOrientation;
DoOrientationChange;
end;
end;
end.
注意 该单元依赖于 Kastri 存储库中的其他单元,但您也可以提取 GetScreenOrientation
方法
使用TOrientationMonitor
的例子:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls,
DW.OrientationMonitor;
type
TForm1 = class(TForm)
Label1: TLabel;
private
FOrientation: TScreenOrientation;
FOrientationMonitor: TOrientationMonitor;
procedure OrientationChangedHandler(Sender: TObject; const AOrientation: TScreenOrientation);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
uses
System.TypInfo;
{ TForm1 }
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
FOrientationMonitor := TOrientationMonitor.Create;
FOrientationMonitor.OnOrientationChanged := OrientationChangedHandler;
FOrientation := FOrientationMonitor.Orientation;
Label1.Text := Format('Orientation: %s', [GetEnumName(TypeInfo(TScreenOrientation), Ord(FOrientation))]);
FOrientationMonitor.IsActive := True;
end;
destructor TForm1.Destroy;
begin
FOrientationMonitor.Free;
inherited;
end;
procedure TForm1.OrientationChangedHandler(Sender: TObject; const AOrientation: TScreenOrientation);
var
LInfo: PTypeInfo;
begin
LInfo := TypeInfo(TScreenOrientation);
Label1.Text := Format('Old: %s, New: %s', [GetEnumName(LInfo, Ord(FOrientation)), GetEnumName(LInfo, Ord(AOrientation))]);
FOrientation := AOrientation;
end;
end.
在这次调查中,我发现测试应用程序不会旋转到倒立的纵向位置。实现它的技巧是更新清单,将值为 fullSensor
的 android:screenOrientation
属性包含到 activity
节点中,如下所示:
<activity android:name="com.embarcadero.firemonkey.FMXNativeActivity"
android:label="%activityLabel%"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
android:screenOrientation="fullSensor"
android:launchMode="singleTask">
我知道这并不能完全回答您关于 OrientationEventListener 的问题,但我希望它能实现您的期望
尝试确定“180° 翻转”:all of the above methods 当设备快速翻转时(例如从“横向”到“反向横向”)毫无用处
但是有一种“原生方式”——通过OrientationEventListener。谁能帮助实现它?
unit android.view.OrientationEventListener;
interface
uses
AndroidAPI.JNIBridge, Androidapi.JNI.JavaTypes, Androidapi.JNI.GraphicsContentViewText;
type
JOrientationEventListener = interface;
JOrientationEventListenerClass = interface(JObjectClass)
['{6C04CBB1-63B1-45E1-9CBE-AE3F41A60064}']
function _GetORIENTATION_UNKNOWN: Integer; cdecl;
function canDetectOrientation : boolean; cdecl;
function init(context: JContext): JOrientationEventListener; cdecl; overload;
function init(context: JContext; rate: Integer): JOrientationEventListener; cdecl; overload;
procedure disable; cdecl;
procedure enable; cdecl;
procedure onOrientationChanged(Integerparam0: integer); cdecl;
property ORIENTATION_UNKNOWN : Integer read _GetORIENTATION_UNKNOWN;
end;
[JavaSignature('android/view/OrientationEventListener')]
JOrientationEventListener = interface(JObject)
['{ED4F435E-E48F-420E-A26E-75BFB8FCCB94}']
function canDetectOrientation: boolean; cdecl;
procedure disable; cdecl;
procedure enable; cdecl;
procedure onOrientationChanged(Integerparam0: integer); cdecl;
end;
TJOrientationEventListener = class(TJavaGenericImport<JOrientationEventListenerClass, JOrientationEventListener>)
end;
const
TJOrientationEventListenerORIENTATION_UNKNOWN = -1;
implementation
end.
我开始考虑创建 OrientationEventListener 的后代(目前只能在 Java 代码中完成),但是它有自己的一系列问题,即 canDetectOrientation 函数会总是 return 错误!
接下来我研究了为什么 activity 没有调用 TOrientationChangedMessage was not being sent when changing immediately from "normal" to "inverted" orientation - that appears to be because the onConfigurationChanged 方法(即 Delphi 无法解决这个问题)
然后我想也许最简单的方法是使用计时器 (ugh) 来检查屏幕旋转何时发生变化,所以我想出了以下 unit which is now in the Kastri repo:
unit DW.OrientationMonitor;
{*******************************************************}
{ }
{ Kastri }
{ }
{ Delphi Worlds Cross-Platform Library }
{ }
{ Copyright 2020-2021 Dave Nottage under MIT license }
{ which is located in the root folder of this library }
{ }
{*******************************************************}
{$I DW.GlobalDefines.inc}
interface
uses
// FMX
FMX.Types;
type
TOrientationChangedEvent = procedure(Sender: TObject; const Orientation: TScreenOrientation) of object;
TOrientationMonitor = class(TObject)
private
FHasOrientationChanged: Boolean;
FOrientation: TScreenOrientation;
FRotation: Integer;
FTimer: TTimer;
FOnOrientationChanged: TOrientationChangedEvent;
procedure DoOrientationChange;
procedure SetIsActive(const Value: Boolean);
procedure TimerHandler(Sender: TObject);
function GetIsActive: Boolean;
public
constructor Create;
destructor Destroy; override;
property IsActive: Boolean read GetIsActive write SetIsActive;
property Orientation: TScreenOrientation read FOrientation;
property OnOrientationChanged: TOrientationChangedEvent read FOnOrientationChanged write FOnOrientationChanged;
end;
implementation
uses
// DW
DW.UIHelper;
{ TOrientationMonitor }
constructor TOrientationMonitor.Create;
begin
inherited;
FOrientation := TUIHelper.GetScreenOrientation;
FTimer := TTimer.Create(nil);
FTimer.Interval := 100;
FTimer.OnTimer := TimerHandler;
end;
destructor TOrientationMonitor.Destroy;
begin
FTimer.Free;
inherited;
end;
procedure TOrientationMonitor.DoOrientationChange;
begin
if Assigned(FOnOrientationChanged) then
FOnOrientationChanged(Self, FOrientation);
end;
function TOrientationMonitor.GetIsActive: Boolean;
begin
Result := FTimer.Enabled;
end;
procedure TOrientationMonitor.SetIsActive(const Value: Boolean);
begin
FTimer.Enabled := Value;
end;
procedure TOrientationMonitor.TimerHandler(Sender: TObject);
var
LOrientation: TScreenOrientation;
begin
LOrientation := TUIHelper.GetScreenOrientation;
if LOrientation <> FOrientation then
begin
FOrientation := LOrientation;
DoOrientationChange;
end;
end;
end.
注意 该单元依赖于 Kastri 存储库中的其他单元,但您也可以提取 GetScreenOrientation
方法
使用TOrientationMonitor
的例子:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls,
DW.OrientationMonitor;
type
TForm1 = class(TForm)
Label1: TLabel;
private
FOrientation: TScreenOrientation;
FOrientationMonitor: TOrientationMonitor;
procedure OrientationChangedHandler(Sender: TObject; const AOrientation: TScreenOrientation);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
uses
System.TypInfo;
{ TForm1 }
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
FOrientationMonitor := TOrientationMonitor.Create;
FOrientationMonitor.OnOrientationChanged := OrientationChangedHandler;
FOrientation := FOrientationMonitor.Orientation;
Label1.Text := Format('Orientation: %s', [GetEnumName(TypeInfo(TScreenOrientation), Ord(FOrientation))]);
FOrientationMonitor.IsActive := True;
end;
destructor TForm1.Destroy;
begin
FOrientationMonitor.Free;
inherited;
end;
procedure TForm1.OrientationChangedHandler(Sender: TObject; const AOrientation: TScreenOrientation);
var
LInfo: PTypeInfo;
begin
LInfo := TypeInfo(TScreenOrientation);
Label1.Text := Format('Old: %s, New: %s', [GetEnumName(LInfo, Ord(FOrientation)), GetEnumName(LInfo, Ord(AOrientation))]);
FOrientation := AOrientation;
end;
end.
在这次调查中,我发现测试应用程序不会旋转到倒立的纵向位置。实现它的技巧是更新清单,将值为 fullSensor
的 android:screenOrientation
属性包含到 activity
节点中,如下所示:
<activity android:name="com.embarcadero.firemonkey.FMXNativeActivity"
android:label="%activityLabel%"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
android:screenOrientation="fullSensor"
android:launchMode="singleTask">
我知道这并不能完全回答您关于 OrientationEventListener 的问题,但我希望它能实现您的期望