在绘制另一个对象时将一个已绘制对象保留在 JPanel 中 (Java)

Keep one painted object in a JPanel while painting another object (Java)

我有一个可以自行绘制的 9x9 网格,一个可以自行绘制的矩形,以及一个允许使用箭头键移动矩形的构造函数。但是,我无法弄清楚如何在矩形四处移动的同时将网格保持在屏幕上。最终,我希望矩形能够通过在用户向右按下时向右移动一个方块等来充当网格的导航。

据我所知,每当调用 paint() 时,整个 JPanel 都会重做,这意味着一种方法是在按下某个键时重新绘制每一行,但这似乎过分了。还有别的办法吗?事实上,绘制矩形会导致网格被覆盖。

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


public class TestDoubleObject extends JFrame
{

    private Color ourRectColor = new Color(28,222,144);
    private int ourRectWidth = 50;
    private int ourRectHeight = 50;

    private Point ourRecLocation = new Point(150,150);


    public class Rectangle
    {
        protected void paint(Graphics2D g2d)
        {
                g2d.setColor(ourRectColor);
                g2d.fillRect(ourRecLocation.x, ourRecLocation.y, ourRectWidth, ourRectHeight);
        }

    } // Rectangle class


    public class OurRectangle extends JPanel
    {

        private Rectangle capableRectangle;

        public OurRectangle()
        {
            capableRectangle = new Rectangle();
        }

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

            capableRectangle.paint(g2d);

            g2d.dispose();
        }

    } // OurRectangle class


    public class CoreGrid
    {
        protected void paint(Graphics2D g2d)
        {

            g2d.setColor(new Color(0,0,0));

            // Draw Horizontal Lines

            for(int i=100;i<=640;i+=60)
                g2d.drawLine(100,i,640,i);


            // Draw Vertical Lines

            for(int i=100;i<=640;i+=60)
                g2d.drawLine(i,100,i,640);

        }

    } // CoreGrid class


    public class OurCoreGrid extends JPanel
    {
        private CoreGrid capableCoreGrid;

        public OurCoreGrid()
        {
            capableCoreGrid = new CoreGrid();
        }

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

            capableCoreGrid.paint(g2d);

            g2d.dispose();
        }

    } // OurCoreGrid class


    KeyStroke pressRight = KeyStroke.getKeyStroke("RIGHT");
    KeyStroke pressLeft = KeyStroke.getKeyStroke("LEFT");
    KeyStroke pressUp = KeyStroke.getKeyStroke("UP");
    KeyStroke pressDown = KeyStroke.getKeyStroke("DOWN");

    OurRectangle recToWorkWith = new OurRectangle();
    OurCoreGrid gridToWorkWith = new OurCoreGrid();


    public TestDoubleObject()
    {

        InputMap inputMap = recToWorkWith.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = recToWorkWith.getActionMap();


        Action rightAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                ourRecLocation.x += 20;
                recToWorkWith.repaint();
            }
        };

        inputMap.put(pressRight, "rightAction");
        actionMap.put("rightAction",rightAction);


        Action leftAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                ourRecLocation.x -= 20;
                recToWorkWith.repaint();
            }
        };

        inputMap.put(pressLeft, "leftAction");
        actionMap.put("leftAction",leftAction);


        Action downAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                ourRecLocation.y += 20;
                recToWorkWith.repaint();
            }
        };

        inputMap.put(pressDown, "downAction");
        actionMap.put("downAction",downAction);


        Action upAction = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                ourRecLocation.y -= 20;
                recToWorkWith.repaint();
            }
        };

        inputMap.put(pressUp, "upAction");
        actionMap.put("upAction",upAction);



        add(gridToWorkWith);

        add(recToWorkWith);



        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(800,800);
        setVisible(true);
    }

    public static void main(String[] argv)
    {
        EventQueue.invokeLater(

        new Runnable()
        {
            @Override
            public void run()
            {
                new TestDoubleObject();
            }
        });
    }
}

one way to do it would be to repaint every single line whenever a key is pressed, but that seems excessive.

这正是绘画的工作方式。 Swing 是双缓冲的,因此您不会注意到绘画问题。

但是,这不是您当前的问题。

I cannot figure out how to keep the grid on-screen at the same time the rectangle is moving around

JFrame 内容窗格的默认布局管理器是 BorderLayout。当您将组件添加到 BorderLayout 且未指定约束时,假定 BorderLayout.CENTER。

add(gridToWorkWith);
add(recToWorkWith);

因此,您尝试使用上述代码向 BorderLayout.CENTER 添加两个组件。但是,只有最后添加的组件由 BorderLayout 管理,这意味着第一个组件的大小为 (0, 0),因此它永远不会被绘制。

Swing 组件具有 parent/child 关系,因此如果您希望矩形绘制在网格之上,则需要将矩形添加到网格,就像将网格添加到框架一样。

所以代码可能是这样的:

gridToWorkWith.setLayout( new BorderLayout() );
gridToWorkWith.add( recToWorkWith );
add(gridToWorkWith);
//add(recToWorkWith);

但是这仍然行不通,因为默认情况下 JPanel 是不透明的,所以您看不到网格,因为矩形绘制在它上面。所以你需要让矩形不透明:

recToWorkWith.setOpaque( false );

很高兴看到您正在使用键绑定。一个建议是创建一个带有参数的动作来控制矩形的移动。所以这两个参数将是 x/y 位置的变化。

有关此方法的示例,请查看 Motion Using the KeyboardMotionWithKeyBindings 示例中的 MotionAction。通过使用不同的参数,您的四个 Action 可以很容易地替换为同一 Action 的 4 个实例。您甚至可以创建对角线运动。