调用paint()方法

时间:2015-07-06 21:03:14

标签: java swing jframe paint bufferedimage

package Game;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;

import Maps.*;

public class Window extends JFrame implements KeyListener
{
    private Insets insets;
    private int currentMapX;
    private int currentMapY;

    public Window()
    {
        super();
        setSize(new Dimension(1920, 1080));
        setLayout(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setUndecorated(true);
        setFocusable(true);
        setContentPane(new Container());
        setBackground(Color.BLACK);
        addKeyListener(this);
    }

    public void startGame()
    {
        insets = getInsets();
        currentMapX = 960 - (Game.level_01.getWidth() / 2); 
        currentMapY = 540 - (Game.level_01.getHeight() / 2);
        Game.level_01.setBounds(currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
        add(Game.level_01);
    }

    private void moveMapRight()
    {
        Game.level_01.setBounds(++currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
    }

    private void moveMapLeft()
    {
        Game.level_01.setBounds(--currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
    }

    public void paint(Graphics g)
    {
        super.paint(g);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Game.player.paint(g2d);
    }

    public void keyPressed(KeyEvent k) 
    {
        if(k.getKeyCode() == KeyEvent.VK_RIGHT) moveMapRight();
        if(k.getKeyCode() == KeyEvent.VK_LEFT) moveMapLeft();
    }
    public void keyReleased(KeyEvent k){}
    public void keyTyped(KeyEvent k){}
}

我有第一个扩展JFrame的类,并包含以下类。

package Maps;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.io.*;
import java.util.*;

import javax.swing.*;

import Game.*;
import Tiles.*;

public class Level extends JPanel
{
    protected final File txt_MAP;
    protected final ArrayList<Tile> jLabel_MAP;
    protected final ArrayList<Integer> linesLength;
    protected Insets insets = Game.window.getInsets();
    protected int arrayIndex;
    protected int leftIndex;
    protected int topIndex;
    protected int width;
    protected int height;

    public Level(File f)
    {
        super();
        setLayout(null);
        setBackground(Color.BLACK);
        leftIndex = 0;
        topIndex = 0;
        txt_MAP = f;
        jLabel_MAP = new ArrayList<>();
        linesLength = new ArrayList<>();
        arrayIndex = 0;
        readTxt();
        width = linesLength.get(0) * Game.tileSize;
        height = linesLength.size() * Game.tileSize;
        addTiles();
    }

    private void readTxt()
    {
        BufferedReader br = null;
        try
        {
            br = new BufferedReader(new FileReader(txt_MAP));
        }
        catch(FileNotFoundException e){}
        try
        {
            String line = br.readLine();
            while(line != null)
            {
                String[] words = line.split(" ");
                for(int a = 0; a < words.length; a++)
                {
                    for(int b = 0; b < Game.tilesIcons.length; b++)
                    {
                        if(Game.tilesList[b].getName().equals(words[a] + ".gif"))
                        {
                            if(Game.tilesList[b].getName().contains(Game.grass_TYPE)) jLabel_MAP.add(arrayIndex, new Grass(Game.tilesIcons[b]));
                            if(Game.tilesList[b].getName().contains(Game.soil_TYPE)) jLabel_MAP.add(arrayIndex, new Soil(Game.tilesIcons[b]));
                            if(Game.tilesList[b].getName().contains(Game.sky_TYPE)) jLabel_MAP.add(arrayIndex, new Sky(Game.tilesIcons[b]));
                            arrayIndex++;
                        }
                    }
                }
                linesLength.add(words.length);
                line = br.readLine();
            }
        }
        catch(IOException e){}
    }

    private void addTiles()
    {
        for(int i = 0; i < jLabel_MAP.size(); i++)
        {
            jLabel_MAP.get(i).setBorder(null);
            jLabel_MAP.get(i).setBounds(insets.left + leftIndex, insets.top + topIndex, 64, 64);
            add(jLabel_MAP.get(i));
            if(leftIndex == width - Game.tileSize) 
            {
                leftIndex = 0;
                topIndex += 64;
            }
            else leftIndex += 64;
        }
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }
}

此类扩展JPanel并包含Jlabels的arrayList。

package Player;

import java.awt.*;

import Game.*;

public class Player
{
    private int x;
    private int y;
    private int xa;
    private int ya;
    private Graphics2D g2d; 
    public Player(double x, double y)
    {
        super();
        this.x = (int)x;
        this.y = (int)y;
        xa = 0;
        ya = 1;
    }

    public void movePlayer()
    {
        x += xa;
        y += ya;
    }

    public void setDirection(int xa, int ya)
    {
        this.xa = xa;
        this.ya = ya;   
    }

    public void paint(Graphics g)
    {
        g2d = (Graphics2D)g;
        g2d.drawImage(Game.playerImages[6], x , y, Game.tileSize, Game.tileSize, null);
    }

    public int getX()
    {
        return x;
    }

    public int getY()
    {
        return y;
    }

    public int getXA()
    {
        return xa;
    }

    public int getYA()
    {
        return ya;
    }
}

最后这个类是玩家的类,即BufferedImage。我的问题是,当我启动程序时,播放器图像开始闪烁,因为当我在JFrame类中调用paint()方法时,它首先绘制jpanel,然后绘制播放器图像。如何解决图像闪烁问题? 提前谢谢。

2 个答案:

答案 0 :(得分:4)

正如其他人所说,你不应该覆盖顶层组件中的paint(Graphics g),这是问题的一部分,但你也需要小心确保你只画画每次重新粉刷一次到屏幕:

  

我的问题是,当我启动程序时,播放器图像开始播放   闪烁,因为当我在JFrame中调用paint()方法时   类,这首先描绘了jpanel,然后是玩家形象。

现在发生的事情是,每次调用修改Graphics方法中paint(Graphics screen)对象的方法时,它都会直接修改屏幕内容,强制屏幕显示在你完成绘制你真正想要的东西之前刷新 - Player。通过首先绘制到super,然后再使用自定义渲染,您实际绘制到屏幕至少两次,从而导致闪烁。你可以通过双缓冲解决这个问题。

双缓冲涉及首先渲染图像,然后将该图像绘制到屏幕上。使用Component类提供的内置方法(请记住JPanel扩展Component),您可以获得组件可视区域的大小。创建一个相同大小的BufferedImage,然后拨打bufferedImage.createGraphics() - 这将为您提供一个Graphics2D对象,您可以使用该对象在bufferedImage上绘制。完成向bufferedImage的渲染后,请致电screen.drawImage(bufferedImage,0,0,null)。这允许您根据需要多次修改bufferedImage而无需实际进行屏幕刷新,然后通过一次调用将缓冲区的内容绘制到屏幕上。

附加信息

我之前应该指出,我对你在屏幕上的实际情况一无所知,所以你可能需要对你的播放机屏幕区域和上面的区域进行一些额外的检查。 left应放在drawImage(Image,x,y,ImageObserver)的调用中。还要注意BufferedImage中的透明度或缺乏透明度 - 如果您在屏幕上绘制其他重要内容的不透明像素,则很容易迷失。

尝试(但不要保留)

同样,我不建议您在顶层组件中进行所有绘画,但是您可以尝试在此期间执行此类操作:

//This is in the Window class:
public void paint(Graphics screen)
{
    //Render everything first to a BufferedImage:
    BufferedImage bufferedImage = ... //Initialize based on Window's
                                      //size and bounds.
    Graphics2D buf = bufferedImage.createGraphics();
    super.paint(buf);
    buf.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);
    Game.player.paint(buf);

    //Now paint the buffer to screen:
    int x = ... //set to top-left x-coordinate, probably 0
    int y = ... //set to top-left y-coordinate, probably 0
    screen.drawImage(buf,x,y,null);
}

答案 1 :(得分:2)

  1. 不要覆盖paint(Graphics)等顶层容器中的JFrame(不是双缓冲)。而是在JPanel中进行自定义绘画(双重缓冲)。
  2. 此外,当覆盖任何绘制方法时,立即调用super方法,从而绘制背景并有效地删除先前的绘图。