ДжазТим — надежный технологический партнер

Agile разработка ПО на Java

FAQ созданное в процессе разработки java-апплета

Как вызывать функции JavaScript из апплета?

Это делается с помощью следующего кода:

applet.getAppletContext().
    showDocument(new URL("javascript:" + js));

Где applet объект класса JApplet, js — имя функции JavaScript, например show().

При этом апплет должен быть запущен из JavaScript.

Как вызывать методы апплета из JavaScript?

Для этого апплет должен быть запущен из JavaScript. Например

<script src="https://www.java.com/js/deployJava.js"></script>
    <script>
        var attributes = { id: 'mainApplet', code:  
'org.jazzteam.Example', archive: 'example.jar', width: 812, height:  
635};                 
        var parameters = {};         
        deployJava.runApplet(attributes, parameters, '1.7');                 
        function actionInApplet(url) {                         
            mainApplet.appletMethod(url);                 
        }
</script>

В данном случае апплет запущен из JavaScript с id= ‘mainApplet’. В апплете находится метод appletMethod(), который мы хотим вызвать. Для этого нужно у объекта JavaScript с именем id вызвать этот метод. Также можно передавать параметры в вызываемые методы, что продемонстрировано в данном примере.

Также стоит знать, что метод апплета, вызываемый из JavaScript должен быть привилегированным. Для этого нужно обернуть код метода в такую “обёртку”:

AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
        //method code
        return new Object();
    }
});

Как использовать параметры запуска апплета?

В приведённом выше примере в конструкцию «var parameters = {};» необходимо добавить нужные параметры. Например,

var parameters = {firstParameter:true, secondParameter:false};

В апплете параметры считываются следующим образом:

String parameter=applet.getParameter(“firstParameter”);

Где applet — объект класса JApplet (или Applet).

Такой способ так же работает при запуске апплета с помощью тегов <object> и <applet>.

Как создать скриншот Swing/AWT компонента?

Это можно сделать 2-мя способами. В обоих случаях компонент должен быть видимым.

Без использования робота:

BufferedImage image = new BufferedImage(component.getWidth(), 
component.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics g = image.getGraphics();
    component.paint(g);

С использованием робота:

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

Point point = new Point(0, 0);
    SwingUtilities.convertPointToScreen(point, component);
    Rectangle region = component.getBounds();
    region.x = point.x;
    region.y = point.y;
    BufferedImage image= new Robot().createScreenCapture(region);

Как использовать одновременно 2 апплета в одном браузере, если в них присутствуют static поля?

Для этого нужно запускать апплеты в разных JVM. Чтобы запустить апплеты в разных JVM нужно запустить их с параметром separate_jvm=true.

Как в Swing можно сделать поле для ввода, которое динамически расширяется при переполнении, а так же имеет минимальный размер?

Пример такого JTextField:

public class TextField extends JTextField {
    public static final int TEXT_FIELD_PADDING = 4;

    public TextField() {
        addAutoResize();
        init();
    }

    public void init() {
        setStyleByParentStyle();
        this.setPreferredSize(new Dimension(Constant.
TEXT_FIELD_MINIMAL_WIDTH, this.getPreferredSize().height));
    }

    private void addAutoResize() {
        this.getDocument().addDocumentListener
(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
            checkResize();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
            checkResize();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
            checkResize();
            }
        });
    }

    private void checkResize() {
        FontMetrics fontMetrics = this.getFontMetrics(this.
getFont());

        if (fontMetrics.stringWidth(this.getText()) + 
TEXT_FIELD_PADDING * 2 < Constant.TEXT_FIELD_MINIMAL_WIDTH) {
            this.setPreferredSize(new 
Dimension(Constant.TEXT_FIELD_MINIMAL_WIDTH, this.getHeight()));
            this.getParent().revalidate();
        } else {
            //NOTE 2 - border width*2
            this.setPreferredSize(new 
Dimension(fontMetrics.stringWidth(this.getText()) + 2 + 
Constant.CARRET_WIDTH + TEXT_FIELD_PADDING * 2, this.getHeight()));
            this.getParent().revalidate();
        }
    }
}

