【问题标题】:Update values in a JPanel that is not visible更新 JPanel 中不可见的值
【发布时间】:2021-01-26 10:18:16
【问题描述】:

一段时间以来,我一直对我的代码中的一些非常奇怪的行为感到沮丧,在花时间慢慢地一点一点地整理我的代码之后,我终于找到了问题的根源。

概述:使用 Java Swing,以下代码创建了一个选项卡式界面。用户可见的第一个选项卡有一个按钮。第二个标签的左上角有一个蓝色方块。

应该发生什么:程序打开后,首先单击按钮,然后打开另一个选项卡。该按钮调用另一个选项卡上的函数,导致方块移动到新位置。因此,另一个选项卡应该在新位置显示正方形,而不是左上角。

实际发生的情况:如果先单击按钮然后打开选项卡,方块的位置不会改变。它保留在左上角,就好像从未按下按钮一样。如果您先打开选项卡,它似乎会以某种方式“启动”程序,所以现在按钮可以按预期工作。

当然,首先单击选项卡以确保程序正常运行似乎有点烦人,但这可能是一个非常大的问题。为什么标签至少被查看一次后才能更新?

观察:在调试代码时,我可以单步执行setUnit() 函数,并验证正方形实际上已成功更改,完全覆盖了之前的位置。然而,当我打开第二个选项卡时,正方形的位置现在恢复到以前的位置。如果此时检查变量,则表明正方形的原始位置完全保持不变,就好像从未调用过 setUnit() 函数一样。知道除非重新绘制这些组件不会在视觉上更新,我确保在 setUnit() 函数中添加 repaint() 函数调用。我真的很困惑,想知道广场位置的原始值甚至存储在哪里?我可以在调试器中看到这些值被覆盖了,所以它们应该完全不存在了,对吧?

代码:

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

【问题讨论】:

  • 如果 DragPanel 从未显示过,那么第一次调用paintComponent 将在调用setUnit 之后 发生,并将矩形移回0,0。
  • @Nathan 他没有回答您的问题,而是做出了有效的观察,即最好不要在 UI 中使用绝对像素定位。你明白我的评论了吗?

标签: java swing user-interface jpanel jtabbedpane


【解决方案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); 的代码行即可解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-07
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    • 1970-01-01
    • 1970-01-01
    • 2011-10-11
    相关资源
    最近更新 更多