JButton поверх анимированной временной компоненты краски - PullRequest
0 голосов
/ 01 сентября 2018

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

Кнопки отображаются за моим paintComponent, но иногда их можно нажимать. У меня также есть MouseListener для настройки преобразования скелета, и иногда это работает. Я могу опубликовать слушателя, но не думаю, что это важно.

Я пробовал различные комбинации graphics.dispose и graphics2d.dispose и пытался изменить положение super.paintComponent (g) со странными результатами, но ничего хорошего.

Ниже видео, показывающее проблему. Если вы внимательно посмотрите, вы увидите, как кнопки всплывают, когда вы наводите курсор на то место, где они должны быть. Вы также увидите, что когда я g.dispose () становится странным с кнопками в двух местах, но щелкает только в одном месте (что не там, где они наиболее заметны). https://youtu.be/CyFpUlbFI1U

Вот мой исходный код:

public SkeletonPlayer(int camera, double scale, int rotLeftRight, int rotUpDown, double translateLeftRight, double translateUpDown) {
    this.camera = camera;
    this.scale = scale;
    this.rotLeftRight = rotLeftRight;
    this.rotUpDown = rotUpDown;
    this.translateLeftRight = translateLeftRight;
    this.translateUpDown = translateUpDown;
    panel = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.translate(getWidth() / 2, getHeight() / 2);

            AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
            at.rotate(Math.toRadians(90), Math.toRadians(90), Math.toRadians(90));

            for (int n = 0; n < MainFrame.skeleton[camera].getNodes().size(); n++) {
                Node node = MainFrame.skeleton[camera].getNodes().get(n);
                int x1 = (int) (scale * node.getPosition().getX());
                int y1 = (int) (-scale * node.getPosition().getY());
                g2d.setColor(Color.RED);
                g2d.fillOval((int) (x1 - 2), (int) (y1 - 2), 4, 4);

                g2d.setColor(Color.BLACK);
                //g2d.drawString(node.getName(), x1 + 10, y1);

                for (Node child : node.getChildrens()) {
                    int x2 = (int) (scale * child.getPosition().getX());
                    int y2 = (int) (-scale * child.getPosition().getY());
                    g2d.drawLine(x1, y1, x2, y2);
                }
            }

            int x1 = (int) (scale * groundPlane.rootNode.getPosition().getX());
            int y1 = (int) (-scale * groundPlane.rootNode.getPosition().getY());
            g2d.setColor(Color.BLACK);
            g2d.fillOval((int) (x1 - 2), (int) (y1 - 2), 4, 4);

            for (int n = 1; n < groundPlane.nodes.size(); n++) {
                Node node = groundPlane.nodes.get(n);

                int x2 = (int) (scale * node.getPosition().getX());
                int y2 = (int) (-scale * node.getPosition().getY());
                g2d.setColor(Color.BLACK);
                g2d.fillOval((int) (x2 - 2), (int) (y2 - 2), 4, 4);

                g2d.setColor(Color.GREEN);

                g2d.drawLine(x1, y1, x2, y2);
                //System.out.println("line:" + x1 + "," + y1 + " " + x2 + "," + y2);
            }
        }
    };
    panel.addMouseListener(this);
    panel.addMouseMotionListener(this);
    panel.addMouseWheelListener(this);
    MigLayout layout = new MigLayout(
            "insets 10, gap 10, wrap", // Layout Constraints
            "[fill,grow][fill,grow][fill,grow]", // Column constraints with default align
            "[fill,grow][fill,grow][fill,grow]");
    panel.setLayout(layout);
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
    panel.add(new JButton("here"));
}

А вот и таймер:

new Timer().scheduleAtFixedRate(new TimerTask() {

        @Override
        public void run() {
            update();
        }
    }, 0, (long) Math.round(motion.getFrameTime() * 1000));

private static void update() {
    for (int i = 0; i < NUM_SKELETON_PLAYERS; i++) {
        MainFrame.skeleton[i].setPose((int) frameIndex, i);
        skeletonPlayers[i].groundPlane.setPose(i);
        skeletonPlayers[i].panel.repaint();
    }
    //skeleton.setPose(null);
    frameIndex += 1;
    if (frameIndex > motion.getFrameSize() - 1) {
        frameIndex = 0;
    }
}

Ответы [ 3 ]

0 голосов
/ 01 сентября 2018

Ваша основная проблема прямо здесь ...

Graphics2D g2d = (Graphics2D) g;
g2d.translate(getWidth() / 2, getHeight() / 2);

AffineTransform at = AffineTransform.getTranslateInstance(0, 0);

Контекст Graphics, передаваемый вашему компоненту, является общим ресурсом, он передается ВСЕМ компонентам, окрашенным во время цикла рисования, это означает, что когда вы изменяете такие вещи, как исходная точка и поворот, это влияет на ВСЕ компоненты, окрашенные после это.