Как реализовать Drag&Drop в Swing?

В Java 6 появился довольно удобный механизм для реализации Drag&Drop, однако у него есть свои минусы. Например, необходимо явно указывать Drop Target, что не очень удобно, когда объект необходимо положить рядом с Drop Target’ом. Также в стандартной реализации нет гарантии порядка выполнения методов слушателей. Я расскажу концепцию реализации более расширяемого Drag&Drop.

Изначально на все перетягиваемые компоненты (Drag Source) необходимо назначить слушателей мыши (Mouse Listener и MouseMotionListener). Необходимо реализовать 3 метода: метод нажатия мыши на объект, метод перемещения мыши при зажатой кнопке мыши на объекте (mouseDragged в MouseMotionListener) и метод отпускания кнопки мыши.

Выглядит назначение слушателей так:

component.addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
        //block click right mouse button
        if (MouseEvent.BUTTON1 == e.getButton()) {
            startDrag(e);
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        //block click right mouse button
        if (MouseEvent.BUTTON1 == e.getButton()) {
            endDrag(e);
        }
    }
});

component.addMouseMotionListener(new MouseMotionAdapter() {
    @Override
    public void mouseDragged(MouseEvent e) {
        drag(e);
   }
});

Соответственно при нажатии мыши на объект стартует Drag&Drop, при перемещении мыши объект должен перемещаться, при отпускании кнопки мыши объект должен изменить свои координаты и переместиться на новый контейнер. Если перемещение объекта будет происходить только в пределах одного контейнера, то можно реализовать лишь метод mouseDragged(), в котором будут изменяться координаты перетаскиваемого объекта:

@Override
public void mouseDragged(MouseEvent e) {
    Point mouseLocation = e.getLocationOnScreen();
    Component draggedComponent = (Component) e.getSource();
    SwingUtilities.convertPointFromScreen(mouseLocation, 
draggedComponent.getParent());
    draggedComponent.setLocation(mouseLocation);
}

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

Алгоритм получается приблизительно такой:

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


GlassPane с эффектом прозрачности:

public class GhostGlassPane extends JPanel {
    private final AlphaComposite composite;
    private BufferedImage ghostImage = null;
    private Point location = new Point(0, 0);

    public GhostGlassPane() {
        setOpaque(false);
        composite = AlphaComposite.getInstance(AlphaComposite.
SRC_OVER, 0.7f);
}
public void paintComponent(Graphics g) {

    if (ghostImage == null)
        return;

    Graphics2D g2 = (Graphics2D) g;
    g2.setComposite(composite);
    g2.drawImage(ghostImage, (int) (location.getX()),
(int) (location.getY()), null);

    }
}

В данном ответе расписан лишь концепт реализации.

Как подписать апплет сертификатом?

Прочитайте нашу статью «Подписывание Java-апплетов«.

Как проигрывать MP3- и WAV-файлы в Java?

Прочитайте нашу статью «Поддержка воспроизведения MP3 и WAV форматов в Java«.

Как использовать HTML-форматирование в Swing компонентах?

Html форматирование поддерживают следующие Swing-компоненты: buttons, menu items, labels, tool tips, и tabbed panes. Для использования форматирования необходимо в начале текста поставить тег <html> (закрывать его в конце не обязательно). Например:

<html><b><u>T</u>wo</b>

На компоненте будет отображен следующий текст: Two

Не отображаются компоненты при использовании цикла, что делать?

Скорее всего цикл используется в потоке отрисовки Swing. Для того, чтобы выполнить какие-то действие над UI и не заблокировать поток отрисовки можно использовать Swing Timer. Пример использования:

Timer timer= new Timer(1000, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        //actions
    }
 });
 timer.setRepeats(true);
 timer.start();

