delphi 安卓开发实现沉浸式状态栏

对于安卓开发来说,沉浸式状态栏这个功能是非常实用的,否则软件的界面看起来,始终有点不上档次,下面用一个对比图来看看
在这里插入图片描述
然而之前在网上搜索,找了好多资料,不是修改这个就是修改那个,一番操作下来都不是我想要的效果,下面我把我实现的方式分享给大家,希望对大家有用。

【第一步】:建立一个独立的单元,代码如下:

unit u_Immerse;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, FMX.Platform,

  // 以下安卓需要的引用
  Androidapi.Helpers,
  FMX.Helpers.Android,
  FMX.Platform.Android,
  FMX.VirtualKeyboard.Android,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Provider,
  Androidapi.JNIBridge,
  Androidapi.JNI.App,
  Androidapi.JNI.JavaTypes,
  FMX.Platform.UI.Android,
  Androidapi.JNI.Embarcadero;

type

  TImmerse = class;

  TTopBotChangedEvent = procedure(const ATop, ABot: Integer) of object;

  TStatusBarStatus = (default, translucent, transparent);
  TStatusBarLight = (NoLight, Light);
  TNavigationBarStatus = (NoTransparent, IsTransparent);

  // 监听创建事件
  TKSCListener = class(TJavaLocal, JOnKeyboardStateChangedListener)
  private
    [Weak]
    FImmerse: TImmerse;
  public
    constructor Create(const AImmerse: TImmerse);
    { JOnKeyboardStateChangedListener }
    procedure onVirtualKeyboardWillShown; cdecl;
    procedure onVirtualKeyboardFrameChanged(newFrame: JRect); cdecl;
    procedure onVirtualKeyboardWillHidden; cdecl;
  end;

  // 用于取导航栏高度的
  JSystemPropertiesClass = interface(IJavaClass)
    ['{E271619E-6C38-430F-8A2A-FB0ECAB5054A}']
    function get(name: JString): JString; cdecl;
    function getInt(name: JString; default: Integer): Integer; cdecl;
  end;

  [JavaSignature('android/os/SystemProperties')]
  JSystemProperties = interface(IJavaInstance)
    ['{F02DD446-AC21-46DD-9A1F-37AF7797C9BA}']
  end;

  TJSystemProperties = class(TJavaGenericImport<JSystemPropertiesClass,
    JSystemProperties>)
  end;

  TImmerse = class
  private
    FNavigationBarHeight: Integer;
    FStatusBarHeight: Integer;

    FStatusBarStatus: TStatusBarStatus;
    FNavigationBarStatus: TNavigationBarStatus;

    FStatusBarLight: TStatusBarLight;
    FNavigationBarLight: TStatusBarLight;

    FStatusBarColor: TAlphaColor;
    FNavigationBarColor: TAlphaColor;
    FOnTopBotChanged: TTopBotChangedEvent;

    FSystemUiVisibility: Integer;
    FWindow: JWindow;
    FKSListener: TKSCListener;

    function AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
    function GetNavigationBarHeight: Integer; // 取导航栏高度
    procedure DoOnTopBotChanged;
    procedure SetStatusBarStatus(const Value: TStatusBarStatus);
    procedure SetStatusBarLight(const Value: TStatusBarLight);
    procedure SetNavigationBarStatus(const Value: TNavigationBarStatus);
    procedure SetNavigationBarColor(const Value: TAlphaColor);
    procedure SetNavigationBarLight(const Value: TStatusBarLight);
    procedure SetStatusBarColor(const Value: TAlphaColor);
  public
    constructor Create;
    destructor Destroy; override;

    procedure RefreshSUIV; // 刷新一次 SystemUiVisibility

    property NavigationBarHeight: Integer read FNavigationBarHeight
      write FNavigationBarHeight;
    property StatusBarHeight: Integer read FStatusBarHeight
      write FStatusBarHeight;
    property StatusBarStatus: TStatusBarStatus read FStatusBarStatus
      write SetStatusBarStatus;
    property OnTopBotChanged: TTopBotChangedEvent read FOnTopBotChanged
      write FOnTopBotChanged;
    property NavigationBarStatus: TNavigationBarStatus read FNavigationBarStatus
      write SetNavigationBarStatus;
    property StatusBarLight: TStatusBarLight read FStatusBarLight
      write SetStatusBarLight;
    property NavigationBarLight: TStatusBarLight read FNavigationBarLight
      write SetNavigationBarLight;
    property NavigationBarColor: TAlphaColor read FNavigationBarColor
      write SetNavigationBarColor;
    property StatusBarColor: TAlphaColor read FStatusBarColor
      write SetStatusBarColor;
  end;

