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

Abstract Window Toolkit

Официальный сайт: docs.oracle.com/javase/7/docs/technotes/guides/awt/

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

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

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

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

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

Недостатки:

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

заключение:

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

Swing

Официальный сайт: docs.oracle.com/javase/tutorial/uiswing/

Как выглядит 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

Официальный сайт: www.eclipse.org/swt

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

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

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

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

Недостатки:

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

Заключение:

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

JavaFX

Официальный сайт: www.oracle.com/

Как выглядит JavaFX
Как выглядит 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"); 

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

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"); 
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[0].setHeight(30); fD[0].setStyle(SWT.BOLD); Font newFont = new Font(display, fD[0]); 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<FeedMessage> entries = new ArrayList<>();

    public void updateData(List<FeedMessage> 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<String,Number>(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<String,Number> series1 = new XYChart.Series<String,Number>();
series1Data = new XYChart.Data[128];
String[] categories = new String[128];
for (int i=0; i<series1Data.length; i++) {
    categories[i] = Integer.toString(i+1);
    series1Data[i] = new XYChart.Data<String,Number>(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<MouseEvent>() {
    public void handle(MouseEvent me) {
        initX = me.getScreenX() - primaryStage.getX();
        initY = me.getScreenY() - primaryStage.getY();
    }
});

root.setOnMouseDragged(new EventHandler<MouseEvent>() {
    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. Кроме описанных графических библиотек, есть еще множество других, не таких распространенных, но не обязательно худших по качеству. У каждой из них есть свои сильные и слабые стороны. Java предоставляет право выбора тебе :).

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    4 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии