Skip to content

JavaFX для меломана. Часть 3

И вот, после трех месяцев мы, наконец-то, оказались на финишной прямой. Последний бой — он трудный самый.

В предыдущих частях повествования стал проглядывать интерфейс и мы получили возможность управлять приложением. Пусть чуть-чуть, но это, все же, лучше чем ничего.

Что осталось? Вот основные пункты:

1. Добавление файлов в список плейлиста.

2. Проигрывание этих файлов.

3. Перемотка по плейлисту.

4. Управление звуком.

5. Вывод названия трека на экран.

6. Перемотка внутри песни.

Так как времени мало, а материала много, то именно по этим пунктам и пройдемся.

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

Листинг 1.
onMouseClicked: function(evt: MouseEvent): Void {
  if ((evt.clickCount == 1) and (evt.button == MouseButton.PRIMARY))
  then {
    var dialogOpen : JFileChooser = new JFileChooser();
    dialogOpen.setMultiSelectionEnabled(true);
    dialogOpen.setFileFilter(new Mp3FileFilter());
    dialogOpen.setAcceptAllFileFilterUsed(false);
    if (JFileChooser.APPROVE_OPTION == dialogOpen.showOpenDialog(null)) {
      for (file in dialogOpen.getSelectedFiles()){
        insert file.getAbsolutePath() into fileList;
      }
    }
  }
}

С первой парой строк все ясно из предыдущих статей, а дальше начинается непонятное. Разъясняю. Сначала создается диалоговое окно выбора файлов JFileChooser. Это окно — одно из самых часто используемых в работе, так как почти в каждой программе требуется выбор файлов и их открытие. JFileChooser имеет множество опций, одной из которых является использование фильтров, то есть возможность отображать только те файлы, которые подходят условию. В нашем случае должны быть видны только mp3 файлы и папки, поэтому мы создаем следующий класс:

Листинг 2.
class Mp3FileFilter extends FileFilter{
  override public function accept(file : File) : Boolean{
    if (file.getName().endsWith(".mp3"))
    then { return true; };
    if (file.isDirectory())
    then { return true; };
    return false;
  }
  override public function getDescription() : String{
    return "Mp3 files";
  }
}

И указываем, что этот фильтр необходимо использовать dialogOpen.setFileFilter(new Mp3FileFilter()). Все, теперь при запуске окна JFileChooser будут видны только папки и файлы с расширением mp3.

В рамках статьи нам понадобятся свойства setMultiSelectionEnabled(true), позволяющее выбирать несколько файлов одновременно, и setAcceptAllFileFilterUsed(false), убирающее возможность просмотра всех файлов (только mp3). Далее программа проверяет, была ли нажата кнопка Open, и если это так, в переменную fileList (а она, напомню, связана со списком песен) добавляются абсолютные пути выбранных файлов (file.getAbsolutePath()).

Вот теперь мы готовы к решительному шагу запуска медиафайла в проигрывателе. Будем это делать двойным кликом на файле в плейлисте, как это принято в больших плеерах. Для этого достаточно добавить пару строчек в код listviewPlaylist:

Листинг 3.
onMouseClicked: function(evt: MouseEvent): Void {
  if ((evt.clickCount == 2) and (evt.button == MouseButton.PRIMARY))
  then {
    playMedia(fileList[listviewPlaylist.selectedIndex]);
  }
}

Здесь вызывается функция playMedia(), которую рассмотрим чуть позже, так как она требует небольшой подготовки с нашей стороны. Добавить работоспособности интерфейсу можно назначив onMouseClicked для кнопок перемотки:

Листинг 4.
onMouseClicked: function(evt: MouseEvent): Void {
  if ((evt.clickCount == 1) and (evt.button == MouseButton.PRIMARY)) {
    if (listviewPlaylist.selectedIndex != sizeof listviewPlaylist.items - 1)
    then {
      listviewPlaylist.selectNextRow()
    } else {
      listviewPlaylist.selectFirstRow();
    }
    playMedia(fileList[listviewPlaylist.selectedIndex]);
  }
}

Здесь представлен код для кнопки Вперед и он прост как две копейки — при нажатии на кнопку проверяется условие на конец списка, в зависимости от результата выбирается следующий или первый трек и запускается все та же незнакомая функция playMedia(). Кнопка Назад аналогична по функциональности и ее код приводиться здесь не будет.

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

Листинг 5.
sliderVolume = Slider {
  layoutX: bind display.layoutX + 10
  layoutY: bind display.layoutY + 10
  min: 0
  max: 9
  value: 0
  vertical: true
  height: 80
}

Переменные layoutX и layoutY — положение слайдера на форме; min и max — минимальное и максимальное значение слайдера; value — значение по умолчанию; vertical — показывает, что слайдер имеет вертикальное расположение; height — высота в пикселах. Теперь привязка к громкости mediaPlayer будет выглядеть как одна строчка в переменных этого компонента:

volume: bind (9 - sliderVolume.value) / 10

Вывод названия трека на экран оказывается ничуть не сложнее, чем предыдущая задача. Добавим компонент Text на небольшой Rectangle в центре нашей сцены, который мы считаем экраном:

