Интерфейс в яве на пальцах. Выбор библиотеки для создания графического интерфейса Java. Внешний вид приложения

Мы недавно видели, что переходы - это просто способ анимации стилевых свойств от исходного до конечного состояния.

Итак, переходы в CSS являются специфическим видом анимации, где:

  • есть только два состояния: начало и конец;
  • анимация не зациклена;
  • промежуточные состояния управляются только функцией времени.

Но что если вы хотите:

  • иметь контроль над промежуточными состояниями?
  • зациклить анимацию?
  • сделать разные виды анимаций для одного элемента?
  • анимировать определённое свойство только на половину пути?
  • имитировать различные функции времени для разных свойств?

Анимация в CSS позволяет всё это, и не только.

Анимация как мини-фильм, где вы в качестве режиссёра даёте инструкции (стилевые правила) вашим актёрам (элементам HTML) для разных сцен (ключевые кадры).

Свойства анимации

Как и transition , свойство animation является сокращённым для нескольких других:

  • animation-name : название анимации;
  • animation-duration : как долго длится анимация;
  • animation-timing-function : как вычисляются промежуточные состояния;
  • animation-delay : анимация начинается спустя некоторое время;
  • animation-iteration-count : сколько раз должна выполняться анимация;
  • animation-direction : должно движение идти в обратную сторону или нет;
  • animation-fill-mode : какие стили применяются до начала анимации и после её завершения.

Быстрый пример

Для оживления кнопки загрузки, вы можете написать анимацию подпрыгивания :

@keyframes bouncing{ 0% { bottom: 0; box-shadow: 0 0 5px rgba(0,0,0,0.5); } 100% { bottom: 50px; box-shadow: 0 50px 50px rgba(0,0,0,0.1); } } .loading-button { animation: bouncing 0.5s cubic-bezier(0.1,0.25,0.1,1) 0s infinite alternate both; }

Сначала нужно написать реальную анимацию подпрыгивания с помощью @keyframes и назвать её .

Я использовал сокращенное свойство animation и включил все возможные варианты:

  • animation-name: bouncing (совпадает с названием ключевых кадров)
  • animation-duration: 0.5s (полсекунды)
  • animation-timing-function: cubic-bezier(0.1,0.25,0.1,1)
  • animation-delay: 0s (без задержки)
  • animation-iteration-count: infinite (воспроизводится бесконечно)
  • animation-direction: alternate (идёт назад и вперёд)
  • animation-fill-mode: both

@keyframes

Перед применением анимации к элементам HTML, вам требуется написать анимацию с помощью ключевых кадров . Вообще, ключевые кадры - это каждый промежуточный шаг в анимации. Они определяются с помощью процентов:

  • 0% - первый шаг анимации;
  • 50% - шаг на полпути в анимации;
  • 100% - последний шаг.

Можно также использовать ключевые слова from и to вместо 0% и 100%, соответственно.

Вы можете определить столько ключевых кадров, сколько хотите, вроде 33%, 4% или даже 29.86%. На практике вы будете писать только некоторые из них.

Каждый ключевой кадр является правилом CSS , это означает, что вы можете писать свойства CSS как обычно.

Чтобы определить анимацию, просто напишите ключевое слово @keyframes с его названием :

@keyframes around { 0% { left: 0; top: 0; } 25% { left: 240px; top: 0; } 50% { left: 240px; top: 140px; } 75% { left: 0; top: 140px; } 100% { left: 0; top: 0; } } p { animation: around 4s linear infinite; }

Обратите внимание, что начало 0% и конец 100% содержат одинаковые правила CSS. Это гарантирует, что анимация зациклится идеально. Поскольку счётчик итераций установлен как infinite , то анимация будет идти от 0% до 100%, а затем немедленно обратно к 0% и так бесконечно.

animation-name

Название анимации используется, по крайней мере, дважды :

  • при написании анимации с помощью @keframes ;
  • при использовании анимации с помощью свойства animation-name (или через сокращённое свойство animation ).
@keyframes whatever { /* ... */ } .selector { animation-name: whatever; }

Подобно именам классов CSS, название анимации может включать в себя только:

  • буквы (a-z);
  • цифры (0-9);
  • подчёркивание (_);
  • дефис (-).

Название не может начинаться с цифры или с двух дефисов.

animation-duration

Как и длительность перехода , длительность анимации может быть установлена в секундах (1s) или миллисекундах (200ms).

Selector { animation-duration: 0.5s; }

Значение по умолчанию равно 0s, что означает отсутствие анимации вообще.

animation-timing-function

Подобно функциям времени для переходов , функции времени для анимации могут использовать ключевые слова , такие как linear , ease-out или могут быть определены с помощью произвольных кривых Безье .

Selector { animation-timing-function: ease-in-out; }

Значение по умолчанию: ease .

Поскольку анимация в CSS использует ключевые кадры, вы можете установить линейную функцию времени и моделировать конкретную кривую Безье, определяя множество очень специфичных ключевых кадров. Посмотрите Bounce.js для создания передовой анимации.

animation-delay

Как и с задержкой перехода , задержка анимации может быть установлена в секундах (1s) или миллисекундах (200ms).

По умолчанию равно 0s, что означает отсутствие любой задержки.

Полезно использовать, когда включается несколько анимаций в серии .

A, .b, .c { animation: bouncing 1s; } .b { animation-delay: 0.25s; } .c { animation-delay: 0.5s; }

animation-iteration-count

По умолчанию, анимация воспроизводится только один раз (значение 1). Вы можете установить три типа значений:

  • целые числа, вроде 2 или 3;
  • дробные числа, вроде 0.5, которые будут воспроизводить только половину анимации;
  • ключевое слово infinite , которое будет повторять анимацию бесконечно.
