如何将对象添加到JFrames

时间:2012-05-21 22:31:17

标签: java image swing object jframe

我正在尝试在JFrame中制作游戏,但遇到了问题。我创建了一个由四个图像组成的对象。我的问题是,我如何在JFrame中绘制这个对象?

以下是代码:

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.*;

public class t4
{
    static boolean running;

    public static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    public static double width = screenSize.getWidth();
    public static double height = screenSize.getHeight();

    public static int x = ( 250 );
    public static int y = ( 150 );

    public static final int sx = (int)width;
    public static final int sy = (int)height;

    public static void main( String[] args ) throws IOException, InterruptedException
    {
        Image ur = new ImageIcon("redBlock.gif").getImage();
        Image ll = new ImageIcon("redBlock.gif").getImage();
        Image ul = new ImageIcon("blueBlock.gif").getImage();
        Image lr = new ImageIcon("blueBlock.gif").getImage();

        // Create game window...
        JFrame app = new JFrame();
        app.setIgnoreRepaint( true );
        app.setUndecorated( true );

        // Add ESC listener to quit...
        app.addKeyListener( new KeyAdapter()
        {
            public void keyPressed( KeyEvent e )
            {
                if( e.getKeyCode() == KeyEvent.VK_ESCAPE )
                    running = false;
                if((e.getKeyCode()==KeyEvent.VK_LEFT)||(e.getKeyCode()==KeyEvent.VK_KP_LEFT))
                    x-=10;
                if((e.getKeyCode()==KeyEvent.VK_RIGHT)||(e.getKeyCode()==KeyEvent.VK_KP_RIGHT))
                    x+=10;
                if((e.getKeyCode()==KeyEvent.VK_UP)||(e.getKeyCode()==KeyEvent.VK_KP_UP))
                    y-=10;
                if((e.getKeyCode()==KeyEvent.VK_DOWN)||(e.getKeyCode()==KeyEvent.VK_KP_DOWN))
                y+=10;
            }
        });

        // Get graphics configuration...
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();

        // Change to full screen
        gd.setFullScreenWindow( app );
        if( gd.isDisplayChangeSupported() )
        {
            gd.setDisplayMode(new DisplayMode( sx, sy, 32, DisplayMode.REFRESH_RATE_UNKNOWN ));
        }

        // Create BackBuffer...
        app.createBufferStrategy( 2 );
        BufferStrategy buffer = app.getBufferStrategy();

        // Create off-screen drawing surface
        BufferedImage bi = gc.createCompatibleImage( sx, sy );

        // Objects needed for rendering...
        Graphics graphics = null;
        Graphics2D g2d = null;
        Color background = Color.BLACK;
        Random rand = new Random();

        // Variables for counting frames per seconds
        int fps = 0;
        int frames = 0;
        long totalTime = 0;
        long curTime = System.currentTimeMillis();
        long lastTime = curTime;

        running = true;
        while( running )
        {
            try
            {
                //    wait(500);

                // count Frames per second...
                lastTime = curTime;
                curTime = System.currentTimeMillis();
                totalTime += curTime - lastTime;
                if( totalTime > 1000 )
                {
                    totalTime -= 1000;
                    fps = frames;
                    frames = 0;
                } 
                ++frames;

                // clear back buffer...
                g2d = bi.createGraphics();
                g2d.setColor( background );
                g2d.fillRect( 0, 0, sx, sy );

                // draw some rectangles...
                /* int r = 45;
                int g = 232;
                int b = 163;
                g2d.setColor( new Color(r,g,b) );
                int w = ( 250 );
                int h = ( 150 );
                g2d.fillRect( x+25, y+25, w, h );*/

                if(y<775)
                {
                    y++;
                }
                else
                {
                    y=0;
                }

                // display frames per second...
                g2d.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
                g2d.setColor( Color.GREEN );
                g2d.drawString( String.format( "FPS: %s", fps ), 20, 20 );

                // Blit image and flip...
                graphics = buffer.getDrawGraphics();
                graphics.drawImage( bi, 0, 0, null );

                graphics.drawImage(ur,x,y,null);
                graphics.drawImage(ll,x+50,y+50,null);
                graphics.drawImage(ul,x,y+50,null);
                graphics.drawImage(lr,x+50,y,null);

                if( !buffer.contentsLost() )
                    buffer.show();

            }
            finally
            {
                // release resources
                if( graphics != null ) 
                    graphics.dispose();
                if( g2d != null ) 
                    g2d.dispose();
            }
        }

        gd.setFullScreenWindow( null );
        System.exit(0);
    }

    public static void wait(int x) throws InterruptedException
    {
        Thread.currentThread().sleep(x);
    }
}

我想创建一个包含图像ur,ll,ul和lr的对象,并能够在屏幕上绘制它。

1 个答案:

答案 0 :(得分:6)

