简体   繁体   中英

Update values in a JPanel that is not visible

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);
    }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM