【问题标题】:Panel components moves after rolling up the frame面板组件在框架卷起后移动
【发布时间】:2019-07-19 17:09:37
【问题描述】:

我有这样的代码,我需要在其中创建一些按钮来自定义正方形。它可以工作,但是在卷起框架后,文本字段会在整个框架中移动,我不知道为什么。我的意思是,当我首先执行程序时,它位于我使用 setBounds() 方法提到的正确位置,但随后它位于正方形上方。那么我该如何解决呢?

    import java.awt.*;
    import java.awt.event.*;
    import java.text.AttributedString;
    import javax.swing.*;
    import java.awt.font.TextAttribute;

     public class Square extends JFrame implements ActionListener {

      Button butt1 = new Button("Fill with yellow");
      Button butt2 = new Button("Fill with red");
      Button butt3 = new Button("Add label");
      Button butt4 = new Button("");

      Pan contentPane = new Pan();


      public Square() {
        super("Square");
        this.setBounds(200, 100, 670, 400);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.getContentPane().add(contentPane);
        contentPane.setBounds(0, 0, 670, 275);
        contentPane.setBackground(Color.BLACK);

        add(butt1);
        butt1.setBounds(25, 300, 190, 25);
        butt1.addActionListener(this);
        add(butt2);
        butt2.setBounds(235, 300, 190, 25);
        butt2.addActionListener(this);
        butt3.setBounds(440, 300, 190, 25);
        butt3.addActionListener(this);
        add(butt3);
        add(butt4);

      }

      @Override
      public void actionPerformed(ActionEvent e) {
        Object o = e.getSource();
        if (o == butt1) {
            contentPane.draw(1);
        }
        else if (o == butt2) {
            contentPane.draw(2);
        }
        else if (o == butt3) {
            contentPane.draw(3);
        }
      }

      public static void main(String[] args) {
          Square w = new Square();
        w.setVisible(true);
      }
    }

    class Pan extends JPanel {
      Graphics g;
      Graphics2D g2;
      protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GRAY);
            g.drawRect(240, 70, 150, 150);  
        }

      public void draw(int i) {

          g = getGraphics();
          super.paintComponent(g);
          g2 = (Graphics2D) g.create();
         switch(i) {
        case 1: g2.setColor(Color.yellow);
            g2.fillRect(240, 70, 150, 150); 
            break;
        case 2: g2.setColor(Color.red);
            g2.fillRect(240, 70, 150, 150); 
            break;
        case 3:
            g.setColor(Color.GRAY);
            g.drawRect(240, 70, 150, 150);   
            JTextField text = new JTextField(25);
            this.add(text);
            Button add = new Button("Add");
            add.setBounds(400, 230, 120, 25);
            this.add(add);
            text.setBounds(240, 230, 150, 25);

            Font font = new Font("Veranda", Font.BOLD|Font.ITALIC, 24);
                class AddButton extends JPanel implements ActionListener {
                    @Override
                    public void actionPerformed(ActionEvent ev) {
                        AttributedString label = new AttributedString(text.getText());  
                        label.addAttribute(TextAttribute.FONT, font);
                        label.addAttribute(TextAttribute.FOREGROUND, Color.PINK);
                        g2.drawString(label.getIterator(), 240, 50);    
                    }
                }   
            AddButton listener = new AddButton();
            add.addActionListener(listener);
            break;
         }

         }
    }

【问题讨论】:

  • g = getGraphics(); 在很多层面上都是个坏主意。在油漆通道之外调用super.paintComponent(g); 也是一个坏主意。这不是应该如何进行自定义绘画。首先查看Performing Custom PaintingPainting in AWT and Swing,了解绘画的工作原理以及您应该如何使用它
  • 将重量级 (AWT) 组件与轻量级 (Swing) 组件混合也会给您带来问题

标签: java swing awt paintcomponent


【解决方案1】:

好吧,很多核心问题和误解。

Swing 使用布局管理器。这可能是您再次遇到的第一件事。布局管理器根据各自的算法决定如何最好地定位组件。

首先查看Laying Out Components Within a Container 了解更多详细信息并充分利用它们。 GUI 是复杂且动态的东西,决定组件大小和位置的方式有很多。