这是你应该做的:

  1. 修改类以使其扩展javax.swing.JComponent
  2. 覆盖paintComponent(Graphics)
  3. 创建javax.swing.Timer以管理帧速率。
  4. 覆盖getPreferredSize()
  5. 首先(根据DavidB的要求)我将解释为什么你应该做这些事情,然后我会告诉你如何

    解释

    • 由于您尝试将组件添加到JFrame,因此您需要组件的类与JFrame的add方法兼容(实际上,它属于Container,但这并不重要)。如果你看the JavaDoc documentation for add,你会发现它不会接受任何Object;相反,它需要一个Component(或其子类)的实例。 可以子类Component而不是JComponent,但Component比AWing应用程序更适用于AWT应用程序。
      简而言之:子类JComponent,以便JFrame.add接受它作为参数。
    • 一旦你进行了JComponent的子类化,你就需要实际告诉窗口管理器要绘制什么。您可以将绘图代码放在任何位置,但请记住,除非实际调用该方法,否则不会调用(使用)它。图形环境调用以启动绘制过程的方法称为paintComponent *。如果覆盖此方法,则图形环境将调用您的自定义绘制代码 简而言之:覆盖paintComponent,因为这是图形环境所关注的内容。
    • 由于您最有可能在游戏中制作动画,因此您希望保持每秒帧数的恒定速度,对吧?如果你不这样做,有许多因素(计算机能力,其他运行应用程序,绘图复杂性等)可能会使帧速率变得混乱。要执行此操作,您需要每秒调用repaint方法指定的次数(每帧一次)。这是Swing计时器的要点。你给它一个代码块和几毫秒,它会在每次指定的间隔时间后运行该代码。
      简而言之:使用Swing计时器,以便您可以保持帧速率不变并受到控制。
    • 想象一下,你有一个文字处理应用程序。它顶部有一个菜单栏,中间有一个文档窗口,底部有一个工具栏。显然,您希望菜单栏和工具栏很小,并且文档占用尽可能多的空间,对吧?这就是为什么你需要让每个组件告诉你它的大小应该是什么,称为首选大小。覆盖getPreferredSize允许您返回所需的任何大小,从而控制组件的大小。**
      简而言之:覆盖getPreferredSize,以便窗口管理器和图形环境能够正确地完成所有尺寸。

    *实际上并没有paintComponent被调用;它是paint。但是,paint方法会调用paintComponentpaintBorderpaintChildren

      

    这种方法实际上将绘画作品委托给三个受保护的人   方法:paintComponent,paintBorder和paintChildren。他们&#39;再   按列出的顺序调用,以确保孩子出现在上面   组件本身。一般来说,组件及其子组件   不应该在分配给边框的insets区域中绘制。   子类可以像往常一样覆盖此方法。一个子类   只是想专门化UI(外观和感觉)代表的绘画   方法应该只覆盖paintComponent。

    (来源:the JavaDoc

    **覆盖getPreferredSize实际上并不能保证这是组件显示的大小。它仅指定显示的大小。一些布局管理器会选择忽略它(例如BorderLayout)。但是,当您调用pack来正确调整窗口大小时,它应根据此大小计算首选大小。

    程序

    扩展JComponent

    要使类扩展JComponent,只需将类签名更改为:

    import javax.swing.JComponent;
    
    public class MyGameDisplay extends JComponent {
        ...
    }
    

    覆盖paintComponent

    您需要导入java.awt.Graphics课程。请参阅此示例代码,了解如何使用paintComponent

    import javax.swing.JComponent;
    import java.awt.Graphics;
    
    public class MyGameDisplay extends JComponent {
        // Some code here
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g); // this line is crucial; see below
            g.drawString(100,100,"Hello world");
        }
    }
    

    注意:上面,我提到了在super.paintComponent方法中调用paintComponent的必要性。这样做的原因是它(除其他外)将清除您之前显示的所有图形。因此,例如,如果您的程序在屏幕上绘制一个圆圈,则除非您致电super.paintComponent,否则图形的每次迭代都将包含前一个绘图中的圆圈轨迹。

    使用Timer

    要获得所需的FPS速率,请修改类以包含Swing计时器,如下所示:

    // Include these imports:
    import javax.swing.Timer;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class MyGameDisplay extends JComponent {
        private Timer t;
        public MyGameDisplay() {
            ActionListener al = new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            }
            t = new Timer(1000 / 30 /* frame rate */, al);
            t.start();
        }
    }
    

    覆盖getPreferredSize

    覆盖getPreferredSize的原因是布局管理员知道如何正确调整容器大小。

    虽然编写实际逻辑来计算大小可能很困难,但覆盖getPreferredSize本身并不是很重要。这样做:

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(400, 400); // for example
    }
    

    完成所有操作后,您只需运行以下代码:

    import javax.swing.JFrame;
    
    public class Test {
        public static void main(String[] args) {
            JFrame frame = new JFrame();
            MyGameDisplay mgd = new MyGameDisplay();
            frame.add(mgd);
            frame.pack();
            frame.setVisible(true);
        }
    }