Данный код будет раз в секунду выполнять какие-то действия, не блокируя поток отрисовки.

Так же можно установить свойство repeats=false, для выполнения действия только 1 раз.

Для остановки таймера необходимо выполнить timer.stop()

Как сделать плавный переход между фреймами в Swing?

Предложу следующий эффект:

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

Алгоритм таков:

Как запустить апплет?

Апплет на HTML-странице можно запустить 3 способами: с помощью тега <applet>, с помощью тега <object>, с помощью JavaScript.

Тег <applet> считается устаревшим, однако до сих пор большинство апплетов запускаются с помощью этого тега:

<applet code="HelloWorldApplet.class" width="320" height="120">
</applet>

Пример запуска апплета с помощью тега <object>:

<object type="application/x-java-applet" name="previewersGraph" 
width="360" height="320"> 
    <param name="codebase" value="/applets" /> 
    <param name="code" value="my.full.class.Name" />
    <param name="archive" value="applets.jar" /> 
</object>

Как можно разместить Swing компоненты на контейнере со случайными координатами?

Могу предложить следующий алгоритм:

Реализация алгоритма:

/**
 * Class for adding components to container with random 
 * coordinates
 */
public class ComponentRandomizer {

    /**
     * Add all components to container with random 
     * coordinates
     */
    public static void addComponentsToContainer(Container 
container, List<JComponent> components) {
        List<Rectangle> cells = getContainerCells(container, 
components);
        for (JComponent component : components) {
            Rectangle randomCell = cells.get(new Random().
nextInt(cells.size()));
            cells.remove(randomCell);
            addComponentToContainer(container, component, 
randomCell);
        }
    }

    /**
     * Add one component to container by it cell
     */
    private static void addComponentToContainer(Container 
container, JComponent component, Rectangle cell) {
        container.add(component);
        int xCoordinate = getRandomInt(cell.getX(), 
cell.getWidth() - component.getWidth());
        int yCoordinate = getRandomInt(cell.getY(), 
cell.getHeight() - component.getHeight());
        component.setLocation(xCoordinate, yCoordinate);
    }

    /**
     * Get random integer
     */
    private static int getRandomInt(double minimal, 
double interval) {
        if (interval > 0) {
            return (int) (minimal + 
new Random().nextInt((int) interval));
        } else {
            return (int) minimal;
        }
    }

    /**
     * Create a cells that serves to adding component into it
     */
    private static List<Rectangle> getContainerCells(Container 
container, List<JComponent> components) {

        Pair<Integer, Integer> optimalCellsNumber = 
getOptimalNumberOfContainerCells(container, components);
        Dimension cellSize = getCellSize(container, 
optimalCellsNumber);

        List<Rectangle> cells = new LinkedList<Rectangle>();
        for (int stringIndex = 0; stringIndex < 
optimalCellsNumber.getKey(); stringIndex++) {
            for (int columnIndex = 0; columnIndex < 
optimalCellsNumber.getValue(); columnIndex++) {
                cells.add(createCell(cellSize, stringIndex, 
columnIndex));
            }
        }

        return cells;
    }

    /**
     * Create a cell that serves to adding component into it
     */
    private static Rectangle createCell(Dimension cellSize, 
int stringIndex, int columnIndex) {
        int xCellCoordinate = (int) (columnIndex * 
cellSize.getWidth());
        int yCellCoordinate = (int) (stringIndex * 
cellSize.getHeight());
        return new Rectangle(xCellCoordinate, yCellCoordinate, 
(int) cellSize.getWidth(), (int) cellSize.getHeight());
    }

    /**
     * Create cell size
     */
    private static Dimension getCellSize(Container container, 
Pair<Integer, Integer> optimalCellsNumber) {
        double cellWidth = container.getWidth() / 
optimalCellsNumber.getValue();
        double cellHeight = container.getHeight() / 
optimalCellsNumber.getKey();
        return new Dimension((int) Math.floor(cellWidth), 
(int) Math.floor(cellHeight));
    }