implementation

{ TImmerse }

function TImmerse.AppEvent(AAppEvent: TApplicationEvent;
  AContext: TObject): Boolean;
begin
  Result := false;
  // 激活时刷新一次
  if AAppEvent = TApplicationEvent.BecameActive then
  begin
    RefreshSUIV;
  end;
end;

constructor TImmerse.Create;
var
  AppEventService: IFMXApplicationEventService;
begin
  inherited Create;
  // 增加一个窗体激活事件的响应,这是因为fmx.jar中的BUG引发的,原本应该是不需要处理这个响应的
  if TPlatformServices.Current.SupportsPlatformService
    (IFMXApplicationEventService, AppEventService) then
    AppEventService.SetApplicationEventHandler(AppEvent);

  // 增加一个键盘事件的监听 这是因为fmx.jar中的BUG引发的,原本应该是不需要这个监听的
  FKSListener := TKSCListener.Create(Self);
  MainActivity.getVirtualKeyboard.addOnKeyboardStateChangedListener
    (FKSListener);
  FWindow := TAndroidHelper.Activity.getWindow;
  FSystemUiVisibility := 0;
  FNavigationBarHeight := 0;
  FStatusBarHeight := 0;
end;

destructor TImmerse.Destroy;
begin
  {
    创建时增加了,现在需要注销的
  }
  MainActivity.getVirtualKeyboard.removeOnKeyboardStateChangedListener
    (FKSListener);
  FreeAndNil(FKSListener);

  inherited;
end;

procedure TImmerse.DoOnTopBotChanged;
begin
  if Assigned(FOnTopBotChanged) then
    FOnTopBotChanged(FStatusBarHeight, FNavigationBarHeight);
end;

function TImmerse.GetNavigationBarHeight: Integer;
var
  JStr: JString;
  resourceId: Integer;
  vStr: string;
  vNavigationBar: Boolean;
  RealValue: Integer;
  TempP: TPointF;
begin
  Result := 0;
  RealValue := 0;
  resourceId := TAndroidHelper.Context.getResources.getIdentifier
    (StringToJString('config_showNavigationBar'), StringToJString('bool'),
    StringToJString('android'));
  if (resourceId <> 0) then
  begin
    RealValue := 0;
    vNavigationBar := TAndroidHelper.Context.getResources.getBoolean
      (resourceId);
    if (not TOSVersion.Check(9)) then
    begin
      vStr := '';
      try
        JStr := TJSystemProperties.JavaClass.get
          (StringToJString('qemu.hw.mainkeys'));
        if not JStr.isEmpty then
          vStr := Trim(JStringToString(JStr));
      except
        vStr := '';
      end;
      if vStr <> '' then
        vNavigationBar := vStr = '0';
    end;
    if vNavigationBar then
    begin
      resourceId := TAndroidHelper.Context.getResources.getIdentifier
        (StringToJString('navigation_bar_height'), StringToJString('dimen'),
        StringToJString('android'));
      if resourceId <> 0 then
        RealValue := TAndroidHelper.Context.getResources.getDimensionPixelSize
          (resourceId);
    end;
  end;

  TempP.x := 0;
  TempP.y := RealValue;
  Result := Trunc(ConvertPixelToPoint(TempP).y);
end;

procedure TImmerse.RefreshSUIV;
begin
  {
    这里增加刷新,是使用delphi代码解决Delphi系统给安卓写的JAVA代码的BUG,
    这不是最好的解决方式,也算是一种解决方法。
    我自己肯定使用的是解决JAVA的BUG方式。用我自己修改后的fmx.jar
  }
  TThread.Synchronize(nil,
    procedure
    begin
      FWindow.getDecorView.setSystemUiVisibility(FSystemUiVisibility);
    end);
end;

procedure TImmerse.SetNavigationBarColor(const Value: TAlphaColor);
begin
  if FNavigationBarColor <> Value then
  begin
    FWindow.SetNavigationBarColor(TAndroidHelper.AlphaColorToJColor(Value));
    FNavigationBarColor := Value;
  end;
end;

