Для запуска приложения порой достаточно одного клика. Но иногда время запуска оказывается больше ожидаемых одной-двух секунд, и тогда у пользователя может возникнуть ощущение, что программа зависла и что-то пошло не так. Избежать этого можно, если сократить время до появления первого окна, которое называют заставкой (англ. splash screen).
Для этого все действия по подготовке программы к работе выполняются после отображения заставки. А сама заставка становится границей, отделяющей серые будни пользователя Windows от фантастического мира вашего приложения.
Визуальная часть
Заставка – это визитная карточка приложения. Единых правил по её оформлению нет и быть не может. И если при проектировании дизайна основного приложения необходимо придерживаться определенных правил и соглашений, то при отображении заставки можно себя не сдерживать и позволить себе проявление своих творческих дарований в полной мере: используйте яркие палитры или черно-белые скетчи, добавьте анимацию или сделайте заставку полупрозрачной, откажитесь от прямоугольной формы. Но, если вы привыкли к сдержанности и экономному использованию ресурсов, я могу предложить решение, которое для своей реализации использует скрипты и файл изображения.

В качестве фонового изображения используется картинка, которая находится в папке Images\Logo, поверх фона отображается название приложения. Название имеет черную тень, которая улучшает читабельность текста. В нижнем левом углу выводится номер версии программы. Так как время инициализации приложения зависит от производительности компьютера, то желательно отображать заставку в течение указанного в настройке времени, но не более чем.
Скрипты
Для создания формы-заставки используется функция Splash_Create(). Она возвращает созданную форму в качестве результата. Параметры создания вынесены в константы.
const
SPLASH_LOGO_WIDTH = 350; // Ширина изображения
SPLASH_LOGO_HEIGHT = 169; // Высота изображения
SPLASH_BORDER = 2; // толщина рамки вокруг изображения
SPLASH_BORDER_COLOR = clBlack; // цвет рамки
SPLASH_FILE_NAME = 'Images\Logo\logo_350x169.jpg'; // файл фонового изображения
SPLASH_FONT_SIZE = 28; // размер шрифта для названия
SPLASH_FONT_COLOR = clWhite; // цвет для названия
SPLASH_SHADOW_COLOR = clBlack; // цвет тени
SPLASH_VER_FONT_SIZE = 11; // размер шрифта для версии
SPLASH_VER_FONT_COLOR = clWhite; // цвет шрифта для версии
SPLASH_DELAY_MIN = 2000; // минимальное время отображения заставки;
// другие константы проекта
LABEL_SHADOW_SHIFT_DEF = 2; // сдвиг тени по умолчанию
SX_SHADOW = '_Shadow'; // тень
APP_NAME = 'Заставка'; // название проекта
APP_VERSION = '1.0'; // Версия проекта
function Splash_Create:TForm;
var
tmpImage:TImage;
tmpLabel:TLabel;
tmpImageFileName:string;
begin
Result := TForm.Create(Application);
with Result do
begin
Width := SPLASH_LOGO_WIDTH + SPLASH_BORDER*2;
Height := SPLASH_LOGO_HEIGHT + SPLASH_BORDER*2;
BorderStyle := bsNone;
Position := poScreenCenter;
Color := SPLASH_BORDER_COLOR;
StyleElements := 0;
end;
// добавить картинку
tmpImage := TImage.Create(Result);
with tmpImage do
begin
Parent := Result;
Width := SPLASH_LOGO_WIDTH;
Height := SPLASH_LOGO_HEIGHT;
Top := SPLASH_BORDER;
Left := SPLASH_BORDER;
tmpImageFileName := ExtractFilePath(Application.ExeName)+SPLASH_FILE_NAME;
if not FileExists(tmpImageFileName) then
RaiseException('Splash_Create() Не найден файл '+tmpImageFileName)
else
Picture.LoadFromFile(tmpImageFileName);
end;
// добавить название
tmpLabel := TLabel.Create(Result);
with tmpLabel do
begin
Name := 'AppName';
Parent := Result;
Top := 0;
Left := 0;
Autosize := False;
Width := Result.ClientWidth;
Height := Result.ClientHeight;
Alignment := taCenter;
Layout := tlCenter;
WordWrap := True;
StyleElements := 0;
Caption := APP_NAME;
Font.Size := SPLASH_FONT_SIZE;
Font.Color := SPLASH_FONT_COLOR
end;
Label_AddShadow( tmpLabel, SPLASH_SHADOW_COLOR ); // добавить тень
// добавить номер версии
tmpLabel := TLabel.Create(Result);
with tmpLabel do
begin
Name := 'AppVer';
Parent := Result;
Caption := APP_VERSION;
StyleElements := 0;
Font.Size := SPLASH_VER_FONT_SIZE;
Font.Color := SPLASH_VER_FONT_COLOR;
Left := Height div 2;
Top := Result.ClientHeight - Height - (Height div 2);
end;
Label_AddShadow( tmpLabel, SPLASH_SHADOW_COLOR, 1 ); // добавить тень
end;
Code language: Delphi (delphi)
Давайте посмотрим на него внимательно. В шестьдесят третьей строке вызывается процедура Label_AddShadow(), которая добавляет тень для указанного текста.
Тень – это точная копия компонента-метки, обычно имеющая черный цвет текста (любо заданный цвет, темнее основного). Она располагается с небольшим смещением позади заданной метки. По непонятным причинам в MVDB метод Assign() для компонента TLabel не поддерживается, поэтому для получения копии пришлось писать кучу кода:
procedure Label_AddShadow( ALabel:TLabel; AColor:TColor = 0; AShift: integer = 0 );
var
tmpLabel:TLabel;
begin
if AShift = 0 then // если сдвиг тени не указан, то берем значение по умолчанию
AShift := LABEL_SHADOW_SHIFT_DEF;
tmpLabel := TLabel.Create( ALabel.Owner );
// tmpLabel.Assign( ALabel ); // К сожалению, в MVDB данный метод не работает :(, поэтому прийдется скопировать все свойства вручную....
// все, что ниже, можно заменить одной командой Assign, но в MVD она не работает...
tmpLabel.Parent := ALabel.Parent;
tmpLabel.Name := ALabel.Name + SX_SHADOW;
tmpLabel.Caption := ALabel.Caption;
tmpLabel.AutoSize := ALabel.AutoSize;
tmpLabel.Width := ALabel.Width;
tmpLabel.Height := ALabel.Height;
tmpLabel.Anchors := ALabel.Anchors;
tmpLabel.Font := ALabel.Font;
tmpLabel.StyleElements := ALabel.StyleElements;
tmpLabel.Alignment := ALabel.Alignment;
tmpLabel.Layout := ALabel.Layout;
tmpLabel.WordWrap := ALabel.WordWrap;
// задать смещение тени
tmpLabel.Top := ALabel.Top + AShift;
tmpLabel.Left := ALabel.Left + AShift;
// задать цвет тени
tmpLabel.Font.Color := AColor;
ALabel.BringToFront; // поместить главную надпись перед тенью
end;
Code language: Delphi (delphi)
Инициализация и запуск
Перед тем, как главная форма приложения frmMain (её название на закладке визуального конструктора отмечается красным цветом) отобразится, происходит событие OnShow, которое мы будем использовать для создания и отображения заставки, а также выполнения действий по инициализации приложения.
procedure frmMain_OnShow (Sender: TObject; Action: string);
// отображение главной формы
begin
// отобразить заставку
App_Splash_Start;
end;
Code language: Delphi (delphi)
Для инициализации приложения с отображением заставки вызывается процедура App_Splash_Start(), в которой создается и отображается форма – заставка, а затем вызывается процедура инициализации приложения App_Start(). После её завершения производится замер затраченного на инициализацию время и, если оно не превышает заданного, то добавляется задержка ровно на столько, чтобы суммарное время отображения заставки соответствовало заданному в константе SPLASH_DELAY_MIN. После чего заставка уничтожается, затем отображается основная форма frmMain.
procedure App_Splash_Start;
// инициализация с заставкой
var
tmpForm:TForm;
tmpStartTime: TTime;
tmpSleepTime: integer;
begin
tmpForm := Splash_Create;
tmpForm.Show;
Application.ProcessMessages;
tmpStartTime := Time();
App_Start;
// гарантированная задержка, но не более указанной в SPLASH_DELAY_MIN
tmpSleepTime := SPLASH_DELAY_MIN - Trunc( (Time() - tmpStartTime)*24*60*60*1000 );
if tmpSleepTime > 0 then
Sleep(tmpSleepTime);
tmpForm.Close;
tmpForm.Free;
end;
Code language: Delphi (delphi)
В App_Start() происходит инициализация подсистем приложения: управление картинками кнопок, стилями, горячими клавишами и т.д..
procedure App_Start;
// запуск приложения
begin
// тут должна быть всяческая инициализация, которая может занять продолжительное время
end;
Code language: Delphi (delphi)
Таким образом, в базовом комплекте будут две процедуры инициализации: с отображением заставки и без её отображения. Это позволит гибко подходить к выбору оптимального решения. Для лёгких приложений можно вызывать App_Start(), а если инициализация долгая, то лучше использовать App_Splash_Start();
Если вы хотите использовать статическую заставку, созданную в визуальном конструкторе My Visual Database, то откорректируйте App_Splash_Start(), вызывая в ней вашу статическую форму frmSplash:
procedure App_Splash_Start;
// инициализация с заставкой
var
tmpStartTime: TTime;
tmpSleepTime: integer;
begin
frmSplash.Show;
Application.ProcessMessages;
tmpStartTime := Time();
App_Start;
// гарантированная задержка, но не более указанной в SPLASH_DELAY_MIN
tmpSleepTime := SPLASH_DELAY_MIN - Trunc( (Time() - tmpStartTime)*24*60*60*1000 );
if tmpSleepTime > 0 then
Sleep(tmpSleepTime);
frmSplash.Hide;
end;
Code language: Delphi (delphi)
Ссылки
Послесловие
К сожалению, предложенное решение не является эффективным на 100%, и в некоторых случаях задержка между кликом по иконке запуска приложения в проводнике операционной системы и появлением заставки может составлять несколько секунд. Почему так происходит? Для ответа на этот вопрос нужно разобрать последовательность событий и тайминги.
- Клик по иконке приложения в проводнике
- Загрузка приложения в память
- Создание служебных форм приложения
- Считывание файла forms.xml и создание пользовательских форм приложения
- Запуск секции begin end. в файле script.pas и других модулях.
- Если используется встроенная система прав, то отображается встроенная форма авторизации
- Соединение с базой данных.
- Подключение данных к визуальным компонентам.
- Отображение главной формы приложения
Несмотря на солидный размер исполняемого файла (19Мб), основное время тратится не на его загрузку в память, а на создание пользовательских форм (п.4) и подключение данных к визуальным компонентам (п.8). Переместив создание и отображение формы заставки в п.5, можно исключить эту задержку, но справиться с п.4 не представляется возможным. А при большом числе форм и компонентов на формах время создания может исчисляться несколькими секундами, что я наблюдаю в некоторых своих проектах.
Мне видится три пути сокращения времени задержки отображения заставки.
Отдельное приложение
Простое решение, для реализации которого потребуется две папки: папка основного проекта и папка проекта запуска (стартер). Несмотря на громоздкость (решение будет включать два одинаковых исполняемых файла по 19Мб каждый плюс кучу служебных файлов для каждого проекта), оно вполне эффективно справляется с задачей: приложение стартер содержит только одну форму (заставку) запускается быстро и запускает основное приложение. А после обнаружения отображения формы основного приложения самоуничтожается.
Динамические формы
Сложное с точки зрения реализации и развития проекта решение, которое предполагает программное создание форм, что снижает эффективность визуальной среды разработки, превращая её в не визуальную. На практике можно комбинировать визуальное и программное создание формы и компонентов, реализуя повторяющиеся стандартные решения с помощью скрипта.
Доработка My Visual Database
Для этого в MVDB нужно добавить возможность написания обработчика на событие, возникающее до создания пользовательских форм. Но в настоящий момент развитие проекта приостановлено на неопределённый срок