.selector { animation-iteration-count: infinite; }

animation-direction

Свойство animation-direction определяет, в каком порядке читаются ключевые кадры.

  • normal : начинается с 0%, заканчивается на 100%, начинается с 0% снова.
  • reverse : начинается со 100%, заканчивается на 0%, начинается со 100% снова.
  • alternate : начинается с 0%, идёт до 100%, возвращается на 0%.
  • alternate-reverse : начинается со 100%, идёт до 0%, возвращается на 100%.

Это легче представить, если счётчик итераций анимации установлен как infinite .

animation-fill-mode

Свойство animation-fill-mode определяет, что происходит перед началом анимации и после её завершения.

При определении ключевых кадров можно указать правила CSS , которые будут применяться на разных шагах анимации. Теперь эти правила CSS могут столкнуться с теми, которые уже применяются к анимируемым элементам.

animation-fill-mode позволяет сообщить браузеру, если стили анимации также должны применяться за пределами анимации .

Давайте представим себе кнопку , которая является:

  • красной по умолчанию;
  • становится синей в начале анимации;
  • и в итоге зелёной , когда анимация завершена.

Это экспериментальная технология
Так как спецификация этой технологии ещё не стабилизировалась, смотрите по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

CSS анимации позволяют анимировать переходы от одних конфигурации CSS стилей к другим. CSS-анимации состоят из двух компонентов: стилевое описание анимации и набор ключей, определяющих начальное, конечное и, возможно, промежуточное состояние анимируемых стилей.

Есть три преимущества CSS-анимации перед традиционными способами:

  1. Простота использования для простых анимации; Вы можете создать анимацию не зная JavaScript.
  2. Анимации будут хорошо работать даже при умеренных нагрузках системы. Простые анимации на JavaScript, если они плохо написаны, часто выполняются плохо. Движок может использовать frame-skipping и другие техники чтобы сохранить производительность на таком высоком уровне.
  3. Позволяет браузеру контролировать последовательность анимации, тем самым оптимизируя производительность и эффективность браузера. Например, уменьшая частоту обновления кадров анимации в непросматриваемых в данный момент вкладках.

Конфигурирование анимации

p { animation-duration: 3s; animation-name: slidein; } @keyframes slidein { from { margin-left: 100%; width: 300%; } to { margin-left: 0%; width: 100%; } }

В стиле для элемента определяет собой абзац текста.">

с помощью свойства animation-duration указано, что исполнение анимации от начала до конца должно занять 3 с, и что имя для @keyframes , описывающей саму анимацию, определено как "slidein".

В элемент определяет собой абзац текста.">

можно добавлять и другие пользовательские стили, чтобы как-то украсить его, однако здесь мы хотели продемонстрировать только эффект анимации.

Ключи определяются с помощью правила @keyframes . В данном случае мы имеем только два ключа. Первый при 0% анимации (from). Здесь мы придаем элементу левый отступ в 100% и ширину в 300% (в три раза больше ширины родительского элемента). Это становится причиной того, что при первом кадре анимации заголовок определяет собой абзац текста.">

находится за пределами правого края окна браузера.

Второй ключ (to) определяет конец анимации, т.е (100%). Левый отступ устанавливается равным 0, а ширина 100%. Все выглядит будто заголовок определяет собой абзац текста.">

приплывает к левому краю окна браузера.

Добавление других ключевых слов

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

75% { font-size: 300%; margin-left: 25%; width: 150%; } p { animation-duration: 3s; animation-name: slidein; } @keyframes slidein { from { margin-left: 100%; width: 300%; } to { margin-left: 0%; width: 100%; } 75% { font-size: 300%; margin-left: 25%; width: 150%; } }

The Caterpillar and Alice looked at each other for some time in silence: at last the Caterpillar took the hookah out of its mouth, and addressed her in a languid, sleepy voice.

Это говорит браузеру о том, что при 75% выполнения анимации, шрифт должен быть 300%, а ширина 150%.

(Обновите страницу, чтобы увидеть анимацию, или щелкните по кнопке CodePen, чтобы воспроизвести ее в окне CodePen)

Настройка повторения

Чтобы настроить повторение, нужно добавить свойство animation-iteration-count и придать ему значение, равное нужному количеству повторений анимаций. В данном случае давайте установим значение infinite для бесконечного повторения:

P { animation-duration: 3s; animation-name: slidein; animation-iteration-count: infinite; } @keyframes slidein { from { margin-left: 100%; width: 300%; } to { margin-left: 0%; width: 100%; } }

The Caterpillar and Alice looked at each other for some time in silence: at last the Caterpillar took the hookah out of its mouth, and addressed her in a languid, sleepy voice.

Движение текста вправо и влево

Итак, мы настроили повторение, Но есть кое-что странное. Текст при каждом повторении снова "запрыгивает" за край окна браузера. То, чего мы хотим, так это чтобы текст двигался влево и вправо. Этого легко достичь с помощью установки свойству значения alternate:

P { animation-duration: 3s; animation-name: slidein; animation-iteration-count: infinite; animation-direction: alternate; } @keyframes slidein { from { margin-left: 100%; width: 300%; } to { margin-left: 0%; width: 100%; } }

The Caterpillar and Alice looked at each other for some time in silence: at last the Caterpillar took the hookah out of its mouth, and addressed her in a languid, sleepy voice.

Использование шорткодов

Шорткод animation полезен для экономии места в коде. Например, правило, которое мы используем в этой статье:

P { animation-duration: 3s; animation-name: slidein; animation-iteration-count: infinite; animation-direction:alternate; }

можно заменить на:

P { animation: 3s infinite alternate slidein; }

Установка нескольких значений свойствам анимации