procedure TImmerse.SetNavigationBarLight(const Value: TStatusBarLight);
begin
  if FNavigationBarLight <> Value then
  begin
    if TOSVersion.Check(8) then
    begin
      case Value of
        NoLight: // 黑色字体
          begin
            FSystemUiVisibility := FSystemUiVisibility And
              (not TJView.JavaClass.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
            FWindow.getDecorView.setSystemUiVisibility(FSystemUiVisibility);
          end;
        Light: // 浅色字体
          begin
            FSystemUiVisibility := FSystemUiVisibility or
              TJView.JavaClass.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
            FWindow.getDecorView.setSystemUiVisibility(FSystemUiVisibility);
          end;
      end;
    end;
    FNavigationBarLight := Value;
  end;
end;

procedure TImmerse.SetNavigationBarStatus(const Value: TNavigationBarStatus);
begin
  if FNavigationBarStatus <> Value then
  begin
    case Value of
      NoTransparent:
        begin
          FWindow.clearFlags
            (TJWindowManager_LayoutParams.JavaClass.
            FLAG_TRANSLUCENT_NAVIGATION);
          FNavigationBarHeight := 0;
          if FStatusBarStatus = TStatusBarStatus.default then
            FStatusBarHeight := 0;
        end;
      IsTransparent:
        begin
          FWindow.addFlags(TJWindowManager_LayoutParams.JavaClass.
            FLAG_TRANSLUCENT_NAVIGATION);
          FNavigationBarHeight := GetNavigationBarHeight;
          if FStatusBarStatus = TStatusBarStatus.default then
            FStatusBarHeight :=
              Trunc(PlatformAndroid.WindowService.StatusBarHeight);
        end;
    end;
    // 回调去处理页面的显示
    DoOnTopBotChanged;
    FNavigationBarStatus := Value;
  end;
end;

procedure TImmerse.SetStatusBarColor(const Value: TAlphaColor);
begin
  if FStatusBarColor <> Value then
  begin
    FWindow.SetStatusBarColor(TAndroidHelper.AlphaColorToJColor(Value));
    FStatusBarColor := Value;
  end;
end;

procedure TImmerse.SetStatusBarLight(const Value: TStatusBarLight);
begin
  if FStatusBarLight <> Value then
  begin
    if TOSVersion.Check(6) then
    begin
      case Value of
        NoLight: // 黑色字体
          begin
            FSystemUiVisibility := FSystemUiVisibility And
              (not TJView.JavaClass.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            FWindow.getDecorView.setSystemUiVisibility(FSystemUiVisibility);
          end;
        Light: // 浅色字体
          begin
            FSystemUiVisibility := FSystemUiVisibility or
              TJView.JavaClass.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            FWindow.getDecorView.setSystemUiVisibility(FSystemUiVisibility);
          end;
      end;
    end;
    FStatusBarLight := Value;
  end;
end;

procedure TImmerse.SetStatusBarStatus(const Value: TStatusBarStatus);
begin
  if FStatusBarStatus <> Value then
  begin
    case Value of
      default: // 默认方式
        begin
          TThread.Synchronize(nil,
            procedure
            begin
              FSystemUiVisibility := FSystemUiVisibility and
                (not TJView.JavaClass.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) and
                (not TJView.JavaClass.SYSTEM_UI_FLAG_LAYOUT_STABLE);

              FWindow.clearFlags(TJWindowManager_LayoutParams.JavaClass.
                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
              FWindow.clearFlags(TJWindowManager_LayoutParams.JavaClass.
                FLAG_TRANSLUCENT_STATUS);
              FWindow.getDecorView().setSystemUiVisibility(FSystemUiVisibility);
              if FNavigationBarStatus = TNavigationBarStatus.IsTransparent then
                FStatusBarHeight :=
                  Trunc(PlatformAndroid.WindowService.StatusBarHeight)
              else
                FStatusBarHeight := 0;

            end);
        end;
      translucent: // 半透明
        begin
          TThread.Synchronize(nil,
            procedure
            begin
              FSystemUiVisibility := FSystemUiVisibility or
                (TJView.JavaClass.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) or
                (TJView.JavaClass.SYSTEM_UI_FLAG_LAYOUT_STABLE);

              FWindow.addFlags(TJWindowManager_LayoutParams.JavaClass.
                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
              FWindow.addFlags(TJWindowManager_LayoutParams.JavaClass.
                FLAG_TRANSLUCENT_STATUS);
              FWindow.getDecorView().setSystemUiVisibility(FSystemUiVisibility);

              FStatusBarHeight :=
                Trunc(PlatformAndroid.WindowService.StatusBarHeight);

            end);
        end;
      transparent: // 透明
        begin
          TThread.Synchronize(nil,
            procedure
            begin
              FSystemUiVisibility := FSystemUiVisibility or
                (TJView.JavaClass.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) or
                (TJView.JavaClass.SYSTEM_UI_FLAG_LAYOUT_STABLE);

              FWindow.addFlags(TJWindowManager_LayoutParams.JavaClass.
                FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
              FWindow.clearFlags(TJWindowManager_LayoutParams.JavaClass.
                FLAG_TRANSLUCENT_STATUS);

              FWindow.SetStatusBarColor(TJColor.JavaClass.transparent);
              FStatusBarHeight :=
                Trunc(PlatformAndroid.WindowService.StatusBarHeight);

              FWindow.getDecorView().setSystemUiVisibility(FSystemUiVisibility);
            end);
        end;
    end;
    // 回调去处理页面的显示
    DoOnTopBotChanged;
    FStatusBarStatus := Value;
  end;

end;

{ TKSCListener }

constructor TKSCListener.Create(const AImmerse: TImmerse);
begin
  inherited Create;
  FImmerse := AImmerse;
end;

procedure TKSCListener.onVirtualKeyboardFrameChanged(newFrame: JRect);
begin
  // 这里什么都不用做 虽然也会触发这个事件,但他在后面才执行。
end;

procedure TKSCListener.onVirtualKeyboardWillHidden;
begin
  FImmerse.RefreshSUIV;
end;

procedure TKSCListener.onVirtualKeyboardWillShown;
begin
  FImmerse.RefreshSUIV;
end;

end.

【第二步】:调用单元实现功能
首先在APP第一个启动界面做如下设置,我的APP第一个启动是DataModule,所以我这里调用就在DataModule进行。

1.先将上面的单元创建保存,并进行引用
2.进行变量的定义

  private
    { Private declarations }
    vImmerse: TImmerse;

3.在DataModule创建时,进行功能调用,代码如下:

procedure TFData.DataModuleCreate(Sender: TObject);
begin
       vImmerse := TImmerse.Create;
       vImmerse.StatusBarStatus:=TStatusBarStatus.transparent;   //设置状态栏透明
       vImmerse.NavigationBarStatus:=TNavigationBarStatus.IsTransparent;
end;

整个APP只需要在启动界面做上面的三步就可以了,其它界面不需要重复做。有了上面这三步,功能基本就实现了,但我们会发现界面顶部与底部都有被遮挡的问题,这个简单,后面我们所有创建的FORM,都把最上面的控件和最下面的控件进行Margin设定就可以了。

比如上面图片的界面,我把最上面的ToolBar的Margin TOP设置为35,底部TabControl的Margin Buttom设定为15,显示就刚刚好,不是吗?
在这里插入图片描述

好了,到这里基本功能就正常实现了,这种手把手的教程我想大家应该都能看懂,希望对大家有用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Delphi是一种广泛使用的集成开发环境(IDE),用于创建Windows应用程序。在Delphi,可以通过代码控制和定制应用程序的状态栏。 要在Delphi沉浸状态栏,可以按照以下步骤进行: 1. 在Delphi创建一个新的窗体(Form)或打开现有的窗体。 2. 在设计模下,双击状态栏(StatusBar)控件,以显示状态栏的代码编辑器。 3. 在代码编辑器,找到控制状态栏显示的属性,并将其设置为“False”。例如,可以将StatusBar的Visible属性设置为False,或者在OnCreate事件添加代码StatusBar.Visible := False。 4. 当状态栏被隐藏后,窗体的其余部分会被自动扩展到填充状态栏的位置。可以调整其他控件的位置和大小,以确保它们不会被隐藏或错位。 5. 根据需求进一步调整窗体的布局和设计,以适应没有状态栏的显示方。 通过上述步骤,我们可以将Delphi应用程序的状态栏沉浸,使其不可见。这样可以腾出更多的屏幕空间来展示应用程序的内容,提供更好的用户体验。当然,在需要显示状态信息的地方,我们可以通过其他途径(如在窗体上添加标签等)来显示相关的信息。 需要注意的是,沉浸状态栏的效果可能因操作系统和窗体样的不同而有所变化。因此,在使用Delphi实现沉浸状态栏时,需要进行兼容性测试和适配,以确保在不同环境下都能达到预期的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值