【问题标题】:JPanel position whacked by BufferStrategyJPanel 位置被 BufferStrategy 重击
【发布时间】:2019-04-05 03:26:26
【问题描述】:

我有一个 JFrame,我在其中添加了一个 JPanel。我正在做一些动画,所以我实现了一个 BufferStrategy 进行渲染。我还有一个渲染循环,让它在运行时保持渲染。

如果我正常运行程序,JPanel 会正确呈现。当然,那么就没有动画了。如果我使用循环和 hte BufferedStrategy 运行它,JPanel 会扩展到应用程序的完整大小,并位于 JFrame 的标题栏下方。我找不到发生这种情况的充分理由,但这很令人沮丧,因为我需要做一些精确的绘图,并且不能将其中的一些隐藏在标题栏下方。

我认为这是因为我没有调用 super.paintComponent(),但我真的不应该调用它,因为我是自己渲染的,而不是在正常的 Swing 管道中。

是否需要进行一些 API 调用才能使 JPanel 在我的渲染调用中正确定位?

import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;

public class MainFrame extends JFrame implements Runnable {

    private static final long serialVersionUID = 2190062312369662956L;

    protected ViewPanel _viewPanel = null;

    public MainFrame() {
        setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

        createGui();
    }

    protected void createGui() {

        setSize( 600, 400 );
        setTitle( "Exact Positioning" );
        setVisible( true );
        setResizable( false );

        _viewPanel = new ViewPanel();
        _viewPanel.init();

        // the layout type shouldn't matter since this is the only component in the frame
        add( _viewPanel );
    }

    @Override
    public void run() {

        // setup
        this.createBufferStrategy( 2 );
        BufferStrategy buffStrategy = this.getBufferStrategy();

        // render loop
        while( true ) {

            Graphics g = null;
            try {
                g = buffStrategy.getDrawGraphics();
                _viewPanel.render( g );
            } finally {
                g.dispose();
            }
            buffStrategy.show();

            // pause a tad
            try {
                Thread.sleep( 500 );
            } catch (InterruptedException e) {
                // Required catch block
                e.printStackTrace();
            } catch (Exception e) {
                System.out.println( "Sorry, don't know what happened: " + e.toString() );
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {

        Thread t1 = new Thread(new MainFrame()); 
        t1.start();
        // if I start the app this way, the JPanel draws correctly
//      MainFrame a = new MainFrame(); 
    }
}

JPanel:

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

import javax.swing.JPanel;

public class ViewPanel extends JPanel {

    private static int APP_WIDTH = 600;
    private static int APP_HEIGHT = 400;

    private static final long serialVersionUID = -8019663913250286271L;

    public ViewPanel() {
        setBackground(Color.GRAY);
    }

    public void init() {
    }

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent( g );

        render( g );
    }

    // Where I do the drawing. It's called from the rendering loop in the JFrame
    public void render( Graphics g ) {

        // refresh the background since we're not relying on paintComponent all the time
        Color bgc = getBackground();
        g.setColor( bgc );
        g.fillRect( 0, 0, APP_WIDTH, APP_HEIGHT );

        // just paint a moving box
        drawBox( g );

        // draw a line to prove correctness. In the loop, you can see part of this line is hidden
        // underneath the title bar
        g.setColor( Color.red );
        g.drawLine(0, 0, APP_WIDTH, APP_HEIGHT);
    }

    protected void drawBox( Graphics g ) {

        // get a random color
        Random ran = new Random();
        int red = ran.nextInt( 255 );
        int grn = ran.nextInt( 255 );
        int ble = ran.nextInt( 255 );
        Color colour = new Color( red, grn, ble );
        g.setColor( colour );

        // get a random position        
        int x = ran.nextInt( APP_WIDTH - 50);
        int y = ran.nextInt( APP_HEIGHT - 50);

        // draw it
        g.fillRect( x, y, 50, 50 );
    }
}

【问题讨论】:

  • 不,不,不,一千次不。 Swing 使用被动渲染引擎,您不能用我们的实现替换它。当一个组件被绘制时,它的原点被平移,所以它的0x0位置是左上角的位置。在您的示例中,BufferStrategy 属于JFrame,因此GraphicsJFrame 的上下文相关 - 所以基本上,这只是一个巨大的混乱
  • MadProgrammer 所说的。如果从 Frame 中获取 BufferStrategy,则必须使用它来绘制该 Frame;如果您从 Canvas 获得 BufferStrategy,则必须使用它来绘制该 Canvas。 BufferStrategy 及其 Graphics 不能转移到其他组件。
  • @vgr 您可以将Graphics 上下文从BufferStrategy 传递到您想要的任何地方,策略的源将仅用于呈现结果。在大多数情况下,这意味着您应该避免任何已实现的组件“绘制”路线,并以独立的方式绘制到 Graphics 上下文 - 这可能是您所说的,我可能只是读错了 - 但是你的基本要点是正确的;)
  • 这一切都清楚了!谢谢!
  • @MadProgrammer 公平地说:主动渲染可能的(如果实施得当)。尽管在大约 20 年的 Swing 编程中,我从未真正使用过它。有些游戏可能有使用它的理由,但是当问题是:“这个问题中的任务是否有必要(甚至只是适当)?”,那么答案肯定是“不,不,不......”

标签: java swing graphics jpanel bufferstrategy


【解决方案1】:

Swing 使用它自己的渲染引擎,这是一种被动实现。你试图用你自己的活跃渲染引擎来规避这个问题,这两者会发生冲突。

因为BufferStrategy 属于JFrame,它是在它的范围内创建的,所以0x0 将是JFrame 的左上角位置,而不是JPanel

Swing 的渲染引擎会自动为您完成翻译。

您有两个基本选择。

  1. 不要以JPanel 为基础进行渲染,而只需使用一个“渲染”类来独立执行此操作(并使用Canvas 而不是JFrame 作为BufferStrategy 的基础)李>
  2. 使用 Swing Timer 作为主要渲染引擎

Swing Timer 基于示例...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new ViewPanel());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class ViewPanel extends JPanel {

        private static int APP_WIDTH = 600;
        private static int APP_HEIGHT = 400;

        private static final long serialVersionUID = -8019663913250286271L;

        public ViewPanel() {
            setBackground(Color.GRAY);
            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            });
            timer.start();
        }

        public void init() {
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            render(g);
        }

        // Where I do the drawing. It's called from the rendering loop in the JFrame
        public void render(Graphics g) {

            // refresh the background since we're not relying on paintComponent all the time
            Color bgc = getBackground();
            g.setColor(bgc);
            g.fillRect(0, 0, APP_WIDTH, APP_HEIGHT);

            // just paint a moving box
            drawBox(g);

            // draw a line to prove correctness. In the loop, you can see part of this line is hidden
            // underneath the title bar
            g.setColor(Color.red);
            g.drawLine(0, 0, APP_WIDTH, APP_HEIGHT);
        }

        protected void drawBox(Graphics g) {

            // get a random color
            Random ran = new Random();
            int red = ran.nextInt(255);
            int grn = ran.nextInt(255);
            int ble = ran.nextInt(255);
            Color colour = new Color(red, grn, ble);
            g.setColor(colour);

            // get a random position        
            int x = ran.nextInt(APP_WIDTH - 50);
            int y = ran.nextInt(APP_HEIGHT - 50);

            // draw it
            g.fillRect(x, y, 50, 50);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-14
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    相关资源
    最近更新 更多