CSS cвойство анимации может иметь несколько значений, разделенных запятыми, это используется, чтобы указать несколько значений анимации в одном правиле и установить разную продолжительность, число повторений, и т.д., для различных анимаций. Рассмотрим несколько примеров, чтобы увидеть разницу:

В первом примере, у свойства имени анимации установлены три значения, у свойств продолжительности и количества повторений - по одному. В этом случае у всех трех анимаций одинаковая продолжительность и число повторений:

Animation-name: fadeInOut, moveLeft300px, bounce; animation-duration: 3s; animation-iteration-count: 1;

Во втором примере установлены три значения для каждого из свойств. В этом случае каждая анимация выполняется с соответствующими по порядку значениями в каждом свойстве, так, например, fadeInOut имеет продолжительность 2.5s и количество повторений 2, и т.д.

Animation-name: fadeInOut, moveLeft300px, bounce; animation-duration: 2.5s, 5s, 1s; animation-iteration-count: 2, 1, 5;

В третьем примере определены три значения имени анимации, но два значения продолжительности и количества повторений. В случае, когда количества значений недостаточно для каждой анимации, значения берутся циклически от начала до конца. Например, у fadeInOut длительность будет 2.5s, а moveLeft300px - 5s. Значения продолжительности закончились, теперь они берутся сначала - bounce получит продолжительность 2.5s. Значение количества повторений (а также другие указанные свойства) будет определено таким же образом.

Animation-name: fadeInOut, moveLeft300px, bounce; animation-duration: 2.5s, 5s; animation-iteration-count: 2, 1;

Использование событий анимации

Вы можете получить дополнительный контроль над анимацией, а также полезную информацию о ней, с помощью событий анимации . Эти события, представленные объектом AnimationEvent , можно использовать, чтобы определить, когда начинается и заканчивается анимация или начинается новая итерация. Каждое событие содержит момент времени, когда оно произошло, а также имя анимации, которая вызвала событие.

Мы будем модифицировать текст, чтобы выводить некоторую информацию о каждом событии анимации. Так мы сможем увидеть как она работает.

Добавление CSS

Начнем с добавления CSS. Анимация будет длиться 3 с., будет называться "slidein", будет повторяться 3 раза, а также значение animation-direction установлено alternate . В ключах @keyframes установлены такие значения ширины и левого отступ, что элемент будет скользить по экрану.

Slidein { -moz-animation-duration: 3s; -webkit-animation-duration: 3s; animation-duration: 3s; -moz-animation-name: slidein; -webkit-animation-name: slidein; animation-name: slidein; -moz-animation-iteration-count: 3; -webkit-animation-iteration-count: 3; animation-iteration-count: 3; -moz-animation-direction: alternate; -webkit-animation-direction: alternate; animation-direction: alternate; } @-moz-keyframes slidein { from { margin-left:100%; width:300% } to { margin-left:0%; width:100%; } } @-webkit-keyframes slidein { from { margin-left:100%; width:300% } to { margin-left:0%; width:100%; } } @keyframes slidein { from { margin-left:100%; width:300% } to { margin-left:0%; width:100%; } }

Добавление обработчика событии анимации

Будем использовать JavaScript для отслеживания всех трех возможных событий анимации. Следующий код конфигурирует обработчик; мы вызываем его при первой загрузке документа.

Var e = document.getElementById("watchme"); e.addEventListener("animationstart", listener, false); e.addEventListener("animationend", listener, false); e.addEventListener("animationiteration", listener, false); e.className = "slidein";

Это довольно стандартный код; Вы можете получить дополнительную информацию в документации element.addEventListener() . Последнее что делает этот код это установка класса "slidein" для анимируемого элемента; мы делаем это чтобы запустить анимацию.

Почему? Потому что в нашем случае событие animationstart происходит как только анимация стартует, и это происходит раньше чем исполняется наш сценарий. Так мы сможем контролировать начало анимации сами посредством вставки класса "slidein" для анимируемого элемента.

Регистрация событий

События будут передаваться функции listener() , показанной ниже.

Function listener(e) { var l = document.createElement("li"); switch(e.type) { case "animationstart": l.innerHTML = "Started: elapsed time is " + e.elapsedTime; break; case "animationend": l.innerHTML = "Ended: elapsed time is " + e.elapsedTime; break; case "animationiteration": l.innerHTML = "New loop started at time " + e.elapsedTime; break; } document.getElementById("output").appendChild(l); }

