Canvas not drawing to JFrame

2019-08-14 08:41发布

问题:

I've been looking around for an answer to this, re-written my code multiple times, still nothing. Essentially I am trying to draw to a JFrame containing just a simple rectangle, yet each time nothing shows in the Frame - its just blank.

package com.Graphics;

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

public class GraphicsMain {

    public static void main(String[] args) {

        GraphicsMain myGraphics = new GraphicsMain();

        myGraphics.createDisplay();

    }

    void createDisplay(){

        int width = 500;
        int height = 500;
        String title = "TestFrame";
        Graphics g;

        Canvas myCanvas = new Canvas();
        JFrame myFrame = new JFrame(title);

        myFrame.setVisible(true);
        myFrame.setResizable(false);
        myFrame.setSize(width, height);
        myFrame.setLocationRelativeTo(null);
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        myCanvas.setPreferredSize(new Dimension(500, 500));
        myCanvas.setMaximumSize(new Dimension(500, 500));
        myCanvas.setMinimumSize(new Dimension(500, 500));

        myFrame.add(myCanvas);
        myFrame.pack();

        myCanvas.createBufferStrategy(2);

        BufferStrategy bs = myCanvas.getBufferStrategy();

        g = bs.getDrawGraphics();

        g.setColor(Color.red);
        g.fillRect(10, 50, 50, 70);

        bs.show();
        g.dispose();
    }
}

I realize conventions here are terrible - this is just a practice for me with graphics. Normally I would have this broken into separate classes etc. Any help is greatly appreciated. Thank you.

回答1:

BufferStrategy is a low level painting mechanism which places full control of the painting process in your hands. With this "power" comes some complexity which you need to be ready to manage

The JavaDocs and BufferStrategy and BufferCapabilities provide a number of excellent examples into how you must manage the API.

The API is volatile, this means that you need to make a number of checks to ensure that what you've been painting has been passed to the rendering pipeline/hardware correctly, otherwise you need to repeat the paint pass again. This is where you're probably having issues.

You also need to remember that, while setVisible returns immediately, this doesn't mean that the window is visible on the screen or that it's been fully realised (attached to a native peer), this can also affect when BufferStrategy is ready to paint.

For example...

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;

public class Test extends Canvas implements Runnable {

    private static final long serialVersionUID = 1L;

    public static int WIDTH = 200;
    public static int HEIGHT = 200;

    private Thread thread;
    private boolean running = false;

    public Test() {
    }

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

    public synchronized void start() {
        running = true;
        thread = new Thread(this, "Display");
        thread.start();

    }

    public synchronized void stop() {
        running = false;
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        while (running) {
            update();
            render();

            // Control frame rate
            try {
                Thread.sleep(5);
            } catch (InterruptedException ex) {
            }
        }

    }

    public void update() {
        // Make changes to the model which need to be painted
    }

    public void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(3);
            return;
        }

        do {
            do {
                Graphics2D g = (Graphics2D) bs.getDrawGraphics();
                // You MUST clear the page before painting, bad things
                // happen otherwise
                g.setColor(Color.WHITE);
                g.fillRect(0, 0, getWidth(), getHeight());
                g.setColor(Color.red);
                g.fillRect(10, 50, 50, 70);
                g.dispose();
            } while (bs.contentsRestored());
            bs.show();
        } while (bs.contentsLost());
    }

    public static void main(String[] args) {
        Test test = new Test();
        JFrame frame = new JFrame();
        frame.add(test);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        test.start();

    }

}

Now, this example also has a basic concept of a "main loop", this is responsible for providing base line from which you can generate dynamic content and render it. But you could simply try calling render one the window is made visible

As I said, BufferStrategy is a low level API, it's powerful, flexible and complex to manage.

A simpler solution might be to go a custom painting route through Swing. See Performing Custom Painting and Painting in AWT and Swing for more details. Swing components offer automatic double buffering and the paint scheduling is done for you (although you do need to use things like repaint to get a paint pass to run)