【问题标题】:previously displayed images disappear after mouse-click以前显示的图像在鼠标单击后消失
【发布时间】:2013-05-09 20:42:54
【问题描述】:

我正在为类似战舰的联网游戏编写 gui(是的,我也是)。 如果我右键单击设置一个航点,所有的 mapLabels 都会消失并且我没有得到它,因为所有右键单击应该做的就是向服务器发送一些字符串并且不会以任何方式影响显示本身。 如果我调用 panel.repaint();窗格.验证();设置航路点后,所有内容都会再次显示,但性能接近于慢到无法想象的速度。

我意识到,这是很多代码,但我无法想象 FieldListener 中的问题,尽管它一定是。因此,我不知道要发布什么。如果你想看其他东西,你可以要求它......

这是我们代码的一部分,很可能是造成问题的原因:

/**
 * The listener for a field.
 * @author Andris
 *
 */
private class FieldListener implements MouseListener {

    private int[] xy;
    private int[] pressedPos = new int[] {-1,-1};


    @Override
    public void mousePressed(MouseEvent evt) {
        Field field = map.getField(xy[0],xy[1]);
        pressedPos = xy.clone();
        switch(evt.getButton()){
        case MouseEvent.BUTTON1://-------well, BUTTON1......
            break;
        case MouseEvent.BUTTON3:
            switch(selBtn){
            case 2://---------------this is the case
                client.Out("some string");// this sends to the server, nothing else...
                break;
            }
            break;
        }
    }

在服务器回答后执行(在来自不同包的完全不同的类中,但是所有消失的字段都是私有的):

public class Server {
   client.Out(cfg.chat_server+"you have set a waypoint at ("+x+","+y+").");

public class ChatFrame extends JPanel {
    public void Out(String nextString){
        text.append(" "+nextString);
        text.append("\n");
        JScrollBar scroll = display.getVerticalScrollBar();
        if(!scroll.getValueIsAdjusting())scroll.setValue(Integer.MAX_VALUE);
    }

这是自定义的绘制方法(可能是因为 ImageObserver 是错误的):

private class MapLabel extends JLabel{ //------------- in MapFrame

    private Field field;
    private int[] xy;

    public void paint(Graphics g){
        Image image = getImageNsetToolTip();
        Graphics2D g2D=(Graphics2D)g;
        g2D.drawImage(image, 0, 0, actImgSize, actImgSize, this);
        g2D.setClip(0, 0, actImgSize, actImgSize);
        super.paint(g2D);
    }

    /**
     * gets the proper image and sets the tool-tip
     * @return
     */
    private Image getImageNsetToolTip(){
        Image result;
        String toolTip = "("+xy[0]+","+xy[1]+")";
        TileType type = field.getType();
        switch(type){
        case harbor:
            result=harborImg[0];
            break;
        case land: 
//------------------------------etc...


        this.setToolTipText(toolTip);
        return result;
    }

以下是其余部分:

...lots of imports...

/**
 * This is the frame in which the GameMap is displayed.
 * @author Andris
 *
 */
@SuppressWarnings("serial")
public class MapFrame extends JPanel {

...lots of variables...

    /**
     * Creates the frame, but doesn't make it visible yet.
     * @param window the GameWindow in which this frame will be embedded.
     * @param client the client who runs this.
     */
    public MapFrame(GameWindow window, Client client){

...lots of variables initialized...

        panel = new JPanel();
        pane = new JScrollPane(panel,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);


        buttons = new JButton[nButtons];
        buttonListener = new ButtonListener();
        for(int i=0; i<nButtons; i++){
            buttons[i] = new JButton(buttonTexts[i]);
            buttons[i].setName(buttonTexts[i].replace(' ', '_'));
            buttons[i].addActionListener(buttonListener);
            buttonPanel.add(buttons[i]);
        }

【问题讨论】:

  • 嗨!欢迎来到 SO。不幸的是,不太可能有人会回答这个问题,因为它包含太多代码并且不完整;所以很难帮你。付出一些努力并发布SSCCE,这将立即为您提供宝贵的帮助。
  • 因为我不知道问题出在哪里,而且我们的代码大约有 3000 多行,所以我不认为它包含太多代码,我无法在较小的程序中重现问题。 ..
  • 因为我不知道问题出在哪里,而我们的代码大约有 3000 多行 因此SSCCE 的想法。如果您遵循链接中描述的方法,它将大大提高发现问题的机会并获得重现问题的较小程序。

标签: java swing jpanel paint jlabel


【解决方案1】:

你的绘制代码有问题

public void paint(Graphics g){
    Image image = getImageNsetToolTip();
    Graphics2D g2D=(Graphics2D)g;
    g2D.drawImage(image, 0, 0, actImgSize, actImgSize, this);
    g2D.setClip(0, 0, actImgSize, actImgSize);
    super.paint(g2D);
 }

super.paint 之前绘画可能会抹掉您之前绘画的内容。这是因为paint 调用paintComponentpaintBorderpaintComponents

paintComponent 的工作之一是为绘制准备图形上下文(清理)

您还应该避免弄乱剪辑。由于在 Swing 中绘制的工作方式,您冒着允许组件绘制超出其物理边界的风险。在调用paint 之前,剪辑由重绘管理器设置为等于组件的大小

相反,您应该使用paintComponent 来执行您的自定义绘画

查看Perofming custom painting了解更多详情

现在,我想到的问题是,为什么要在 JLabel 上绘制自定义图像,而 JLabel 的功能之一是显示图标?

【讨论】:

  • 我正在努力提高性能。有人告诉我宁愿重写 paint 或 paintCompnent 方法而不是使用 ImageIcons(据称更有效的重新缩放),尽管我没有发现它(明显)更有效。
  • 我尝试了您的建议,但它们并没有解决主要问题(尽管省略 setClip 似乎没有负面影响,因此肯定是一种改进)。既然你提到了它,我意识到,我可能应该坚持我(自己和)使用 ImageIcons 的旧计划并放弃提高性能。谢谢
  • 因此是最后一条语句 ;)
【解决方案2】:

我终于明白了:
有很多线程在运行,因为服务器、客户端、每个连接等等都在它们自己的线程中运行。
可能导致摇摆事件队列起作用,结果是事情没有按预期执行。
这通常只发生在有 许多 个其他线程的情况下(因此不可能创建 SSCCE)。
为了防止这种情况,像 validate() 或 repaint() 这样的每个方法都应该作为 invokeLater 调用(因此由事件队列正确处理),特别是如果它们是从不同的线程调用的。
所以这就是我到处写的:

    SwingUtilities.invokeLater(new Runnable(){
        @override
        public void run(){
            panel.validate();
            panel.repaint();
            pane.validate();
        }
    });

或:

    SwingUtilities.invokeLater(new Runnable(){
        @override
        public void run(){
            mapLabels[x][y].repaint();
        }
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-26
    • 1970-01-01
    • 2012-03-15
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 1970-01-01
    相关资源
    最近更新 更多