绘画。新开发人员遇到的另一个问题是他们无法控制绘制系统。 Swing 使用被动渲染系统,这意味着它会在需要时进行绘制。

您可以通过调用 repaint 来提供提示何时应该完成新的绘制过程,但由系统决定应该绘制什么以及何时绘制。

g = getGraphics(); 在很多层面上都是个坏主意。除了能够返回null之外,它只不过是最后一次paint pass的快照,在新的paint pass发生时会被丢弃。

在油漆通道之外调用super.paintComponent(g); 也是一个坏主意。事实上,应该很少需要直接调用任何绘制方法。

这不是自定义绘画的方式。首先查看Performing Custom PaintingPainting in AWT and Swing,了解绘画的工作原理以及您应该如何使用它

此外,将重量级 (AWT) 和重量级 (Swing) 组件混合在一起通常是一个坏主意,应尽可能避免。

示例...

所以,我把你的例子“破解”成“稍微”更合理的东西

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

public class Square extends JFrame implements ActionListener {

    JButton butt1 = new JButton("Fill with yellow");
    JButton butt2 = new JButton("Fill with red");
    JButton butt3 = new JButton("Add label");
    JButton butt4 = new JButton("");

    Pan contentPane = new Pan();

    public Square() {
        super("Square");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        this.getContentPane().add(contentPane);
        contentPane.setBackground(Color.BLACK);

        JPanel actions = new JPanel();
        actions.setBorder(new EmptyBorder(10, 10, 10, 10));

        actions.add(butt1);
        butt1.addActionListener(this);
        actions.add(butt2);
        butt2.addActionListener(this);
        butt3.addActionListener(this);
        actions.add(butt3);
//      actions.add(butt4);

        add(actions, BorderLayout.SOUTH);

        pack();
        setLocationRelativeTo(null);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Object o = e.getSource();
        if (o == butt1) {
            contentPane.draw(1);
        } else if (o == butt2) {
            contentPane.draw(2);
        } else if (o == butt3) {
            contentPane.draw(3);
        }
    }

    public static void main(String[] args) {
        Square w = new Square();
        w.setVisible(true);
    }
}

class Pan extends JPanel {

    private int state = -1;
    private String text = null;

    public Pan() {
        Font font = new Font("Veranda", Font.BOLD | Font.ITALIC, 24);
        setFont(font);
        setLayout(new GridBagLayout());
        setBorder(new EmptyBorder(10, 10, 10, 10));
    }

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

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setColor(Color.GRAY);
        g2.drawRect(240, 70, 150, 150);
        switch (state) {
            case 1:
                g2.setColor(Color.yellow);
                g2.fillRect(240, 70, 150, 150);
                break;
            case 2:
                g2.setColor(Color.red);
                g2.fillRect(240, 70, 150, 150);
                break;
            case 3:
                g.setColor(Color.GRAY);
                g.drawRect(240, 70, 150, 150);
                break;
        }

        if (text != null) {
            AttributedString label = new AttributedString(text);
            label.addAttribute(TextAttribute.FONT, getFont());
            label.addAttribute(TextAttribute.FOREGROUND, Color.PINK);
            g2.drawString(label.getIterator(), 240, 50);
        }
    }

    public void draw(int i) {
        switch (i) {
            case 3:
                JTextField textField = new JTextField(25);
                JButton add = new JButton("Add");

                GridBagConstraints gbc = new GridBagConstraints();

                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.weighty = 1;
                gbc.anchor = GridBagConstraints.SOUTH;

                this.add(textField, gbc);
                gbc.gridx++;
                this.add(add, gbc);

                add.addActionListener(new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent ev) {
                        text = textField.getText();
                        remove(textField);
                        remove(add);

                        revalidate();
                        repaint();
                    }
                });

                revalidate();
                repaint();

                break;
        }
        state = i;
        repaint();

    }
}

您的代码充满了通常所说的“幻数”。这些是意义不明的值。

运行代码并尝试调整窗口大小,您就会明白我的意思。相反,您应该依靠“已知”值,例如 getWidthgetHeight 来更好地确定应该如何呈现输出

【讨论】:

    猜你喜欢
    • 2014-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    相关资源
    最近更新 更多