понедельник, 3 февраля 2014 г.

Создаём кастомное окно для вывода сообщений


Если вам не нравится, как выглядит стандартный MessageBox или просто захотелось немного оригинальности в своём приложение, то можно создать кастомный MessageBox. В этой заметке я расскажу, как это сделать.

UPD. Добавляем плавность и тень, используем TFloatAnimation и TShadowEffect

Upd (21.04.14). Проверено на Delphi XE6







Для начала создадим пустое приложение.
На форму положим компонент TRectangle(Rectangle1) (Вкладка Shapes) и изменим свойства:
  • Выравнивание - Align: alContents
  • Прозрачность - Opacity: 0,5
  • Фон - Fill->Color: Black
  • Visible := False;
В итоге выглядеть это будет так:


Далее кладём на форму ещё один TRectangle, это будет наше кастомное окно. Т.к. мы используем компонент TRectangle, то и изменять можем очень много свойств (фон, прозрачность, закругление краёв и т.д. и т.п.) влияющих на отображение. На TRectangle можем поместить любые компоненты(Label, Edit и т.д.).

Приведу пример такого окна:
Выставляем у второго TRectangle(Rectangle2) свойства:
  • Align: alCenter
  • Height: 170
  • Width: 230
  • Fill->Color: White
  • Stroke->Kind: bkNone
  • Visible := False;
Далее кладём на Rectangle2 компоненты:

TToolBar, свойства по умолчанию.

Кладём TLabel на TToolBar, свойства:
  • Align: alCenter; 
  • AutoSize: True; 
  • Text: ‘Введите имя’;
TEdit, свойства:
  • Align: alVertCenter; 
  • Margins-Left: 10; 
  • Margins-Right: 10;
TLayout, свойства:
  • Align: alBottom
На TLayout, кладём две кнопки:
TSpeedButton (SpeedButton1):
  • StyleLookup: deletetoolbutton; 
  • Text: ‘Отмена’; 
  • Align: alLeft; 
  • Width: 100
TSpeedButton (SpeedButton2):
  • StyleLookup: donetoolbutton; 
  • Text: ‘Готово’; 
  • Align: alRight; 
  • Width: 100
Что получилось:

UPD. 
Постарался сделать (на глаз) плавность открытия такой же, как у стандартных MessageBox.

Начнём с тени окна.
Помещаем компонент TShadowEffect в Rectangle2, свойства для изменения:
  • Distance: 1
Готово, тень у окна есть.

Теперь плавное появление окна.
Чтобы создать ощущение плавного открытия, необходимо добавить эффект анимации (TFloatAnimation) к свойству TRectangle.Opacity.
Начнём с Rectangle1. Выбираем его на панели Structure. Находим свойство Opacity, жмём и выбираем «Create New TFloatAnimation» из выпадающего списка. Теперь меняем свойства у FloatAnimation1:
  • Duration: 0,2
  • StartValue: 0
  • StopValue: 0,5
  • PropertyName: Opacity
  • Все галочки сняты.
  • Trigger: IsVisible=true
Также создаём анимацию для Rectangle2, но со свойствами:
  • Duration: 0,2
  • StartValue: 0,8
  • StopValue: 1
  • PropertyName: Opacity
  • Все галочки сняты.
  • Trigger: IsVisible=true
Всё готово. Запускаем и любуемся результатом. (Внимание! Анимацию использовал впервые, всё проверил, вроде работает).

Примечание: Хотите больше плавности, меняйте значения Duration, StartValue, StopValue.

Код везде обновил.

Вопрос для читателей: Как добавить анимацию для закрытия окна?

Теперь код:
Чтобы вызвать наше окно, будем использовать свойство Visible у TRectangle. У меня на форме есть кнопка «ShowMessage» для неё создадим событие OnClick:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Rectangle1.Visible := True;
  Rectangle2.Visible := True;
end;

Кнопкам SpeedButton1 и SpeedButton2 также создадим событие OnClick:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  Rectangle2.Visible := False;
  Rectangle1.Visible := False;
end;

Теперь посмотрим, что получилось в итоге:
Версия без анимации

Версия с анимацией


Исходный код (версия без анимации): Скачать с Google Drive
Исходный код (версия с анимацией): Скачать с Google Drive

4 комментария:

  1. То есть окно не совсем настоящее :)

    На самом деле было бы здорово иметь какие-то готовые шаблоны подобных UI-элементов в составе FMX. На мобильных устройствах есть определенные подходы к проектированию интерфейсов вроде панелей, которые можно вытаскивать пальцем, или вот подобных "окон", которые в Delphi в принципе можно сделать, но это требует определенных объемов однообразной ручной работы.

    ОтветитьУдалить
  2. >>То есть окно не совсем настоящее :)
    Окно имеет смысл, если оно суть объект ОС для передачи сообщений и т.д. Если все рендерится зацело, то всё "не настоящее".

    @Author
    Анимашечку добавить.
    Если в дизайн-тайм, то TFloatAnimaton (несколько). Если в рантайм (кодом), то
    Rectangle1.AnimateFloat(...) по пропертям.

    Если поиграться типом интерполяции, то вообще будет кул "с продёргиванием". Попробуйте! Обязательно поставлю еще один лайк :)

    ОтветитьУдалить
  3. По идее, FloatAnimation1.Start; - не по-пацански. Крутые человеки используют в анимации триггер, типа "Rectangle2.Visible=True" Тогда нет нужды заботиться об анимации каждый раз, когда объект севетишь-гасишь. Так оно гораздо-гораздее. Советую поковырять этот вопрос. А мы посмотрим :)

    ОтветитьУдалить
    Ответы
    1. Про триггеры спасибо, упустил этот момент. Однако анимация на закрытие всё также не срабатывает, т.е. окно закрывается быстрее, чем успевает сработать анимация.

      Удалить