JButton поверх анимированной временной компоненты краски
У меня есть 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 ответа
Ваша основная проблема прямо здесь...
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
в зависимости от ваших потребностей.
Посмотрите на Concurrency in Swing для более подробной информации
Еще одна проблема at.rotate(Math.toRadians(90), Math.toRadians(90), Math.toRadians(90));
, Это просто не имеет смысла.
Документация гласит
общественная пустота вращается (двойная тета, двойной якорь, двойная якорь)Сцепляются это преобразование с преобразованием, который вращается вокруг координаты опорной точки. Эта операция эквивалентна переводу координат таким образом, что точка привязки находится в начале координат (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
О, где, о, где мои кнопки - вероятно, повернутые на 90 градусов вокруг центральной точки контейнера, основываясь на изменениях, которые вы внесли в Graphics
контекст, так что теперь они, вероятно, за кадром
Если вы внимательно посмотрите, вы увидите, как кнопки всплывают, когда вы наводите курсор на то место, где они должны быть.
Это означает, что у вас есть проблема ZOorder. ZOrder - это порядок, в котором компоненты окрашиваются, когда они наложены друг на друга по оси Z.
Swing рисует компоненты в обратном порядке, в котором компонент добавляется на панель, поэтому последний добавленный компонент будет окрашен первым. Похоже, у вас есть несколько панелей, наложенных друг на друга, и последняя добавленная панель содержит кнопки, но сначала она закрашивается, поэтому все остальные панели закрашивают ее.
Когда мышь перемещается над кнопкой, кнопка должна перекрашиваться сама, чтобы показать эффект свертывания кнопки, поэтому кнопка временно окрашивается сверху до следующего перекрашивания родительской панели, когда соответствующий ZOrder будет восстановлен.
Я не знаю достаточно о структуре вашей программы, но я хотел бы спросить, почему у вас куча панелей, наложенных друг на друга. Все, что вам нужно, это одна панель с пользовательской окраской, а затем вы размещаете кнопки на этой панели.
Я перешел на 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));