动画定时paintComponent顶部的JButton

时间:2018-08-31 21:26:38

标签: java jpanel paintcomponent jcomponent bounding-volume

我有一个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 :(得分:0)

  

如果您仔细观察,将鼠标悬停在其应有的位置时,会弹出按钮。

这意味着您有ZOorder问题。 ZOrder是组件在z轴上彼此堆叠时的绘制顺序。

Swing按照与将组件添加到面板中相反的顺序绘制组件,因此最后添加的最后一个组件首先被绘制。因此,听起来好像您有多个面板堆叠在一起,并且最后添加的面板包含按钮,但是它首先被绘制,因此所有其他面板都被绘制。

当鼠标移到该按钮上时,该按钮需要重新绘制自身以显示该按钮的滚动效果,因此,在适当的ZOrder重新建立时,该按钮会暂时在顶部进行绘制,直到下一次重新绘制父面板。

我对您的程序的结构了解不多,但是我想问一问,为什么您有一堆面板彼此叠放。您需要做的就是在面板上添加自定义绘画,然后将按钮放在该面板上。

答案 1 :(得分:0)

您的主要问题就在这里...

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并不是线程安全的,更新UI或UI依赖的状态可能会导致意外且难以诊断的结果。

在这种情况下,您有两种选择,根据需要使用Swing TimerSwingWorker

看看Concurrency in Swing了解更多详情

另一个问题是at.rotate(Math.toRadians(90), Math.toRadians(90), Math.toRadians(90));。这根本没有道理。

documentation状态

 public void rotate​(double theta,
                   double anchorx,
                   double anchory) 
     

将此变换与围绕锚点旋转坐标的变换连接。这个   操作等同于平移坐标,以便   锚点位于原点(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

哦,在哪里有我的按钮-可能根据您对Graphics上下文所做的更改围绕容器的中心点旋转了90度,所以现在它们可能不在屏幕上

答案 2 :(得分:0)

我已经移到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));