Вместо этого вам следует создать копию состояния Graphics, прежде чем применять изменения, при этом исходное состояние не изменится

Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(getWidth() / 2, getHeight() / 2);

AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
//...
g2d.dispose();   

Ваша вторая ошибка ...

new Timer().scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        update();
    }
}, 0, (long) Math.round(motion.getFrameTime() * 1000));

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

В этом случае у вас есть два варианта: использовать Swing Timer или SwingWorker в зависимости от ваших потребностей.

Взгляните на Параллельность в Swing для более подробной информации

Другая проблема - at.rotate(Math.toRadians(90), Math.toRadians(90), Math.toRadians(90));. Это просто не имеет смысла.

В документации указано

 public void rotate​(double theta,
                   double anchorx,
                   double anchory) 
* +1032 * сцепляется это преобразование с преобразованием, который вращается вокруг координаты опорной точки. это операция эквивалентна переводу координат, так что точка привязки находится в начале координат (S1), затем вращая их вокруг нового происхождение (S2) и, наконец, перевод, так что промежуточный источник восстанавливается до координат исходной точки привязки (S3). это операция эквивалентна следующей последовательности вызовов:
 translate(anchorx, anchory);      // S3: final translation
 rotate(theta);                    // S2: rotate around anchor
 translate(-anchorx, -anchory);    // S1: translate anchor to origin   Rotating by a positive angle theta rotates points on the

положительная ось X в направлении положительной оси Y. Обратите внимание также на обсуждение обработки поворотов на 90 градусов выше.
Параметры:
theta - угол вращение в радианах
anchorx - координата X опорная точка вращения
anchory - координата Y вращения опорная точка

Что это говорит о том, что второй и третий параметры фактически являются точкой, вокруг которой происходит вращение. В вашем случае at.rotate(Math.toRadians(90), 0, 0);, вероятно, то, что вам действительно нужно.

Использование Graphics#create

Happy, happy, joy, joy

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private double angle;

        public TestPane() {
            setLayout(new GridBagLayout());
            for (int index = 0; index < 5; index++) {
                add(new JButton("Here"));
            }

            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    angle += 0.01;
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            //Graphics2D g2d = (Graphics2D) g;
            g2d.translate(getWidth() / 2, getHeight() / 2);

            AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
            at.rotate(Math.toRadians(90), 0, 0);
            g2d.drawRect(-50, -50, 100, 100);
            g2d.setColor(Color.RED);
            g2d.rotate(angle, 0, 0);
            g2d.drawRect(-100, -100, 200, 200);
            g2d.dispose();
        }

    }

}

Не используется Graphics#create

Boowho

О, где, о, где мои кнопки - вероятно, повернутые на 90 градусов вокруг центральной точки контейнера, основываясь на изменениях, которые вы внесли в контекст Graphics, так что теперь они, вероятно, за пределами экрана

0 голосов
/ 01 сентября 2018

Я перешел на JLayeredPane, чтобы получить эффект, который искал. Я могу щелкнуть верхний слой и перетащить нижний слой. enter image description here

    LayerUI backgroundUI = new WallpaperLayerUI();
    jlayer = new JLayer<JPanel>(panel, backgroundUI);

    LayerUI controlsUI = new LayerUI();
    JPanel controlPanel = new JPanel();
    MigLayout layout = new MigLayout(
            "debug, insets 10, gap 10, wrap", // Layout Constraints
            "[fill,grow][fill,grow][fill,grow]", // Column constraints with default align
            "[fill,grow][fill,grow][fill,grow]");
    controlPanel.setLayout(layout);
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));
    controlPanel.add(new JButton("here"));

    jlayer2 = new JLayer<JPanel>(controlPanel, controlsUI);

    panel.addMouseListener(this);
    panel.addMouseMotionListener(this);
    panel.addMouseWheelListener(this);

    finalPanel = new JLayeredPane();
    finalPanel.setPreferredSize(new Dimension(300, 310));
    //finalPanel.setLayout(null);
    jlayer.setBounds(0, 0, (808-40)/3, (608-40)/3);
    jlayer2.setBounds(0, 0, (808-80)/3, (608-80)/3);
    controlPanel.setBackground(new Color(0,0,0,0));
    finalPanel.add(jlayer, new Integer(0));
    finalPanel.add(jlayer2 ,new Integer(1));
0 голосов
/ 01 сентября 2018

Если вы внимательно посмотрите, вы увидите, как кнопки всплывают, когда вы наводите курсор на то место, где они должны быть.

Это означает, что у вас проблема с ZOorder. ZOrder - это порядок, в котором компоненты окрашиваются, когда они наложены друг на друга по оси Z.

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

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

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

...