Важно (9.07.22)

Если картинки в постах не отображаются, зайдите в блог через прокси. РКН заблокировал поддомены blogger.com на которые загружались картинки.

вторник, 2 сентября 2014 г.

Как программно изменять вид окна приложения (скрыть статус бар) в XE6?

В этой заметке вы узнаете, как скрывать/отображать статус бар в Андроиде. В XE7 данная возможность уже реализована. Рассмотрено несколько вариантов, «статический» (рассмотрено два варианта, существует как минимум ещё один) и «динамический» (существует множество вариантов, я рассмотрел один, который кажется мне наиболее правильным).



Прочитал одну тему на форуме Ярослава, в которой просили готовый код для скрытия статус бара в рантайме. Готового кода ни у кого не оказалось и ребята вроде начали экспериментировать сами, при этом крича на форуме, что ни чего не получается и уже решили, что такое можно сделать только при помощи отдельного Java-класса. На мою просьбу, объяснить, откуда такие выводы, какой код пробовали и какие ошибки получали, ни чего не ответили, ну значит, так нужна помощь…
В то же время стало самому интересно, что там такого сложного могло возникнуть… 
Результат получился в виде заметки :)

Все действия, выполнялись и проверялись с использованием:
  • Samsung Galaxy S2
  • Android 4.1.2
  • Delphi XE6 Update 1

Приступим.

Первый вариант – Статический.
Допустим ситуацию: Приложение должно всегда работать в полноэкранном режиме.
Решение:
Заходим в меню "Project->Options->Version Info(Android)" (Для тех, кто не знает, я уже описывал это окно тут), там находим параметр «theme» и выставляем у него значение «No TitleBar». Всё, теперь ваше приложение всегда будет работать в полноэкранном режиме.

Второй вариант – Статический.
Если по какой-то причине не сработал первый вариант, то можно сделать то же самое при помощи кода.
Заходим в меню Project->View Source, видим код
program Project1;

uses
  System.StartUpCopy,
  FMX.MobilePreview,
  FMX.Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Изменяем его вот так и компилим приложение. 
program Project1;

uses
  System.StartUpCopy,
  FMX.MobilePreview,
  FMX.Forms,
  FMX.Helpers.Android,
  Androidapi.JNI.GraphicsContentViewText,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  SharedActivity.getWindow.getDecorView().setSystemUiVisibility(TJView.JavaClass.SYSTEM_UI_FLAG_FULLSCREEN);
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Для управления настройками окна, существуют специальные флаги. Чтобы отобразить окно в полноэкранном режиме на Андроид 4.1.* необходимо воспользоваться флагом «SYSTEM_UI_FLAG_FULLSCREEN».

Всё, приложение отображается в полноэкранном режиме.

Третий вариант – Динамический.
Допустим ситуацию: Во время работы приложения необходимо изменять режим отображения.
Решение: Вариантов решения данной задачи много, но все они сводятся к использованию потоков, созданию нового потока или использованию уже существующих поток.

Важно понимать, что я не углублялся в детали и нашёл решение задачи очень быстро. В связи с этим, объясняю все, как понял это я.

Мы будем использовать код из второго варианта. Но как вы уже догадались, просто скопировать этот код в обработчик «OnClick» кнопки не получится. 
Результат нажатия на такую кнопку будет:

Проблема в том, что мы пытаемся внести изменения в другой поток.
Я предлагаю выполнить наш код в главном потоке UI - runOnUiThread (Runnable action)
Для того чтобы это сделать, необходимо объявить новый класс типа JRunnable
Код ниже я нашёл в исходниках (внёс некоторые изменения):
type
  TRunner = class(TJavaLocal, JRunnable)
  private
    FRunMethod: TThreadProcedure;
  public
    constructor Create(RunMethod: TThreadProcedure); overload;
    procedure run; cdecl;
  end;

constructor TRunner.Create(RunMethod: TThreadProcedure);
begin
  FRunMethod := RunMethod;
  Create;
end;

procedure TRunner.run; cdecl;
begin
  FRunMethod;
end;

Теперь пишем обработчик для кнопки:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Runner := TRunner.Create(
    procedure
    begin
      SharedActivity.getWindow.getDecorView().setSystemUiVisibility
        (TJView.JavaClass.SYSTEM_UI_FLAG_FULLSCREEN);
    end);

  SharedActivity.runOnUiThread(Runner);
  sleep(400);
  Form1.Recreate;
end;

В конце обработчика, ждём (300-400 мс), пока свернётся статус бар, и пересоздаём форму.
Единственное, что меня смущает – это слип и пересоздание формы, возможно, есть варианты лучше, но мне они пока не известны, да и не думал я особо :)

Чтобы отобразить статус бар, вместо флага пишем «0».

Вот и всё. Варианты, описанные выше, не претендуют на идеальность! Знаете способы лучше, пишите в комменты.

Интересно посмотреть, как это сделано в XE7, если есть у кого-то возможность, скиньте кусок кода, пожалуйста.