    /**
     * Get the optimal amount of cells that can fit all of the 
     * components
     *
     * @return count strings and columns
     */
    private static Pair<Integer, Integer> 
getOptimalNumberOfContainerCells(Container container, 
List<JComponent> components) {

        //<Strings count, Columns count>
        Pair<Integer, Integer> numberOfContainerCells = 
getNumberOfContainerCells(container, components);

        //remove extra columns
        while (Math.ceil((double) components.size() / 
numberOfContainerCells.getKey()) < 
numberOfContainerCells.getValue()) {
            numberOfContainerCells = new Pair<Integer, 
Integer>(numberOfContainerCells.getKey(), 
numberOfContainerCells.getValue() - 1);
        }
        //remove extra strings
        while (Math.ceil((double) components.size() / 
numberOfContainerCells.getValue()) < 
numberOfContainerCells.getKey()) {
            numberOfContainerCells = new Pair<Integer, 
Integer>(numberOfContainerCells.getKey() - 1, 
numberOfContainerCells.getValue());
        }


        return numberOfContainerCells;
    }

    /**
     * Split container on the cells by largest component and get 
     * the number of cells of the container
     *
     * @return count strings and columns
     */
    private static Pair<Integer, Integer> 
getNumberOfContainerCells(Container container, 
List<JComponent> components) {
        JComponent largestComponent = getLargestComponent(components);
        int stringsCount = (int) Math.floor(container.getHeight() / 
largestComponent.getHeight());
        int columnsCount = (int) Math.floor(container.getWidth() / 
largestComponent.getWidth());
        if (stringsCount * columnsCount < components.size()) {
            throw new IllegalArgumentException(Constant.
MESSAGE_THE_COMPONENTS_CANNOT_BE_ACCOMMODATED_IN_THE_CONTAINER);
        }
        return new Pair<Integer, Integer>(stringsCount, columnsCount);
    }

    /**
     * Get component with largest square
     */
    private static JComponent getLargestComponent(List<JComponent> 
components) {
        JComponent largestComponent = components.get(0);
        for (JComponent checkedComponent : components) {
            if (getComponentSquare(
checkedComponent.getPreferredSize()) > 
getComponentSquare(largestComponent.getPreferredSize())) {
                largestComponent = checkedComponent;
            }
        }
        return largestComponent;
    }

    /**
     * Create component square
     */
    private static double getComponentSquare(Dimension 
checkedComponentSize) {
        return checkedComponentSize.getHeight() * 
checkedComponentSize.getWidth();
    }

    /**
     * Class for saving pair values
     */
    public static class Pair<K, V> {
        private K key;
        private V value;

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }
        public Pair(K k, V v) {
            key = k;
            value = v;
        }
    }
}

Как автоматизировать сборку апплета с помощью Maven?

Для этого необходимо иметь 2 проекта: проект апплета, который будет собираться как Jar-файл, и проект веб-приложения, которые нужно будет деплоить (на выходе получим War-файл).

Соответственно создадим многомодульный проект, в котором модулями будут 2 вышеуказанных проекта:

<project 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.jazzteam</groupId>
    <artifactId>applet-project</artifactId>
    <packaging>pom</packaging>
    <modules>
        <module>applet</module>
        <module>applet-web</module>
    </modules>
    <version>1.0-SNAPSHOT</version>
</project>

После сборки модуля апплета, необходимо скопировать получившийся JAR-файл в веб-приложение.

<build>
    <plugins>
        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <id>copy</id>
                    <phase>site</phase>
                    <configuration>
                        <tasks>
                            <copy file="targetapplet-eugliajar" 
tofile="..euglia-appletsrcmainwebappapplet-euglia.jar"/>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

В данном случае общий проект нужно собирать командой “clean package site”, потому как на фазе site начинается копирование файлов.

При таком подходе в веб-приложении должна быть HTML-страница, запускающая апплет.