我有一个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;
}
}
答案 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 Timer
或SwingWorker
。
看看Concurrency in Swing了解更多详情
另一个问题是at.rotate(Math.toRadians(90), Math.toRadians(90), Math.toRadians(90));
。这根本没有道理。
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
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
哦,在哪里有我的按钮-可能根据您对Graphics
上下文所做的更改围绕容器的中心点旋转了90度,所以现在它们可能不在屏幕上>
答案 2 :(得分:0)
我已经移到JLayeredPane以获得所需的效果。我可以单击顶层并拖动底层。
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));