在不可见的JPanel中更新数值。

huangapple go评论70阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2020年10月12日 13:26:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/64312135.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定