Этот код также очень прост. Этот код следит за event.type , чтобы определить тип события и добавляет элемент

    чтобы залогировать произошедшее событие.

    Вывод, когда анимация закончится, будет выглядеть как-то так:

    • Started: elapsed time is 0
    • New loop started at time 3.01200008392334
    • New loop started at time 6.00600004196167
    • Ended: elapsed time is 9.234000205993652

    Обратите внимание, что время, указанное в выводе, и время, которое мы указали в стилях, не совпадют. Также обратите внимание, что после окончания итерации не посылается событие animationiteration ; вместо него посылается событие animationend .

    HTML

    Ради полноты картины приведем HTML. В разметке имеется тег ul в который и выводится вся информация:

    Watch me move

    This example shows how to use CSS animations to make p elements move across the page.

    In addition, we output some text each time an animation event fires, so you can see them in action.

    Пользовательский интерфейс на Java прошел весьма тернистый путь становления и развития. Долгое время его обвиняли в медленной работе, жадности к ресурсам системы, ограниченной функциональности. Появление.NET с более быстрыми графическими компонентами еще больше пошатнуло позиции Java. Но нет худа без добра - все эта движуха только подстегивала разработчиков Java к развитию и улучшению графических библиотек. Посмотрим, что из этого получилось.

    Abstract Window Toolkit

    AWT была первой попыткой Sun создать графический интерфейс для Java. Они пошли легким путем и просто сделали прослойку на Java, которая вызывает методы из библиотек, написанных на С. Библиотечные методы создают и используют графические компоненты операционной среды. С одной стороны, это хорошо, так как программа на Java похожа на остальные программы в рамках данной ОС. Но с другой стороны, нет никакой гарантии, что различия в размерах компонентов и шрифтах не испортят внешний вид программы при запуске ее на другой платформе. Кроме того, чтобы обеспечить мультиплатформенность, пришлось унифицировать интерфейсы вызовов компонентов, из-за чего их функциональность получилась немного урезанной. Да и набор компонентов получился довольно небольшой. К примеру, в AWT нет таблиц, а в кнопках не поддерживается отображение иконок.

    Использованные ресурсы AWT старается освобождать автоматически. Это немного усложняет архитектуру и влияет на производительность. Освоить AWT довольно просто, но написать что-то сложное будет несколько затруднительно. Сейчас ее используют разве что для апплетов.

    Достоинства:

    • часть JDK;
    • скорость работы;
    • графические компоненты похожи на стандартные.

    Недостатки:

    • использование нативных компонентов налагает ограничения на использование их свойств. Некоторые компоненты могут вообще не работать на «неродных» платформах;
    • некоторые свойства, такие как иконки и всплывающие подсказки, в AWT вообще отсутствуют;
    • стандартных компонентов AWT очень немного, программисту приходится реализовывать много кастомных;
    • программа выглядит по-разному на разных платформах (может быть кривоватой).

    заключение:

    В настоящее время AWT используется крайне редко - в основном в старых проектах и апплетах. Oracle припрятал обучалки и всячески поощряет переход на Swing. Оно и понятно, прямой доступ к компонентам оси может стать серьезной дырой в безопасности.

    Swing


    Вслед за AWT Sun разработала набор графических компонентов под названием Swing. Компоненты Swing полностью написаны на Java. Для отрисовки используется 2D, что принесло с собой сразу несколько преимуществ. Набор стандартных компонентов значительно превосходит AWT по разнообразию и функциональности. Стало легко создавать новые компоненты, наследуясь от существующих и рисуя все, что душе угодно. Стала возможной поддержка различных стилей и скинов. Вместе с тем скорость работы первых версий Swing оставляла желать лучшего. Некорректно написанная программа и вовсе могла повесить винду намертво.

    Тем не менее благодаря простоте использования, богатой документации и гибкости компонентов Swing стал, пожалуй, самым популярным графическим фреймворком в Java. На его базе появилось много расширений, таких как SwingX, JGoodies, которые значительно упрощают создание сложных пользовательских интерфейсов. Практически все популярные среды программирования Java включают графические редакторы для Swing-форм. Поэтому разобраться и начать использовать Swing не составит особого труда.

    Достоинства:

    • часть JDK, не нужно ставить дополнительных библиотек;
    • по Swing гораздо больше книжек и ответов на форумах. Все проблемы, особенно у начинающих, гуглу досконально известны;
    • встроенный редактор форм почти во всех средах разработки;
    • на базе свинга есть много расширений типа SwingX;
    • поддержка различных стилей (Look and feel).

    Недостатки:

    • окно с множеством компонентов начинает подтормаживать;
    • работа с менеджерами компоновки может стать настоящим кошмаром в сложных интерфейсах.

    Заключение:

    Swing жил, Swing жив, Swing будет жить. Хотя Oracle и старается продвигать JavaFX, на сегодняшний день Swing остается самым популярным фреймворком для создания пользовательских интерфейсов на Java.

    Standard Widget Toolkit


    Как
    выглядит
    SWT

    SWT был разработан в компании IBM в те времена, когда Swing еще был медленным, и сделано это было в основном для продвижения среды программирования Eclipse. SWT, как и AWT, использует компоненты операционной системы, но для каждой платформы у него созданы свои интерфейсы взаимодействия. Так что для каждой новой системы тебе придется поставлять отдельную JAR-библиотеку с подходящей версией SWT. Это позволило более полно использовать существующие функции компонентов на каждой оси. Недостающие функции и компоненты были реализованы с помощью 2D, как в Swing. У SWT есть много приверженцев, но, положа руку на сердце, нельзя не согласиться, что получилось не так все просто, как хотелось бы. Новичку придется затратить на изучение SWT намного больше времени, чем на знакомство с тем же Swing. Кроме того, SWT возлагает задачу освобождения ресурсов на программиста, в связи с чем ему нужно быть особенно внимательным при написании кода, чтобы случайное исключение не привело к утечкам памяти.

    Достоинства:

    • использует компоненты операционной системы - скорость выше;
    • Eclipse предоставляет визуальный редактор форм;
    • обширная документация и множество примеров;
    • возможно использование AWT- и Swing-компонентов.

    Недостатки:

    • для каждой платформы необходимо поставлять отдельную библиотеку;
    • нужно все время следить за использованием ресурсов и вовремя их освобождать;
    • сложная архитектура, навевающая суицидальные мысли после тщетных попыток реализовать кастомный интерфейс.

    Заключение:

    Видно, что в IBM старались. Но получилось уж очень на любителя…

    JavaFX


    JavaFX можно без преувеличения назвать прорывом. Для отрисовки используется графический конвейер, что значительно ускоряет работу приложения. Набор встроенных компонентов обширен, есть даже отдельные компоненты для отрисовки графиков. Реализована поддержка мультимедийного контента, множества эффектов отображения, анимации и даже мультитач. Внешний вид всех компонентов можно легко изменить с помощью CSS-стилей. И самое прекрасное - в JavaFX входит набор утилит, которые позволяют сделать родной инсталлятор для самых популярных платформ: exe или msi для Windows, deb или rpm для Linux, dmg для Mac. На сайте Oracle можно найти подробную документацию и огромное количество готовых примеров. Это превращает программирование с JavaFX в легкое и приятное занятие.

    Достоинства:

    • быстрая работа за счет графического конвейера;
    • множество различных компонентов;
    • поддержка стилей;
    • утилиты для создания установщика программы;
    • приложение можно запускать как десктопное и в браузере как часть страницы.

    Недостатки:

    • фреймворк еще разрабатывается, поэтому случаются и падения и некоторые глюки;
    • JavaFX пока не получил широкого распространения.

    Заключение:

    Хорошая работа, Oracle. Фреймворк оставляет только позитивные впечатления. Разобраться несложно, методы и интерфейсы выглядят логичными. Хочется пользоваться снова и снова!

    Визуальные библиотеки на практике

    SWT: погодный виджет

    Для демонстрации возможностей наиболее популярных графических библиотек и основных принципов работы с ними сделаем несколько небольших виджетов с отображением различной информации.

    И начнем, пожалуй, с самого популярного виджета - отображения текущей погоды, для реализации которого выберем SWT.

    Любая программа на SWT начинается с создания объекта Display. Он служит своеобразным контекстом приложения, который содержит необходимые методы для обращения к ресурсам системы и обеспечивает цикл событий. Следующим шагом будет создание не менее важного объекта Shell. Shell представляет собой обычное окно операционной системы. В конструктор shell передается Display, чтобы создать окно верхнего уровня.

    Display display = new Display(); shell = new Shell(display, SWT.NO_TRIM);

    Так как мы создаем виджет, нам не нужно отображать стандартное обрамление окна и кнопки управления, для этого мы указали флаг NO_TRIM. Для фона мы будем использовать картинку - прямоугольник с закругленными углами. В принципе, окно SWT может принимать любые формы. Чтобы добиться такого эффекта, используем класс Region. Все, что нужно, - добавить в этот класс все видимые точки из картинки фона, пропуская прозрачные.

    Загружаем картинку:

    Image image = new Image(display, "images/bg.png#26759185");

    В изображениях разных форматов прозрачность задается по-разному, поэтому и извлекается информация о прозрачных областях тоже не одинаково. Создаем область фона и добавляем туда все видимые точки:

    Region region = new Region(); ImageData imageData = image.getImageData(); if (imageData.alphaData != null) { Rectangle pixel = new Rectangle(0, 0, 1, 1); for (int y = 0; y < imageData.height; y++) { for (int x = 0; x < imageData.width; x++) { if (imageData.getAlpha(x, y) == 255) { pixel.x = imageData.x + x; pixel.y = imageData.y + y; region.add(pixel); } } } } else { ImageData mask = imageData.getTransparencyMask(); Rectangle pixel = new Rectangle(0, 0, 1, 1); for (int y = 0; y < mask.height; y++) { for (int x = 0; x < mask.width; x++) { if (mask.getPixel(x, y) != 0) { pixel.x = imageData.x + x; pixel.y = imageData.y + y; region.add(pixel); } } } }

    Устанавливаем форму окна:

    Shell.setRegion(region);

    Теперь нужно создать слушателя событий для окна. Нас будут интересовать события рисования окна, события мыши и нажатия клавиш, чтобы окно можно было передвигать по экрану.

    Listener listener = new Listener() { int startX, startY; public void handleEvent(Event e) { if (e.type == SWT.KeyDown && e.character == SWT.ESC) { shell.dispose(); } if (e.type == SWT.MouseDown && e.button == 1) { startX = e.x; startY = e.y; } if (e.type == SWT.MouseMove && (e.stateMask & SWT.BUTTON1) != 0) { Point p = shell.toDisplay(e.x, e.y); p.x -= startX; p.y -= startY; shell.setLocation(p); } if (e.type == SWT.Paint) { e.gc.drawImage(image, imageData.x, imageData.y); } } };

    Итак, по нажатию на клавишу Esc окно закроется. При нажатии левой клавиши мыши на области окна запомним координаты нажатия. При движении мыши с зажатой левой клавишей - передвигаем окно на экране соответственно движению. При событии перерисовки - рисуем картинку фона, используя графический контекст GC.

    Назначим слушатель соответствующим событиям окна:

    Shell.addListener(SWT.KeyDown, listener); shell.addListener(SWT.MouseDown, listener); shell.addListener(SWT.MouseMove, listener); shell.addListener(SWT.Paint, listener);

    Устанавливаем размер окна равным размеру изображения:

    Shell.setSize(imageData.x + imageData.width, imageData.y + imageData.height);

    Открываем окно и запускаем цикл событий:

    Shell.open(); while (!shell.isDisposed ()) { if (!display.readAndDispatch ()) display.sleep (); }

    Не забываем в конце освободить использованные ресурсы:

    Region.dispose(); image.dispose(); display.dispose();

    Запустив программу на этом этапе, мы получим прямоугольничек, который можно двигать мышкой и закрывать по Esc.

    Настало время добавить содержания. Будем отображать текущую погоду в виде иконки состояния (солнечно, дождь, снег…), показаний температуры и времени последнего обновления.

    Для расположения графических компонентов в окне в нужном виде используются менеджеры компоновки. Менеджер компоновки занимается не только расположением компонентов, но и изменением их размеров при изменении размеров окна. Для нашего виджета будем использовать GridLayout. Этот менеджер располагает компоненты в ячейках воображаемой таблицы. Создаем GridBagLayout на две колонки с различной шириной колонок (флаг false в конструкторе), устанавливаем его в качестве менеджера компоновки окна:

    GridLayout layout = new GridLayout(2, false); shell.setLayout(layout);

    Для картинки статуса используем компонент Label. В качестве родителя передаем объект окна. Вторым параметром можно установить стиль компонента. Для каждого компонента набор возможных флагов стиля разный, их можно посмотреть в документации или прямо в исходниках компонента.

    //draw status image Label imageLabel = new Label(shell, SWT.NONE); imageLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, true, 1, 1));

    Флаги в классе GridData означают, что метка будет располагаться слева вверху, будет растягиваться горизонтально и вертикально (флаги, установленные в true) при наличии свободного места и занимает одну строку и один столбец таблицы компоновки.

    В SWT нет прозрачного фона компонентов, и позади картинки статуса будет красоваться белый фон, чего, конечно, не хотелось бы. Поэтому создадим объект Color с цветом фона окна:

    Color bgColor = new Color(display, 0x2b, 0x2b, 0x2b);

    В конце программы этот объект также необходимо очистить, вызвав метод dispose. Устанавливаем цвет фона и картинку статуса, которую можно загрузить из файла точно так же, как мы загрузили картинку фона вначале:

    ImageLabel.setBackground(bgColor); Image statusImage = new Image(display, "images/1.png#26759185"); imageLabel.setImage(statusImage);

    Теперь добавим Label с текущей температурой и расположим его в правой верхней части окна:

    Label temperatureLabel = new Label(shell, SWT.NONE); temperatureLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));

    Установим какую-нибудь температуру:

    TemperatureLabel.setText("+1 \u2103");

    Для записи температуры по Цельсию используется юникодный номер соответствующего символа со служебными символами \u.

    Шрифт по умолчанию для текстовых меток слишком маленький. Так что создадим новый, побольше:

    FontData fD = temperatureLabel.getFont().getFontData(); fD.setHeight(30); fD.setStyle(SWT.BOLD); Font newFont = new Font(display, fD); temperatureLabel.setFont(newFont); Шрифт, как и другие ресурсные объекты, нужно освобождать. Для этого воспользуемся слушателем события разрушения метки:

    TemperatureLabel.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { newFont.dispose(); } });

    Наконец, добавим метку с описанием погодных условий:

    Label descriptionLabel = new Label(shell, SWT.WRAP); descriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 2, 1)); descriptionLabel.setText("Облачно с прояснениями, небольшой дождь"); descriptionLabel.setBackground(bgColor); descriptionLabel.setForeground(display.getSystemColor(SWT.COLOR_WHITE));

    Текст может быть довольно длинным, так что при создании метки указываем флаг WRAP, чтобы текст автоматически разбивался на несколько строк при нехватке места. Расположим компонент по центру и разрешим ему заполнить все горизонтальное пространство. Также укажем, что компонент занимает два столбца таблицы компоновки. Запускаем и получаем окошко с картинки «Виджет погоды».

    Теперь можно прикрутить какой-нибудь сервис погоды, создать таймер для автоматического обновления - и виджет готов.

    Swing: всегда свежие новости

    На Swing мы напишем виджет для отображения RSS-новостей. Начинаем, как и в прошлый раз, с создания окна. Класс, реализующий функционал стандартного окна в Swing, называется JFrame. По умолчанию закрытие окна приложения в Swing не приводит к остановке программы, так что лучше прописать, как должно себя вести окно при закрытии:

    JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    Для представления новостей лучше всего подходит таблица. Swing построен на паттерне «Модель -представление - контроллер» (MVC). В архитектуре MVC модель предоставляет данные, представление отвечает за отображение данных (например, текст, поля ввода), а контроллер обеспечивает взаимодействие между моделью и представлением. Таблица хорошо демонстрирует этот подход. Для представления данных используется класс, реализующий интерфейс TableModel.

    Для хранения информации о доступных новостях заведем класс FeedMessage c полями для названия статьи и даты выхода:

    Public class FeedMessage { public String title; public Date publicationDate; }

    Чтобы упростить и ускорить разработку, наследуем нашу модель данных от класса AbstractTableModel, который предлагает готовую реализацию почти всех методов интерфейса TableModel.

    Public class RssFeedTableModel extends AbstractTableModel { private List entries = new ArrayList<>(); public void updateData(List entries) { this.entries = entries; fireTableDataChanged(); } public int getRowCount() { return entries.size(); } public int getColumnCount() { return 2; } public Object getValueAt(int rowIndex, int columnIndex) { switch (columnIndex) { case 0: return entries.get(rowIndex).title; case 1: return entries.get(rowIndex).publicationDate; } return null; } }

    Метод fireTableDataChanged сообщает представлению, что модель данных изменилась и необходима перерисовка.

    Создаем таблицу и немного изменяем ее вид, чтобы она была больше похожа на виджет. Убираем линии между строками и столбцами, увеличиваем высоту строки и убираем заголовок таблицы с названиями колонок:

    JTable table = new JTable(new RssFeedTableModel()); table.setShowGrid(false); table.setIntercellSpacing(new Dimension(0, 0)); table.setRowHeight(30); table.setTableHeader(null);

    Теперь займемся внешним видом ячеек. Swing позволяет назначать отдельные классы представления для разных типов данных. За отрисовку отдельных ячеек таблицы отвечает класс, наследующий интерфейс TableCellRenderer. По умолчанию используется DefaultTableCellRenderer, который представляет собой текстовую метку.

    Назначим свой отрисовщик ячейки для данных типа String. Изменим стандартный цвет шрифта и сделаем чередующийся цвет фона, чтобы улучшить читаемость.

    Table.setDefaultRenderer(String.class, new DefaultTableCellRenderer() { Color oddColor = new Color(0x25, 0x25, 0x25); Color evenColor = new Color(0x1a, 0x1a, 0x1a); Color titleColor = new Color(0x3a, 0xa2, 0xd7); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); setBackground(row % 2 == 0 ? oddColor: evenColor); setForeground(titleColor); setFont(font); return this; } });

    Чтобы таблица начала использовать наш отрисовщик, необходимо добавить метод, который возвращает тип данных для каждой ячейки, в модель данных:

    Public Class getColumnClass(int columnIndex) { switch (columnIndex) { case 0: return String.class; case 1: return Date.class; } return Object.class; }

    Новостей может быть много, поэтому поместим таблицу на панель прокрутки и сделаем ползунок прокрутки невидимым, чтобы он не портил нам дизайн виджета:

    JScrollPane scrollPane = new JScrollPane(table); table.setFillsViewportHeight(true); scrollPane.getVerticalScrollBar().setPreferredSize (new Dimension(0,0));

    Добавляем компонент прокрутки на главную панель окна. Вторым аргументом можно передать размещение компонента. По умолчанию главная панель окна использует менеджер компоновки BorderLayout, который располагает компоненты по сторонам света. Поместим таблицу с прокруткой в центре.

    Frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

    Как и в прошлый раз, уберем стандартное обрамление окна. А в качестве заголовка окна будем использовать стилизованную текстовую метку, которую разместим вверху окна.

    JLabel titleLabel = new JLabel("Xakep RSS"); Font titleFont = new Font("Arial", Font.BOLD, 20); titleLabel.setFont(titleFont); titleLabel.setHorizontalAlignment(SwingConstants.CENTER); titleLabel.setForeground(Color.WHITE); titleLabel.setPreferredSize(new Dimension(0, 40)); frame.getContentPane().add(titleLabel, BorderLayout.NORTH);

    В отличие от SWT, объекты «цвет» и «шрифт» освобождаются автоматически, так что можно больше не переживать за утечки памяти.

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

    MouseAdapter listener = new MouseAdapter() { int startX; int startY; public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { startX = e.getX(); startY = e.getY(); } } public void mouseDragged(MouseEvent e) { Point currCoords = e.getLocationOnScreen(); frame.setLocation(currCoords.x - startX, currCoords.y - startY); } }; titleLabel.addMouseListener(listener); titleLabel.addMouseMotionListener(listener);

    Теперь поменяем форму окна на прямоугольник с закругленными углами. Лучше всего это делать в слушателе компонента, так как, если размер окна изменится, форма окна будет правильно пересчитана:

    Frame.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { frame.setShape(new RoundRectangle2D.Double(0, 0, frame.getWidth(), frame.getHeight(), 20, 20)); } });

    Устанавливаем размер окна, убираем обрамление и делаем окно полупрозрачным.

    Frame.setSize(520, 300); frame.setUndecorated(true); frame.setOpacity(0.85f);

    Наконец, открываем окно в графическом потоке. SwingUtilities.invokeLater(new Runnable() { public void run() { frame.setVisible(true); } });

    Осталось дописать загрузку данных в отдельном потоке, и получим такой вот виджет с последними новостями твоего любимого журнала:).

    JavaFX: послушаем музычку

    И наконец, гвоздь сезона - JavaFX. Воспользуемся его мультимедийными возможностями и компонентом для построения графиков и сделаем простенький эквалайзер.

    Для начала наследуем класс виджета от Application. Это основной класс приложения в JavaFX. Application содержит основные методы жизненного цикла приложения. Компоненты формы создаются в методе start, аргументом которому служит класс Stage. Stage представляет собой окно программы. Изменим стиль окна на TRANSPARENT, чтобы убрать обрамление и кнопки. В Stage помещается класс Scene, в котором задаются размеры окна и цвет фона. В Scene, в свою очередь, передаем класс Group, в который будем помещать дочерние компоненты:

    Public void start(Stage primaryStage) { primaryStage.initStyle(StageStyle.TRANSPARENT); Group root = new Group(); Scene scene = new Scene(root, 400, 200, Color.TRANSPARENT); primaryStage.setScene(scene);

    Для отображения эквалайзера используем столбиковую диаграмму, по осям которой будем отображать частоту и мощность звука:

    CategoryAxis xAxis = new CategoryAxis(); NumberAxis yAxis = new NumberAxis(0,50,10); BarChart bc = new BarChart(xAxis,yAxis); bc.setPrefSize(400, 200); bc.setLegendVisible(false); bc.setAnimated(false); bc.setBarGap(0); bc.setCategoryGap(1); bc.setVerticalGridLinesVisible(false); bc.setHorizontalGridLinesVisible(false); xAxis.setLabel("Частота"); yAxis.setLabel("Мощность"); yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis, null, "dB"));

    Заполняем диаграмму начальными данными:

    XYChart.Series series1 = new XYChart.Series(); series1Data = new XYChart.Data; String categories = new String; for (int i=0; i(categories[i], 50); series1.getData().add(series1Data[i]); } bc.getData().add(series1);

    Создаем прямоугольник с закругленными углами, чтобы придать виджету соответствующую форму:

    Rectangle rectangle = new Rectangle(0, 0, 400, 200); Stop stops = new Stop { new Stop(0, new Color(0, 0, 0, 0.8)), null}; LinearGradient lg2 = new LinearGradient(0, 0, 0, 0, false, CycleMethod.NO_CYCLE, stops); rectangle.setFill(lg2); rectangle.setArcHeight(20); rectangle.setArcWidth(20);

    Добавляем оба компонента к группе:

    Root.getChildren().addAll(rectangle, bc);

    Назначаем слушателей мыши к группе, чтобы двигать окно по экрану:

    Root.setOnMousePressed(new EventHandler() { public void handle(MouseEvent me) { initX = me.getScreenX() - primaryStage.getX(); initY = me.getScreenY() - primaryStage.getY(); } }); root.setOnMouseDragged(new EventHandler() { public void handle(MouseEvent me) { primaryStage.setX(me.getScreenX() - initX); primaryStage.setY(me.getScreenY() - initY); } });

    Загружаем песню в плеер:

    File file = new File("выпусти меня отсюда.mp3"); Media audioMedia = null; audioMedia = new Media(file.toURI().toURL().toString()); audioMediaPlayer = new MediaPlayer(audioMedia);

    Добавляем слушатель, который будет обновлять столбиковую диаграмму:

    AudioMediaPlayer.setAudioSpectrumListener(new AudioSpectrumListener() { public void spectrumDataUpdate(double timestamp, double duration, float magnitudes, float phases) { for (int i = 0; i < series1Data.length; i++) { series1Data[i].setYValue(magnitudes[i] + 60); } } });

    Делаем сцену видимой и запускаем песню:

    PrimaryStage.show(); audioMediaPlayer.play();

    Запускаем приложение:

    Public static void main(String args) { launch(args); }

    И наслаждаемся такой вот красотой.

    В Java есть 2 основных пакета для создания графических интерфейсов (Graphics User Interface). Это Abstract Windows Toolkit (AWT) и Swing. AWT использует виджеты операционной системы, поэтому эта библиотека немного быстрее. Но на мой взгляд, Swing более хорошо спроектирован.

    В данном туториале мы рассмотрим основные элементы библиотеки Swing и создадим простой интерфейс (GUI) в качестве примера.

    Для группировки компонент интерфейса используются контейнеры (Container). Для создания основного контейнера для приложения чаще всего используется контейнер JFrame (есть еще JWindows и JApplet). Проще всего унаследоваться от JFrame тем самым получить доступ ко множеству методов, например:

    setBounds(x, y, w, h) - указывает координаты верхней левой вершины окна, а также его ширину и высоту.

    setResizable(bool) - указывает, можно ли изменять размер окна.

    setTitle(str) - устанавливает название окна.

    setVisible(bool) - собственно отображает окно.

    setDefaultCloseOperation(operation) - указывает операцию, которая будет произведена при закрытии окна.

    Основные элементы управления:

    • JLabel - элемент для отображения фиксированного текста;
    • JTextField - простой edit-box;
    • JButton - обычная кнопка (button);
    • JCheckBox - элемент выбора (аналог checkbox);
    • JRadioButton - радио кнопка

    Как видите, все довольно просто и логично.

    При отображении элементов управления используются специальные менеджеры - LayoutManager. У всех LayoutManager"ов есть методы для добавления у удаления элементов.

    FlowLayout - используется для последовательного отображения элементов. Если элемент не помещается в конкретную строку, он отображается в следующей.

    GridLayout - отображения элементов в виде таблицы с одинаковыми размерами ячеек.

    BorderLayout - используется при отображении не более 5 элементов. Эти элементы располагаются по краям фрейма и в ценрте: North, South, East, West, Center.

    BoxLayout - отображает элементы в виде рядка или колонки.

    GridBagLayout - позволяет назначать месторасположение и размер каждого виджета. Это самый сложный, но и самый эффективный вид отображения.

    Стоит еще обратить внимание на обработку событий. Для этого используются так называемые Event Listeners.

    Ну все, довольно теории, перейдем к примеру GUI:

    Import java.awt.*; import java.awt.event.*; import javax.swing.*; public class SimpleGUI extends JFrame { private JButton button = new JButton("Press"); private JTextField input = new JTextField("", 5); private JLabel label = new JLabel("Input:"); private JRadioButton radio1 = new JRadioButton("Select this"); private JRadioButton radio2 = new JRadioButton("Select that"); private JCheckBox check = new JCheckBox("Check", false); public SimpleGUI() { super("Simple Example"); this.setBounds(100,100,250,100); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container container = this.getContentPane(); container.setLayout(new GridLayout(3,2,2,2)); container.add(label); container.add(input); ButtonGroup group = new ButtonGroup(); group.add(radio1); group.add(radio2); container.add(radio1); radio1.setSelected(true); container.add(radio2); container.add(check); button.addActionListener(new ButtonEventListener()); container.add(button); } class ButtonEventListener implements ActionListener { public void actionPerformed(ActionEvent e) { String message = ""; message += "Button was pressed\n"; message += "Text is " + input.getText() + "\n"; message += (radio1.isSelected()?"Radio #1":"Radio #2") + " is selected\n"; message += "CheckBox is " + ((check.isSelected()) ?"checked":"unchecked"); JOptionPane.showMessageDialog(null, message, "Output", JOptionPane.PLAIN_MESSAGE); } } public static void main(String args) { SimpleGUI app = new SimpleGUI(); app.setVisible(true); } }

    Примечания:

    getContentPane возвращает контейнер верхнего уровня. ButtonGroup служит для создания группы взаимосвязанных радио-кнопок.

    Внутренний класс ButtonActionListener реализует интерфейс ActionListener. Для этого необходимо предоставить имплементацию метода actionPerformed.

    JOptionPane служит для отображения диалоговых окон.

    Жду ваших вопросов и комментариев. Если вы хотите больше узнать о Swing, скажите об этом, и в скором времени я напишу еще одну статью с более сложными приемами и компонентами.



В продолжение темы:
Windows

Часть вторая : "Важнейшие характеристики каждого семейства процессоров Intel Core i3/i5/i7. Какие из этих чипов представляют особый интерес" Введение Сначала мы приведём...

Новые статьи
/
Популярные