英文:
Update values in a JPanel that is not visible
问题
以下是您要翻译的代码部分:
我已经对我的代码中一些非常奇怪的行为感到沮丧了一段时间,经过逐步缩减代码的过程,我终于找到了问题的源头。
总体概述:使用Java Swing,以下代码创建了一个选项卡界面。对用户可见的第一个选项卡上有一个按钮。第二个选项卡上有一个蓝色的正方形位于左上角。
应该发生的情况:一旦程序打开,先点击按钮,然后打开其他选项卡。按钮调用其他选项卡上的一个函数,使正方形移动到新的位置。因此,其他选项卡应该显示正方形的新位置,而不是左上角。
实际发生的情况:如果您首先点击按钮,然后再打开选项卡,正方形的位置不会改变。它仍然位于左上角,就好像从未按过按钮一样。如果您先打开选项卡,它似乎以某种方式“激活”了程序,所以现在按钮可以按预期工作。
当然,先点击选项卡以确保程序正常工作似乎是一个小烦恼,但这可能是一个很大的问题。为什么选项卡要等到至少查看一次才能更新?
观察:在调试代码时,我可以逐步执行`setUnit()`函数并验证正方形实际上已经成功更改,完全覆盖了以前的位置。然而,当我然后打开第二个选项卡时,正方形的位置现在又恢复到了以前的位置。如果在那时检查变量,它会显示正方形的原始位置完全没有改变,就好像从未调用过`setUnit()`函数一样。我知道这些组件不会在不重新绘制的情况下视觉上更新,所以我确保在`setUnit()`函数中添加了`repaint()`函数调用。我真的很纳闷正方形的原始位置究竟存储在哪里?我可以在调试器中看到这些值被覆盖了,所以它们应该完全不存在,对吧?
代码:
DragPanel.java:
```java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JPanel;
class DragPanel extends JPanel
{
private static final long serialVersionUID = 1L;
boolean isFirstTime = true;
Rectangle area;
Rectangle rect = new Rectangle(0, 0, 20, 20);
private Dimension dim = new Dimension(400, 300);
public DragPanel() {
setBackground(Color.white);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (isFirstTime) {
area = new Rectangle(dim);
rect.setLocation(0, 0);
isFirstTime = false;
}
g2d.setColor(Color.blue);
g2d.fill(rect);
}
public void setUnit()
{
rect.setLocation(200, 50);
repaint();
}
}
ShapeMover.Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class ShapeMover {
public ShapeMover() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
}
public static void main(String s[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new ShapeMover();
}
});
}
private void initComponents(JFrame frame) {
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setBounds(10, 93, 426, 527);
frame.getContentPane().add(tabbedPane);
DragPanel shaper = new DragPanel();
shaper.setBounds(0, 79, 420, 420);
JPanel input = new JPanel();
tabbedPane.addTab("Test", null, input, null);
input.setLayout(null);
JButton add = new JButton("Click this");
add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
shaper.setUnit();
}
});
add.setBounds(201, 64, 65, 23);
input.add(add);
JPanel output = new JPanel();
tabbedPane.addTab("Second", null, output, null);
output.setLayout(null);
output.add(shaper);
frame.pack();
frame.setVisible(true);
}
}
英文:
I had been frustrated with some really odd behavior in my code for a while, and after taking time to slowly widdle down my code bit by bit, I finally found the source of the issue.
General overview: using Java Swing, the following code creates a tabbed interface. The first tab that is visible to the user has a button. The second tab has a blue square in the upper left hand corner.
What should happen: Once the program is open, click the button first and then open the other tab. The button calls a function on the other tab, causing the square to move to a new location. Thus, the other tab should show the square in its new location, not the upper left hand corner.
What actually happens: If you click the button first and then open the tab, the square's position is unchanged. It remains in the upper left hand corner, as if the button was never pressed. If you open the tab first, it seems to "prime" the program somehow, so now the button works as expected.
Sure, it seems like a minor annoyance to click on the tab first to ensure the program works, but potentially this is a really big problem. Why is the tab unable to be updated until it is viewed at least once?
Observations: When debugging the code, I can step through the setUnit()
function and verify that the square is, in fact, being successfully changed, completely overwriting the previous position. And yet, when I then open the second tab the square's position is now reverted back to where it was previously. If the variables are inspected at that point, it shows the square's original position has remained completely unchanged, as if the setUnit()
function was never called. Knowing that these components don't visually update unless repainted, I made sure to add the repaint()
function call within the setUnit()
function. It really baffles me to wonder where the original values of the square's location are even stored? I can see in the debugger the values are overwritten, so they should completely cease to exist, right?
Code:
DragPanel.java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JPanel;
class DragPanel extends JPanel
{
private static final long serialVersionUID = 1L;
boolean isFirstTime = true;
Rectangle area;
Rectangle rect = new Rectangle(0, 0, 20, 20);
private Dimension dim = new Dimension(400, 300);
public DragPanel() {
setBackground(Color.white);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (isFirstTime) {
area = new Rectangle(dim);
rect.setLocation(0, 0);
isFirstTime = false;
}
g2d.setColor(Color.blue);
g2d.fill(rect);
}
public void setUnit()
{
rect.setLocation(200, 50);
repaint();
}
}
ShapeMover.Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class ShapeMover {
public ShapeMover() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
}
public static void main(String s[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new ShapeMover();
}
});
}
private void initComponents(JFrame frame) {
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setBounds(10, 93, 426, 527);
frame.getContentPane().add(tabbedPane);
DragPanel shaper = new DragPanel();
shaper.setBounds(0, 79, 420, 420);
JPanel input = new JPanel();
tabbedPane.addTab("Test", null, input, null);
input.setLayout(null);
JButton add = new JButton("Click this");
add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
shaper.setUnit();
}
});
add.setBounds(201, 64, 65, 23);
input.add(add);
JPanel output = new JPanel();
tabbedPane.addTab("Second", null, output, null);
output.setLayout(null);
output.add(shaper);
frame.pack();
frame.setVisible(true);
}
}
答案1
得分: 1
根据tgdavies的评论,我找到了解决方案。
paintComponent
不是对象构造的一部分,而只会在面板可见时调用。也就是说,当它被绘制时。即使调用repaint()
,也不会在面板可见之前调用paintComponent
。因此,面板首次被查看时,此代码部分仅执行一次:
if (isFirstTime)
{
area = new Rectangle(dim);
rect.setLocation(0, 0);
isFirstTime = false;
}
在执行一次后,isFirstTime
被设置为false,因此不会再次运行代码部分。因此,调用setUnit()
的确会覆盖方块的初始位置。然后上面显示的代码部分将其设置回0,0。
只需注释掉读取rect.setLocation(0, 0);
的代码行,就可以解决这个问题。
英文:
Based on tgdavies comment, I found the solution to the situation.
paintComponent
is not part of the construction of the object, but rather is only called whenever the panel is seen. That is, when it is painted. Even calling repaint()
is not going to call paintComponent
until the panel is seen. Thus, the first time the panel is viewed then this section of code is executed exactly once:
if (isFirstTime)
{
area = new Rectangle(dim);
rect.setLocation(0, 0);
isFirstTime = false;
}
After being executed once, isFirstTime
is set to false, and thus doesn't run the section of code again. Thus, calling setUnit()
does, indeed, overwrite the square's original position. And then the section of code shown above sets it back to 0,0.
Simply commenting out the line of code that reads rect.setLocation(0, 0);
fixes the problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论