Java 使用BorderLayout将图形添加到JFrame中

Java 使用BorderLayout将图形添加到JFrame中,java,swing,jframe,jpanel,paintcomponent,Java,Swing,Jframe,Jpanel,Paintcomponent,我试着做一个简单的作业,在这里我显示一行文本,显示门对象是否打开。在这下面,我可视化地表示它(使用drawRect)方法。在底部我有两个按钮,可以打开或关闭门,从而改变文本和矩形 编辑:现在可以编译的代码列表: import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.ev

我试着做一个简单的作业,在这里我显示一行文本,显示门对象是否打开。在这下面,我可视化地表示它(使用drawRect)方法。在底部我有两个按钮,可以打开或关闭门,从而改变文本和矩形

编辑:现在可以编译的代码列表:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;

    public class Test {

    public static void main(String[] args) {

        // Creates new JFrame called frame, with title "Door" 
        // (displayed at top of screen).
        JFrame frame = new JFrame ("Door");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        TempDoorPanel panel = new TempDoorPanel();
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);

        }
    }

    class Door {

    private String state;
    private String message;

    Door (String state) {
        this.state = state;
        message = "The door is currently closed.";
    }

    public boolean isOpen() {
        return state.equals ("open");
    }

    public boolean isClosed() {
        return state.equals ("closed");
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getMessage() {
        return message; 
    }

    public void open() {
        if (state.equals("open")) {
            message = "The door is already open.";
        }
        else {
            state = "open";
            message = "The door has been opened.";
        }
    }

    public void drawOpenDoor (Graphics page) {
        page.drawRect(100, 100, 100, 100);
    }
    }

    class TempDoorPanel extends JPanel {

    private Door door;
    private JTextField currentStateOfDoor;
    private JButton openDoor;

    public TempDoorPanel() {
        super.setLayout(new BorderLayout());
        door = new Door("closed");
        super.setBackground(Color.blue);
        super.setPreferredSize(new Dimension (360, 400));

        currentStateOfDoor = new JTextField(14);
        currentStateOfDoor.setText(door.getMessage());
        super.add(currentStateOfDoor, BorderLayout.NORTH);

        openDoor = new JButton("Open Door");

        class openDoorListener implements ActionListener {
            public void actionPerformed (ActionEvent event) {
                door.open();
                repaintText();
            }
        }

        openDoorListener openlistener = new openDoorListener();
        openDoor.addActionListener(openlistener);

        JPanel holder = new JPanel();
        holder.add(openDoor);
        super.add(holder, BorderLayout.SOUTH);
    }

    private void repaintText() {
        currentStateOfDoor.setText(door.getMessage());
        // These methods are from Door class.
    }

    public void paintComponent (Graphics page) {
        super.paintComponent(page);
        if (door.isOpen())
            door.drawOpenDoor(page);
        // isOpen is a boolean method from Door class.
    }
}
工作原理:

  • 按钮一个接一个地出现在屏幕右侧的BorderLayout.SOUTH处
  • JTextField显示在BorderLayout.NORTH的正确位置
  • 最后,蓝色区域出现在屏幕中央的正确位置
我试图解决的问题:

    我不知道如何在那个蓝色区域中间正确显示矩形。我尝试过改变矩形的坐标和大小,但根本不会改变它的大小。我可以让它drawRect(100100100100),它不会改变任何东西
  • 我还知道矩形当前隐藏在JTextField左上角的后面,但我不知道如何将其移动到BorderLayout中
问题:

  • 如何在边框布局中放置矩形
  • 在这样的布局中,如何调整通过drawrect()绘制的矩形的大小

因为您将组件添加到您在
JTextField上绘制的JPanel中,而
正在覆盖您的图形

解决方案:

1) 通过检查
drawRect(..)
方法中的
JTextField
高度来补偿这一点

或者更好

2) 不要将组件添加到正在绘制的同一
JPanel
,除非无法帮助

所以基本上我让你的
tempoorpanel
添加了一个新的
JPanel
BorderLayout.CENTER
,这是我们现在可以使用的绘图面板
drawRect(0,0,10,10)
,它将显示在
JPanel
drawingPanel
的左上角

  • 也不要在
    JPanel
    上调用
    setPreferredSize
    ,而是覆盖
    getPreferredSize()
    并返回符合图形的
    尺寸

  • 要在类外部调用
    paintComponent
    ,只需调用其实例即可

参见使用第2点的示例:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Test {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Door");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

                TempDoorPanel panel = new TempDoorPanel();
                frame.add(panel);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}
