让JLabel跨越JPanel

时间:2018-02-26 18:04:36

标签: java swing

我正试图让我的游戏中的一个桶自己向下浮动,我已经尝试了一些东西并查找了一些教程,但我似乎无法使其工作。也许我试图在paintcomponent()中做这个以错误的方式接近这个?在我的JPanel中,我还有一个car对象,它是由KeyListeners控制的JPanel的扩展。

// this is inside a JPanel class that I made that's painting out the game


OilBarrel barrel = new OilBarrel();


@Override
public void paintComponent(Graphics g) {

    super.paintComponent(g);
    road.paintComponent(g);

    this.add(car);
    this.add(barrel);

    barrel.setBounds(barrelX, barrel.getBarrelY(), 150, 180);
    int carX = car.getCarX();

    car.setBounds(carX, carY, 800, 400);
    barrel.setBounds(550, barrel.getBarrelY(), 800, 400);
    while (true) {
        barrel.move();
        repaint();
        try {
            Thread.sleep(1);

        } catch (Exception e) {
        }
    }

}



    // this is the oilbarrel class

import java.awt.Dimension;
import java.awt.Image;

 import javax.swing.ImageIcon;
import javax.swing.JLabel;


public class OilBarrel extends JLabel{

int barrelX;
int barrelY = 0;

public OilBarrel() {

    setVisible(true);
    this.setFocusable(true);
    Image img = new ImageIcon(this.getClass().getResource("/oil.png")).getImage();


    this.setIcon(new ImageIcon(img));


    }



public void move(){

    if(barrelY<1000)
     barrelY +=1;

}



public int getBarrelY() {
    return barrelY;
 }

}

2 个答案:

答案 0 :(得分:3)

Swing是单线程的,这意味着for-loop方法中的paintComponent阻止了事件调度线程,阻止它更新UI。

有关详细信息,请参阅Concurrency in Swing

解决问题的最简单的解决方案是使用Swing Timer,因为它允许在EDT之外安排一个小延迟,但是它会触发它在EDT上的更新,使其成为可能可以安全地从内部更新UI。

组件动画并不难,但确实需要不同的工作流程,例如:

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel label;
        private int xDelta = 1;

        public TestPane() {
            setLayout(null);
            label = new JLabel("*");
            label.setSize(label.getPreferredSize());
            label.setLocation(0, (200 - label.getHeight()) / 2);
            add(label);
            Timer timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int x = label.getX();
                    x += xDelta;
                    if (x + label.getWidth() > getWidth()) {
                        x = getWidth() - label.getWidth();
                        xDelta *= -1;
                    } else if (x < 0) {
                        x = 0;
                        xDelta *= -1;
                    }
                    label.setLocation(x, label.getY());
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

}

作为个人喜好,我会避免使用组件来完成这项工作,并倾向于使用完整的自定义绘画路线。

这有很多原因,组件是复杂的对象,需要很多细节。向他们应用额外的效果或转换也很困难

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Rectangle box;
        private int xDelta = 1;

        public TestPane() {
            setLayout(null);
            box = new Rectangle(0, 75, 50, 50);
            Timer timer = new Timer(10, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int x = box.x;
                    x += xDelta;
                    if (x + box.width > getWidth()) {
                        x = getWidth() - box.width;
                        xDelta *= -1;
                    } else if (x < 0) {
                        x = 0;
                        xDelta *= -1;
                    }
                    box.setLocation(x, box.y);
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.draw(box);
            g2d.dispose();
        }

    }

}

答案 1 :(得分:0)

当线程需要更新GUI时,请使用SwingUtilities.invokeLater。 https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)

Thread t = new Thread(){
                public void run(){
                    while( true ){
                        // queue on Event Dispatch Thread
                        SwingUtilities.invokeLater( new Runnable(){
                            public void run(){
                                barrel.move();
                                repaint();
                            }
                        });
                        try{
                            Thread.sleep( 100 );
                        }catch( InterruptedException ie ){}
                    }
                }
            };
            t.start();

另外,查找Swing Timer。我认为这可能会更好。