GUI更新在IDE中有效,但并不总是来自jar文件

时间:2013-11-29 03:33:59

标签: java eclipse swing user-interface

我在运行OSX 10.7.5的Mac上使用Eclipse IDE进行开发。此程序构造一个JPanel数组,当用户单击一个面板时,同一行或列上的所有其他面板都会更改颜色,蓝色表示鼠标左键,红色表示右键。当我从IDE运行时一切正常,至少我从未见过它失败。但是当我创建一个.jar文件并通过双击运行时,大部分时间它都能正常工作,但每隔一段时间它就不会改变其他一些面板的颜色。

要测试我选择一个正方形并交替右键单击并左键单击它。正如我所说,大部分时间它都有效,而同一行或同一列中的所有其他面板都会改变颜色。但是,如果我右键单击一个正方形而某些面板保持蓝色则表示失败。如果我再次右击同一个方格,它不会改变显示;失败的面板保持蓝色。这就像经理认为一切都是正确的颜色,并不打扰更新显示。

如果我调整面板大小或使用任一按钮单击任何其他正方形,则所有面板都会翻转为正确的颜色。这段代码没有显式调用repaint(),但我已经尝试过了,并没有解决问题。我曾尝试阅读有关重绘管理器的信息,但还没有找到让显示器可靠更新的方法。正如我所说,我在运行IDE时从未见过这种情况,大部分时间它也可以作为应用程序运行。刚才需要大约50次点击才能获得失败。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GUITest extends JPanel {

    final static int NROWS = 81;
    final static int TOTAL_SQ = NROWS * NROWS;
    Square[] square = new Square[TOTAL_SQ];

    public GUITest() {
        setLayout(new GridLayout(NROWS, NROWS));
        for (int i = 0; i < TOTAL_SQ; i++) {
            square[i] = new Square(i);
            add(square[i]);
        }

    }

    public void squareClicked(int id, int button) {
        // determine which row and column
        int row = id / NROWS;
        int col = id % NROWS;
        // change background color of all squares on same row or column
        for (int i = 0; i < TOTAL_SQ; i++) {
            if ((row == i / NROWS) || (col == i % NROWS))
                if (button == MouseEvent.BUTTON3)
                    square[i].setBackground(Color.RED);
                else
                    square[i].setBackground(Color.BLUE);
        }
    } // squareClicked

    class Square extends JPanel implements MouseListener {
        int id = 0; // which square this is

        public Square(int id) {
            this.id = id;
            setBorder(BorderFactory.createLineBorder(Color.black));
            addMouseListener(this);
        } // Square constructor

        public void mouseClicked(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mousePressed(MouseEvent e) {
        }

        public void mouseReleased(MouseEvent e) {
            squareClicked(id, e.getButton());
        }
    } // Square

    public static void main(String[] args) {
        JFrame frame = new JFrame("GUI Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        GUITest app = new GUITest();
        frame.setSize(800, 600);
        frame.setLocationRelativeTo(null);
        frame.getContentPane().add(app, BorderLayout.CENTER);
        frame.setVisible(true);
    }
}

我还有第二个问题可能与此有关。 IDE显示比应用程序快得多。 IDE会快速更新。应用程序单独更新每个面板,速度明显变慢。我猜IDE正在使用双缓冲显示器。我也读到了这一点,但是在整个显示器准备就绪之前,看不到如何让它保持更新状态。

提前致谢。

1 个答案:

答案 0 :(得分:1)

我没有看到您的逻辑有任何问题,当我在Windows 7上使用JDK7从命令行运行代码时,我无法复制您的问题。

但是,当我运行代码时,我在框架加载时会出现异常:

C:\Java>java GUITest
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Comparison method violates its general contract!
        at java.util.TimSort.mergeLo(Unknown Source)
        at java.util.TimSort.mergeAt(Unknown Source)
        at java.util.TimSort.mergeCollapse(Unknown Source)
        at java.util.TimSort.sort(Unknown Source)
        at java.util.TimSort.sort(Unknown Source)
        at java.util.Arrays.sort(Unknown Source)
        at java.util.Collections.sort(Unknown Source)
        at javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(Unknown Source)
        at javax.swing.SortingFocusTraversalPolicy.getFocusTraversalCycle(Unknown Source)
        at javax.swing.SortingFocusTraversalPolicy.getFirstComponent(Unknown Source)
        at javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(Unknown Source)
        at javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(Unknown Source)

我尝试的第一件事是确保所有代码都在EDT上执行:

EventQueue.invokeLater(new Runnable()
{
    public void run()
    {
        JFrame frame = new JFrame("GUI Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        GUITest app = new GUITest();
        frame.getContentPane().add(app, BorderLayout.CENTER);
        frame.setSize(800,600);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
});

我仍然有例外。然后我尝试更改代码以减少单个面板上显示的组件数量:

setLayout( new GridLayout(NROWS,1) );
int squares = 0;

for (int i = 0; i < NROWS; i++)
{
    JPanel panel = new JPanel( new GridLayout(1, NROWS) );

    for (int j = 0; j < NROWS; j++)
    {
        square[squares] = new Square(squares);
        panel.add( square[squares] );
        squares++;
    }

    add( panel );
}

我不再得到例外。不知道这是否有帮助。

此外,Swing默认是双缓冲的,所以我不确定为什么每个组件都单独更新。 Swing应该组合对单个组件的重绘请求,以便它们一次完成,但可能只有组件组被重新绘制。

唯一的解决方法是不使用组件,而是创建自己的组件,对每个矩形进行自定义绘制。当然,这将涉及更多,因为您还需要进行自己的“命中检测”以确定单击了哪个矩形。