class Door {

    private String state;
    private String message;

    public Door(String state) {
        this.state = state;
        message = "The door is currently closed.";
    }

    public void drawOpenDoor(Graphics page) {
        page.setColor(Color.GREEN);
        page.drawRect(0, 0, 10, 10);
    }
}

class TempDoorPanel extends JPanel {

    private Door door;
    private JTextField currentStateOfDoor;
    private JButton openDoor;

    public TempDoorPanel() {
        super.setLayout(new BorderLayout());
        door = new Door("closed");

        currentStateOfDoor = new JTextField(14);
        //AcurrentStateOfDoor.setText(door.getMessage());
        super.add(currentStateOfDoor, BorderLayout.NORTH);

        openDoor = new JButton("Open Door");

        final JPanel drawingPanel = new JPanel() {
            @Override
            protected void paintComponent(Graphics grphcs) {
                super.paintComponent(grphcs);
                // if (door.isOpen()) {
                door.drawOpenDoor(grphcs);
                // }
                // isOpen is a boolean method from Door class.

            }
        };
        drawingPanel.setBackground(Color.blue);
        add(drawingPanel);

        class openDoorListener implements ActionListener {

            public void actionPerformed(ActionEvent event) {
                //door.open();
                repaintText();
                drawingPanel.repaint();//so paint component of drawing panel is called
            }
        }

        openDoorListener openlistener = new openDoorListener();
        openDoor.addActionListener(openlistener);

        JPanel holder = new JPanel();
        holder.add(openDoor);
        super.add(holder, BorderLayout.SOUTH);
    }

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

    private void repaintText() {
        // currentStateOfDoor.setText(door.getMessage());
        // These methods are from Door class.
    }
}


当您与侦听器一起处理开门事件时

class openDoorListener implements ActionListener {
  public void actionPerformed(ActionEvent event) {
    door.open();
    repaintText();
  }
}
实际上,您没有调用重新绘制面板;因此,不会调用面板的
paintComponent()
方法,也不会调用
door.drawOpenDoor()
。您可以通过单击按钮然后调整帧大小来测试这一点。当您调整大小时,面板将自动重新绘制,并显示门

您可以通过在ActionListener中添加对
repaint()
的调用来解决此问题

class openDoorListener implements ActionListener {
  public void actionPerformed(ActionEvent event) {
    door.open();
    repaintText();
    repaint();   // requests that the panel be repainted
  }
}

请产生一个需要一个单一的复制和粘贴现在我们必须去创建一个新的项目,新的类和新的主等我想简单地创建一个与您的名称相同的类,并复制和粘贴代码。但是,嘿,那是我,我很懒:P它也不应该包含错误,例如
door.isOpen()
给出了一个错误,因为你还没有包括现在正在处理它的方法,谢谢你的提示。问题是,你看不到矩形是它被JTextField覆盖的,因为你的合作词是:
page.drawRect(100,100,10,10)代码已经重新编译-应该能够一次编译所有代码。更好的方法是创建一个用于绘制门的自定义组件,如JLabel。有没有一种方法可以在不使用invokeLater Runnable等部分的情况下完成此操作。我之所以这么问,是因为这是我的大学给我的一个部分,他们没有给出任何需要改变的迹象(我不知道runnable实际上是什么)。无论如何,谢谢你。它需要改变(是的,你可以省略它,但这会导致以后的问题)。所有Swing组件都应该通过
swingutilites.invokeLater(Runnable r)
事件调度线程上创建和操作。请在这里阅读。所以基本上你的大学没有坚持Swing的最佳实践,告诉他们吧!!我会的!还有一件事。我知道paintComponent是自动运行的(尽管可以手动覆盖)。但是我在API中找不到它是自动运行的。我好奇的原因是我想知道还有什么是自动运行的。当我昨天开始这样做时,我花了几个小时才发现我不需要将图形对象传递到paintComponent类中,因为这一切都是为我完成的!它在哪里说它为什么是自动的,还有什么是自动的?
paintComponent
将在其可见时以不同的间隔被调用,即当对调用
update(…)
repaint(…)
等的UI进行更改时。但是,我们可以通过调用
repaint()强制调用
paintComponent
在组件实例上,但API在哪里告诉您它是以不同的间隔调用的?也就是说,不问别人我怎么知道呢?谢谢你,突然之间一切都好了。正是这些自动呼叫让我心烦。如果像你这样的人不告诉我必须这么做,我也不会想到。