Листинг 6.
trackName = Text {
  content: bind track;
  wrappingWidth: 130
  layoutX: bind display.layoutX + 30
  layoutY: bind (display.height - trackName.layoutBounds.height) / 2
    + display.layoutY + trackName.font.size / 2
  fill: Color.AQUA
  textAlignment: TextAlignment.CENTER
  font: Font{ size:14 }
  effect: Bloom{ }
}

Самая главная строчка здесь — content: bind track. С помощью нее выводимый текст привязывается к переменной track типа String.

Чтоб не загружать статью еще одним огромным листингом предлагаю открыть исходные коды программы и найти место, где мы выводим на сцену lineProgress и rectProgress. LineProgress — линия, по которой будет двигаться слайдер перемотки. Более интересен другой компонент — rectProgress, являющийся тем самым ползунком перемотки песни. Здесь мы используем переменную blocksMouse, которая запрещает передавать событие вниз по слоям интерфейса. Эту возможность необходимо использовать для того, что бы при перетаскивании ползунка не начинала двигаться наша форма (напомню, что мы добавили возможность drag’n'drop для Stage в прошлой статье). Листинг обработчика событий для этого компонента необходим, так что вот и он:

Листинг 7.
onMousePressed: function(e: MouseEvent):Void {
  timelineProgress.pause();
  beginPointX = e.sceneX - rectProgress.width / 2;
}
onMouseDragged: function(e: MouseEvent):Void {
  var minPosition = lineProgress.layoutX - rectProgress.width / 2;
  var maxPosition = lineProgress.layoutX - rectProgress.width / 2 + 350;
  var xPosition = e.sceneX - rectProgress.width / 2;
  if(xPosition <= minPosition)
  then {
    xPosition = minPosition
  } else if (xPosition >= maxPosition)
  then { xPosition = maxPosition }
  rectProgress.layoutX = xPosition;
}
onMouseReleased: function(e: MouseEvent):Void {
  resumeMedia(e.sceneX - rectProgress.width / 2
    - lineProgress.layoutX - rectProgress.width / 2);
}

В этом коде придется разобраться достаточно подробно. Во-первых — что такое timelineProgress?

Листинг 8.
var timelineProgress = Timeline {
  repeatCount: Timeline.INDEFINITE
  keyFrames : [
    KeyFrame {
      time : bind Duration.valueOf(mediaDurStep);
      canSkip : true
      action: function () {
        if (rectProgress.layoutX >= lineProgress.layoutX - rectProgress.width / 2 + 350)
        then { } else { rectProgress.layoutX++ }
      }
    }
  ]
}

TimelineProgress имеет тип TimeLine — один из базовых типов JavaFX, с которыми придется работать постоянно. TimeLine дает нам возможность изменения параметров нашего приложения в процессе выполнения приложения. Стоит рассмотреть его подробнее. Ключевые переменные этого типа следующие:

1. autoReverse — разрешает возврат анимации. После завершения анимации объект возвращается на исходную позицию по той же траектории.

2. keyFrames — содержит последовательность KeyFrame.

3. repeatCount — количество повторов TimeLine.

4. time — указывается с какого времени начать проигрывание анимации.

В нашем случае repeatCount получил значение Timeline.INDEFINITE. Это значит, что анимация будет повторяться бесконечное количество раз.

В переменной keyFrames мы используем только один компонент. KeyFrame — своеобразная единица анимации, которая выполняется необходимое количество раз с интервалом time. Action — функция, которая выполняется каждый период времени. Наша функция перемещает слайдер перемотки по линии и нельзя допустить, чтоб он вышел за пределы этой полосы перемотки, для этих целей и написана проверка на границы. Длина полосы перемотки — 350 пикселей.

TimeLine имеет четыре функции:

1. pause() — ставит анимацию на паузу, то есть временно ее приостанавливает.

2. play() — снимает анимацию с паузы, либо запускает с указанной позиции.

3. playFromStart() — запускает TimeLine с самого начала.

4. stop() — останавливается анимация и возвращает все значения в начальные позиции.

После всех этих строк теории становится немного понятнее практика. В Листинге 7 событие onMousePressed запоминает начальную позицию ползунка и останавливает анимацию. При перетаскивании вызывается onMouseDragged, в котором мы просто перемещаем ползунок по полосе перемотки. Опять же, постоянно следим за тем, что бы Rectangle не выходил за границы. И, наконец, при отпускании кнопки мыши вызываем resumeMedia() — функцию, которая продолжает выполнение timelineProgress и включает наш трек в выбранном месте. Делается это так:

Листинг 9.
function resumeMedia(position : Number){
  mediaplayer.mediaPlayer.currentTime = Duration.valueOf(position * mediaDurStep);
  timelineProgress.play();
}

Здесь position — положение слайдера на полосе перемотки. Duration.valueOf() переводит некоторое значение в тип данных Duration — время. В нашем контексте мы устанавливаем текущим временем внутри песни положение ползунка, умноженное на шаг выполнения TimeLine. После чего возобновляем выполнение timelineProgress().

Осталась малость — разобраться с нашим Неуловимым Джо — функцией playMedia(). Вот ее листинг:

Листинг 10.
function playMedia(mediaSrc : String){
  mediaplayer.mediaPlayer.stop();
  rectProgress.layoutX = lineProgress.layoutX - rectProgress.width / 2;
  playPause = true;
  mediaplayer.mediaPlayer.media = Media{source: "file:/{mediaSrc.replace("\\","/").replace(" ","%20")}"};
  mediaDurStep = mediaplayer.mediaPlayer.media.duration.toMillis() / 350;
  mediaplayer.mediaPlayer.play();
  timelineProgress.playFromStart();
  track = mediaSrc.substring(mediaSrc.lastIndexOf("\\") + 1);
}

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

1. Останавливается проигрывание предыдущей песни.

2. Слайдер перемотки устанавливается в начальное положение.

3. Переменная playPause (на нее, например, завязано переключение кнопки Пауза/Проигрывание) устанавливается в true.

4. Присваиваем переменной media новый медиафайл (здесь производится форматирование и приведение к принятому в JavaFX имени файла).

5. Вычисляем mediaDurStep — количество секунд, которые должны пройти между перемещением ползунка на один пиксел, для чего берем продолжительность песни и делим на длину полосы перемотки.

6. Запускаем проигрывание файла.

7. Запускаем наш TimeLine с начала.

8. Присваиваем переменной track (к ней привязано название песни, выводимое на экран) значение, которое будет выглядеть примерно как «название-песни.mp3″.

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

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

Ну а исходники вы как всегда найдете по этой ссылке.

JavaFX для меломана. Часть 1

Вот и вышла в Мире ПК моя последняя статья серии про JavaFX. Но так как далеко не все читают этот журнал я решил напечатать ее и здесь. Читайте на здоровье.

Любой житель нашей планеты, за исключением редких папуасов, знаком с технологией под названием Java. И если вы не папуас, то наверняка пользовались телефоном с поддержкой Java-приложений, видели программу, написанную на Java, или хотя бы раз выходили в Интернет и сталкивались с Java-аплетом. Кто-то, может быть, даже добавит к перечисленному JavaScript, широко распространенный в Сети. И этот кто-то будет совершенно неправ, так как Java и JavaScript связаны разве что схожими названиями.

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

Главным минусом виртуальной машины Java на протяжении долгих лет являлось полное отсутствие адекватных библиотек для создания интерфейса. Хотя SWT и Swing и представляют собой мощнейшие системы отображения графического интерфейса, все же их никогда не назвали бы «шустрыми» и современными.

И вот, 4 декабря 2008 г. Sun Microsystems наконец-то выпустила то, что так долго ждали все любители красивого и удобного интерфейса, — язык JavaFX.

Представители корпорации Sun позиционируют свой продукт как RIA (Rich Internet Application), но JavaFX охватывает не только эту нишу. Не менее важна возможность интеграции нового интерфейса в уже написанное настольное приложение, процесс создания которого и рассмотрим в данной статье. В первой ее части будут приведены основы языка JavaFX, а синтаксис и конструкции мы обсудим в продолжении.

Для начала нужно выбрать среду разработки. Основной IDE для программистов, использующих JavaFX, является NetBeans, которую мы и станем применять. Дистрибутив ее находится на сайте www.netbeans.org. Во время написания статьи последняя стабильная версия данной среды была 6.7, и потому именно с ней мы и станем практиковаться. А чтобы не мудрствовать, скачайте с сайта сборку со встроенной поддержкой JavaFX.

Полагаю, с ее установкой вы справитесь и без подсказок, так что перейдем к проекту, над которым нам предстоит «поиздеваться». Это обычный плеер, коих выложены миллионы по всему Интернету, но он будет вашим и, следовательно, самым замечательным.

Для начала разработаем интерфейс. Поскольку мы выбрали небольшой проект, то и интерфейс сделаем похожим на дешевые китайские MP3-плееры. По-моему, вполне правильное решение. Весь листинг первого урока можно скачать здесь. Значит, когда будете читать эту статью, откройте единственный (пока!) файл листинга под названием Main.fx и смотрите на него. Итак, приступим.

Запускаем установленную среду NetBeans и создаем новый JavaFX-проект. Естественно, мы серьезные люди и потому назовем проект ChinesePlayer, а главному файлу дадим имя com.pcworld.chineseplayer.Main. Вот в принципе и все. Мы готовы к свершениям, первым из которых станет решительное переименование заголовка приложения (title): с Application title на «Китаский плееир» (назовем по-русски — в знак гордости за свою страну, а ошибки — для аутентичности).Как выглядят дешевые китайские плееры за 1000 руб.? Правильно, небольшие продолговатые устройства с парой кнопок и маленьким монохромным экраном в центре. Такой мы сейчас и сделаем.

В основе всего интерфейса JavaFX лежит объект Stage, без его определения ничего не получится. Stage — это сама форма с кнопками сворачивания/разворачивания/закрытия окна. В параметрах данного объекта задаются размер окна и все его атрибуты. В нашем случае размер окна не имеет значения, так как в параметрах мы указываем стиль StageStyle.TRANSPARENT — сделать окно без рамки.

Вторым по важности элементом любой формы является сцена — Scene. Сцен в Stage может быть несколько, но тогда их необходимо менять. Мы же выбрали простой вариант без каких-либо изысков, а значит, будем использовать лишь одну сцену. В нашей сцене первым параметром стоит fill — он предназначен для определения цвета фона. Укажем значение fill: Color.TRANSPARENT, что сделает нашу форму не только не имеющей границ, но и прозрачной. А кому нужно абсолютно прозрачное окно без какой-либо смысловой нагрузки? Правильно, никому, и потому самое время наполнить сцену содержимым. Параметр content в JavaFX служит лишь для одного — он содержит внутри квадратных скобок все те элементы, которые должны быть объединены. В данном случае это будут компоненты на форме. Далее можно увидеть, как используется компонент Group, данный чуть выше в основном листинге на «Мир ПК-диске». Кстати, его объявление выглядит следующим образом: var player: Group = new Group(), где var — зарезервированное слово для объявления переменных, player — имя переменной, Group — тип данных, а все то, что идет после знака равенства, — присваиваемое значение. Кроме var, есть еще def, позволяющий создавать read-only-переменную, т.е. константу.

В JavaFX принято несколько логически связанных визуальных компонентов объединять в группу. В данном случае такой группой будет player с тем же параметром content, что был и при рассмотрении сцены. Внутри этой группы мы поместим несколько компонентов, позволяющих показать создаваемый плеер во всей красе. Рассмотрим их по очереди (листинг 1).

Листинг 1
case = Rectangle {
  x: 20, y: 20
  width: 450, height: 150
  fill: Color.rgb(5, 5, 5)
  arcHeight:150 arcWidth:80
  stroke:Color.DARKRED strokeWidth:15
  effect: Lighting {
    diffuseConstant:1.5
    light: DistantLight {
      azimuth: -135
    }
    surfaceScale: 1 specularConstant:0.1
  }
}

Объект Case — корпус нашего плеера. Как следует из объявления типа данных, это прямоугольник с размерами 400×150 пикселов почти черного цвета.

Цвет в JavaFX можно задавать несколькими способами. Вот основные из них:

Color.ЦВЕТ — значения, соответствующие нужному цвету, можно выбрать из списка автозаполнения кода, нажав комбинацию клавиш +Space;

Color.rgb(0, 0, 0) — в скобках указывается интенсивность красного, зеленого и синего оттенков;

Color.web(0xffffff) используется шестнадцатеричное представление цвета, принятое в Интернете. Также существуют и методы для градиентной заливки — LinearGradient и RadialGradient. Их мы применять не будем, а чтобы узнать о них более подробно, можно обратиться к справочной системе.

Значения arcWidth и arcHeight нужны для скругления углов прямоугольника. А вот параметры Stroke и strokeWidth описывают рамку плеера: первый задает цвет рамки, второй — ее ширину.

Теперь корпус готов. Осталось добавить немного спецэффектов с помощью параметра effects. Мы применим эффект освещения Lighting, чтобы объект стал реалистичнее. Уточним кое-какие его особенности: diffuseConstant обеспечивает рассеивание света, specularConstant придает объекту блеск, создавая эффект зеркала, surfaceScale делает объект по-настоящему трехмерным, реализуя эффект затемнения его краев. Наконец, параметр корпуса light позволяет указать, какого типа свет направлен на объект. В рассматриваемом примере используется равномерное освещение, источник которого находится сверху слева.

Перейдем к описанию экрана плеера, задаваемого в листинге 2 объектом display. Переменные, описывающие прямоугольник, вам уже знакомы. Уточним два новых параметра: layoutX и layoutY. Эти переменные — методы позиционирования узла на сцене. С их помощью мы разместим экран в центре корпуса плеера.

Листинг 2
display = Rectangle {
  width: 170, height: 100
  fill: Color.GREY
  layoutX: bind (case.width - display.layoutBounds.width) / 2 + case.y
  layoutY: bind (case.height - display.layoutBounds.height) / 2 + case.x
  effect: Lighting {
    diffuseConstant:0.5 light: DistantLight { azimuth: 135 }
    surfaceScale: 1 specularConstant:0.1
  }
}

Обратите внимание на слово bind — оно довольно быстро становится лучшим другом JavaFX-разработчика. Его основная задача заключается в том, чтобы связывать переменную с определенными событиями. Указав bind при присваивании переменной, вы «заставляете» ее следить за всеми переменными, участвующими в ее вычислении. Так, в нашем случае, если изменится ширина или положение корпуса, дисплей все равно окажется в его центре.

С прямоугольниками разобрались, но не одними же ими живет программист? Опишем для примера круг. Пусть это будет кнопка плеера (листинг 3)

Листинг 3
buttonPlay = Circle {
  radius: 50 fill: Color.DARKRED
  centerY: bind case.height / 2 + case.y
  centerX: bind display.layoutX - 70
  effect: Lighting {
    diffuseConstant:1.5
    light: DistantLight { azimuth: -135 }
    surfaceScale: 1 specularConstant:0.5
  }
}

Кратко рассмотрим все используемые переменные класса Circle, хотя их предназначение легко понять из названий. Например, radius — радиус круга. С параметрами fill и effect вы уже знакомы, а в переменных centerX и centerY хранятся координаты центра круга.

Треугольники на кнопках сделаны с помощью фигуры polyline, пример которой дан в листинге 4.

Листинг 4
polylinePlay = Polyline {
  points : [
    buttonPlay.centerX - 20,
    buttonPlay.centerY - 30,
    buttonPlay.centerX + 30,
    buttonPlay.centerY,
    buttonPlay.centerX - 20,
    buttonPlay.centerY + 30,
    buttonPlay.centerX - 20,
    buttonPlay.centerY - 30
  ]
  strokeWidth: 1.0 stroke: Color.WHITE
  visible: true fill:Color.WHITE
  effect: DropShadow { offsetY: 4 }
}

Класс Polyline позволяет рисовать любые фигуры из прямых линий, задавая координаты через запятую (x и y). Делается это внутри квадратных скобок переменной points. Параметр Visible сообщает о том, что фигура будет показана на экране (это необходимо для того, чтобы менять ее на два прямоугольника, символизирующих паузу), а эффект DropShadow показывает тень у фигуры и добавляет немного живости в картинку.

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

Листинг 5
buttonPrev = Arc {
  centerX: bind display.layoutX + display.width + 70,
  centerY: bind case.arcHeight / 2 + case.x
  radiusX: 50, radiusY: 50
  startAngle: 90, length: 180
  type: ArcType.CHORD
  fill: Color.GREEN
  effect: Lighting {
    diffuseConstant:1.5
    light: DistantLight { azimuth: -135 }
    surfaceScale: 1
  }
}

Переменные centerX и centerY вам уже известны, а вот параметр radius здесь превращается в два разных радиуса по ширине и высоте дуги соответственно — radiusX и radiusY. Значение startAngle задает начальный угол дуги, а length — длину арки в градусах. Переменная type содержит тип арки (в JavaFX их три — CHORD, ROUND и OPEN).

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

JavaFX для меломана. Часть 2

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

В части 2 шагнем на следующий уровень, так как по прошествии целого месяца наши знания JavaFX, естественно, улучшились. Итак, начнем с нескольких конструкций языка, которые сейчас обязательно понадобятся. Поскольку они являются основами любого языка, останавливаться на них не будем, а просто вкратце перечислим.

Операторы

Операторы, используемые в языке JavaFX, знакомы тем, кто владеет другими языками программирования.

Последовательности

В JavaFX последовательности закрывают в квадратные скобки и пишут через запятую (вы уже сталкивались с ними при построении точек в Polyline) либо имеют вид [1..100], что означает все цифры от 1 до 100.

Выражение if

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

if (условие) then {событие1} else {событие2}.

В объяснении она не нуждается. Особенностью языка является то, что then можно не использовать.

Выражение for

Выражение for, использующееся для работы с последовательностями, имеет конструкцию: — for(последовательность) {событие}
Событие будет выполнено столько раз, сколько элементов содержится в последовательности. А теперь можно вернуться к написанию программы.

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

  1. Платформонезависимый слой как часть JavaFX SDK, на который не влияет операционная система; он важен для вас, поскольку непосредственно с ним вы и будете работать.
  2. Платформозависимый нижний слой, на котором происходит «общение» виртуальной машины с кодеками вашей операционной системы.

Из-за лицензионных и других ограничений далеко не все аудио- и видеофайлы будут проигрываться на поддерживаемых JavaFX операционных системах. Полностью поддерживаются только видеоформат FLV и всем известный аудиоформат MP3.

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

Листинг 1
mediaplayer = MediaView {
  mediaPlayer : MediaPlayer { media : Media { } }
}
  • Media — сам мультимедиа­файл. В этом классе хранится информация об имени файла, его длине и разрешении видео.
  • MediaPlayer — промежуточный класс. У него нет никаких визуальных компонентов, но есть все функции для перемотки и регулирования звука и т.д.
  • MediaView предназначен лишь для вывода медиафайла на экран монитора. Можно накладывать на этот класс все эффекты, вращать его и сворачивать в трубочку, но вам это не потребуется.

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

Система проигрывания музыки уже есть, но не хватает плейлиста. Для этого и обычный класс ListView (листинг 2), но вам понадобится переменная, в которой будет храниться список песен, — var fileList: String[].

Листинг 2
listviewPlaylist = ListView {
  width: 300
  height: 200
  layoutY: bind case.layoutBounds.maxY - 3
  layoutX: bind (case.width - listviewPlaylist.layoutBounds.width) / 2
    + case.y items: bind fileList
}

Параметры, описывающие размеры компонента и его положение, были рассмотрены в предыдущей части данной статьи. А вот переменная items привязывается к списку песен и изменяется вместе с ним при добавлении либо удалении элементов последовательности.
Посмотрите на вашу форму. Красиво? Очень, но чего-то не хватает. Если поразмыслить, то поймете, что забыли добавить кнопку выключения вашего устройства. Начинайте с листинга (листинг 3).

Листинг 3
groupX = Group {
  layoutX: bind case.layoutBounds.width - 39
  layoutY: bind case.layoutBounds.minY - 26
  content: [
    Rectangle {
      arcHeight:8
      arcWidth:8
      x: 10, y: 10
      width: 20, height: 20
      stroke:Color.GREY
      fill:Color.DARKRED
      onMouseClicked: function(evt: MouseEvent): Void {
        if ((evt.clickCount == 1) and (evt.button == MouseButton.PRIMARY))
        then { FX.exit(); }
      }
    },
    Line {
      startX: 13, startY: 13
      endX: 27, endY: 27
      strokeWidth: 1 stroke: Color.WHITE
    },
    Line { startX: 13, startY: 27
      endX: 27, endY: 13
      strokeWidth: 1 stroke: Color.WHITE
    }
  ]
}

Как видите, все очень просто. Вы создаете группу, в которую входят прямоугольник (в данном случае — квадрат) и две пересекающиеся линии, четко дающие представление о том, что эта кнопка делает. Но вы можете возразить, что ваши кнопки пока еще ничего не выполняют.

Именно так. До этого мы рассматривали исключительно визуальную форму плеера, но теперь уже видим полностью функционирующий элемент системы. Произошло это благодаря новой конструкции языка — событию. В JavaFX есть несколько видов событий, но вас в данном случае должны интересовать возникшие по вине пользователя. Он может нажать кнопку мыши и отпустить ее, покрутить колесико, а также нажать кнопку на клавиатуре. И сделать еще пару действий, но их мы обсудим ниже. Что же происходит в тот момент, когда осуществляется действие? Виртуальная машина Java перехватывает событие и делает то, что было заложено программистом.

Стоит упомянуть, что у JavaFX есть одна особенность, о которой необходимо знать любому программисту, — событие исполняется одновременно для всех слоев интерфейса. Что это значит? Примерно следующее: когда вы нажимаете на кнопку, то нажимаете и на ту форму, где она находится. В случае с кнопкой выключения плеера вас это должно мало волновать или даже скорее нисколько не волновать, но данное знание еще пригодится, когда вы запустите ваш плеер и решите, что было бы неплохо иметь возможность перемотать песню вперед/назад. Но обо всем по порядку.

В первом случае рассматриваем событие onMouseEvent, имеющее тип function(evt: MouseEvent): Void {}, где вас больше всего должен интересовать параметр evt типа MouseEvent.

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

  • clickCount — количество нажатий на клавишу мыши;
  • button — кнопка мыши, изменившая свое состояние;
  • altDown, controlDown и shiftDown — нажаты ли соответствующие кнопки на клавиатуре;
  • primaryButtonDown, secondaryButtonDown, middleButtonDown — нажаты ли левая, правая или средняя кнопки мыши;
  • sceneX, sceneY — координаты события в сцене;
  • screenX, screenY — координаты события на всем экране;
  • x, y — координаты события внутри node;
  • wheelRotation — количество событий вращения колеса мыши.

Это все базовые переменные. Теперь рассмотрим конкретный пример:

onMouseClicked: function(evt: MouseEvent): Void {
  if ((evt.clickCount == 1) and (evt.button == MouseButton.PRIMARY))
  then { FX.exit(); }
}

Уже после одного взгляда на список приведенных выше переменных все становится предельно ясно — событие после then вызывается в том случае, если вы единожды (evt.clickCount == 1) нажали левую кнопку мыши (evt.button == MouseButton.PRIMARY). Что же происходит потом? Метод FX.exit() закрывает приложение. Вот и все. Основные функции ваше приложение выполняет — оно умеет запускаться и выключаться.

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

Листинг 4
onMouseClicked: function(evt: MouseEvent): Void {
  if ((evt.clickCount == 1) and (evt.button == MouseButton.PRIMARY))
  then { stage.iconified = true }
}

Переменная iconified, принимая значение true, сворачивает окно в Панель задач. Параметр Stage также позволяет задавать значки для программы с помощью переменной icons.

Теперь можно убрать плеер с глаз долой — в Панель задач, но разве такое положение устроит настоящего программиста? Этого определенно мало. А если захочется полюбоваться на свое детище, но при условии, чтобы оно не загораживало весь остальной экран? Правильно, для этого его нужно аккуратно отодвинуть в сторону. Все, что от вас требуется, — добавить следующие несколько строк в код оператор case (листинг 5).

onMouseEntered: function(evt: MouseEvent): Void {
  listviewPlaylist.visible = true
}
onMouseExited: function(evt: MouseEvent): Void {
  listviewPlaylist.visible = false
}
onMousePressed: function(e: MouseEvent):Void {
  beginPointX = e.screenX - stage.x; beginPointY = e.screenY - stage.y;
}
onMouseDragged: function(e: MouseEvent):Void {
  stage.x = e.screenX - beginPointX; stage.y = e.screenY - beginPointY;
}

Также допишите две строки в список переменных:

var beginPointX: Number; var beginPointY: Number;

События onMouseEntered и onMouseExited выполняются тогда, когда мышь попадает в площадь формы или выходит из нее соответственно. Эти функции убирают список воспроизведения с экрана в том случае, если на плеер не наведен курсор мыши. Посмотрите на фрагмент программы (на «Мир ПК-диске»), описывающий список воспроизведения, и вы увидите то же самое (ListView не должен исчезать при наведении на него).

Теперь разберемся в том, что здесь будет происходить.
События onMouseEntered и onMouseExited выполняются в тех случаях, когда мышь попадает в площадь формы или выходит из нее соответственно. Эти функции предназначены, чтобы убирать список воспроизведения с экрана в том случае, если на наш плеер не наведен курсор мыши. Если посмотреть на код нашего списка воспроизведения, выложенного на сайте, то там вы увидите то же (ListView не должен исчезать при наведении на него).
OnMousePressed вызывается при нажатии на элемент кнопкой мыши и в нашем конкретном примере запоминает положение stage.
Зажав кнопку теперь вы можете перетащить плеер в любую точку экрана. Это происходит за счет функции onMouseDragged. Понять ее просто — координатам сцены присваиваются новые значения, соответствующие разнице между новым положением мыши и начальными координатами. Если не поняли, то просто поверьте — это работает.

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

JavaFX Composer 1.1

15 февраля вышел JavaFX Composer 1.1, существенно упрощающий жизнь любому разработчику на JavaFX, если в его приложении не нужно создавать особых изысков в интерфейсе (в этом случае всегда лучше ручками).

Сейчас посмотрим на самые существенные изменения в этой версии.

Во-первых, упростился процесс компоновки интерфейса за счет того, что теперь можно как перетаскивать фигуры, так и транслировать их. Очень удобно. При обычном перетаскивании мышью меняются параметры layoutX и layoutY, что чревато изменениями размеров контейнеров. В новой версии добавили возможность перетаскивать компоненты с зажатым Ctrl — при этом меняется translateX и translateY, а значит, размеры контейнеров изменяться не будут.

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

В-третьих (в-очень-важных) расширилась поддержка источников данных. Появилась поддержка парсинга XML-файлов. Изменился парсинг JSON — теперь записи представляются как RecordSet, Record или аттрибут Record. Появился парсер LINES, представляющий RecordSet набором Record, каждая из которых — целая строка. Появился метод HTTP POST. Добавили поддержку записи в источники JDBC (!!!). Расширилась поддержка FileDataSource — теперь данные из файла можно брать по абсолютному пути, classpath или Storage API.

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

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

В-шестых шаблон Exclusively Visible получил свежую фишку — теперь он может быть не только видимым, но и при этом прозрачным. Естественно настройки прозрачности и видимости присутствуют.

В-седьмых — на палитре появились сразу две новых категории — JavaFX Charts (experimental) и JavaFX Chart Resources (experimental). Что на них находится, я думаю, понятно — графики/диаграммы и источники данных для них. Вот этого действительно не хватало. Теперь производить подобные манипуляции стало на порядок проще. Как и положено — все меняется в два клика и один drag’n'drop. В общем как у взрослых.

Ну и в-последних — добавились несколько новых примеров и переделались несколько старых.

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

JavaFX и H2. Работа с БД

Ну, вот, теперь я смог выделить немного времени не только улучшению дизайна моего блога, но и улучшению его содержания.
Уже довольно долго я собираю пароли такой замечательной программой, как KeePass, но в голове всегда крутилась злобная мыслишка накатать свой проект с теми же свойствами. На чем писать — вопрос не стоял. Оставалось выбрать немного времени и приступить.
Выбрал и приступил. Естественно в одну запись в блоге это все не поместится. Разделим нашу программу на несколько частей (каких пока сам не знаю) и начнем.
Что самое главное в программе, которая хранит некоторый набор данных? Естественно база данных. Так что вполне логично начать не с интерфейса, а с нее.
Для мелких проектов я давно использую JavaDB, но в данном случае — было выбрано лучшее портативное решение — H2 ( http://www.h2database.com/ ). Почему портативное? Глупый вопрос если вы хотите запускать программу с флешки. Скачав дистрибутив и, потратив час на мануал, все стало ясно — отличное и крайне удобное решение.
Создаем класс H2Connector, который будет выполнять все необходимые функции при работе с бд, и начинаем. В этом классе нам понадобятся 3 переменные:

// Сессия с БД
var connector : Connection;
// Отвечает за выполнение статических запросов
var statement : Statement;
// Имя базы данных (пока считается что она всегда лежит в папке с jar)
public-init var baseName : String;

Создаем функцию соединения с H2:


function dbConnect(dbName : String) {
  // Папка с jar
  var str : String;
  str = __DIR__.substring(0, __DIR__.lastIndexOf("!"));
  str = str.substring(0, str.lastIndexOf("/", str.length()) + 1);
  str = str.substring(4, str.length());
  // Подключаем драйвер H2
  try {
    Class.forName("org.h2.Driver");
  } catch (e : ClassNotFoundException) {
    println("dbConnect. Unable to load Driver Class");
    FX.exit();
  };
  // Соединяемся с БД
  try {
    connector = DriverManager.getConnection("jdbc:h2:{str}{dbName}", "sa", "");
    statement = connector.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  } catch (e : SQLException) {
    println("dbConnect. SQLException: {e.toString()}");
    FX.exit();
  }
}

При выключении программы отсоединяемcя:

public function dbDisconnect(){
  try {
    statement.close();
    connector.close();
  } catch (e : SQLException) {
    println("dbDisconnect. SQLException: {e.toString()}");
    FX.exit();
  }
}

А вот и две функции, отвечающие за выполнение запросов:

// На SELECT ...
public function dbExecute(query : String) : ResultSet{
  var rs : ResultSet;
  try {
    rs = statement.executeQuery(query);
  } catch (e : SQLException) {
    println("dbExecute. SQLException: {e.toString()}");
  }
  return rs;
}
// ... и на UPDATE
public function dbUpdate(query : String) : Integer{
  var res : Integer;
  try {
    res = statement.executeUpdate(query);
  } catch (e : SQLException) {
    println("dbUpdate. SQLException: {e.toString()}");
  }
  return res;
}

Почти готово. Осталось создать главную таблицу нашей базы:

function dbCreateTables() {
  try {
    dbUpdate("CREATE TABLE IF NOT EXISTS PASS(ID INT AUTO_INCREMENT "
    "PRIMARY KEY, RESOURCE VARCHAR(255), USER VARCHAR(255), PASSWORD "
    "LONGVARCHAR(1024), TYPE VARCHAR(255), FOLDER VARCHAR(255), "
    "COMMENT LONGVARCHAR(1024), PICTURE VARCHAR(255));");
  } catch (e : SQLException) {
    println("dbCreateTables. SQLException: {e.toString()}");
    FX.exit();
  }
}

Все. Добавляем init:

init { dbConnect(baseName); dbCreateTables(); }

Класс готов.
Интерфейс пока прост. Описывать тут не стоит, так как проще посмотреть в исходниках.

JavaFX выиграла медаль

Сегодня, 9 февраля 2010 года, JavaFX была признана официальной RIA технологией Олимпиады в Ванкувере и будущих Параолимпийских Игр. Так что олимпиада 2010 пройдет под нашим с вами знаком. Теперь каждый может посмотреть на результаты текущей Олимпиады и ее предшественниц рядом с логотипом JavaFX. http://www.vancouver2010.com/olympic-medals/geo-view/

Нагрузка на сайт XXI Олимпийских игр ожидается грандиозной — до 1’500’000’000 кликов и разработчики портала постарались сделать его наиболее эффективным и как можно более интуитивно понятным для пользователя. Для этих целей и был разработан апплет, позволяющий простым понятным образом иллюстрировать достижения спортсменов. Также, с 12 числа будет выложен виджет на JavaFX, позволяющий просматривать результаты прямо с десктопа.

Один из директоров VANOC (организации, отвечающей за проведение XXI Олимпийских игр и X Параолимпийских игр), отвечающий за интернет технологии при проведении игр Дэвид Маршал сказал следующее: «Зимние Олимпийские Игры в Канаде в 2010 году — это время блеснуть, время показать всему миру кто мы и что мы можем сделать. Технология JavaFX позволила нашей команде создать по-настоящему потрясающий способ доступа к получению информации о выигранных медалях. После окончания олимпиады нашим наследием окажется умение предоставлять доступ к результатам через инновационный интерфейс.»

Выбор технологии во многом был определен правильной политикой продвижения технологии JavaFX компанией Sun (приобретенной Oracle). Компания Sun организовала JavaFX Partner Program, проводила тренировки и оказывала поддержку разработчикам.

Введение

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

За долгое время своего обучения и работы я перепробовал и Pascal/Delphi, и C++, и C#, и даже VBA (здесь упоминаются только более-менее серьезные опыты с языками). Но в итоге остановился на, сначала,  Java, а спустя некоторое время и JavaFx. Причина выбора до безобразия проста – Java – самый сильный язык последнего десятилетия, а появившийся не так давно  JavaFX (ранее JavaFX Script) позволил Java, наконец, выйти из чисто корпоративных решений в массы.

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

И вот, наконец, в мае 2007 года на конференции JavaOne было анонсировано то, о чем так долго думали разработчики – технология, позволяющая привязать Java к профессиональному интерфейсу. Автором языка стал Крис Оливер.

После непродолжительного молчания (с учетом важности события) Sun выложила JavaFX плагины к NetBeans 6.1 с версиями 0.X, а затем и версию 1.0. Это случилось 4 декабря 2008 года.

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

Вот и кончилась моя первая запись в этот блог.

Теперь о будущем: В этом блоге будут выкладываться новости из мира Java/JavaFX, о которых я решу упомянуть, мои собственные записи о языке и технологии, с примерами и описанием, как положено, а также переводы тех статей, которые меня заинтересуют.

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

Визуально блог будет меняться и очень сильно. То что сейчас на экране – “моя первая картина”.