如何将自定义 JComponent 正确绘制到使用 GridBagLayout 的自定义 JPanel 中

问题描述 投票:0回答:1

我想在我的 GUI 上显示一组 3 张卡片。 BoardPanel 是我想在其上显示它们的 JPanel。正如您所看到的,它使用 GridBagLayout,并且我将卡片的引用添加为面板上的组件。这样做是因为我认为列表中的卡片会随着时间的推移而改变,并且我希望它们每次都以相同的方式显示(因此顶部卡片是 List[0],firstCard 是 List[1])。 我已将 ViewCard 实现为 JComponent,并重写了 PaintComponent 方法,正如我在 Oracle 教程和堆栈溢出中所读到的那样:

public class GUI extends JFrame {
 public GUI(){
  //Classic JFrame stuff
  setSize(new Dimension(1280,720));
  Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
  center.translate(-1280/2, -720/2);
  setLocation(center);
  setLayout(new BorderLayout());
  setDefaultCloseOperation(EXIT_ON_CLOSE);

 //Create cards to populate panel
  List<ViewCard> cards = new ArrayList<>();
  cards.add(new ViewCard("P0"));
  cards.add(new ViewCard("P1"));
 //Create board panel
  BoardPanel boardPanel = new BoardPanel(cards);

  //add components
  add(boardPanel, BorderLayout.CENTER);
  boardPanel.setVisible(true);
  setVisible(true);
 }

 public static void main(String[] args) {
  SwingUtilities.invokeLater(
          GUI::new
  );
 }
}

class BoardPanel extends JPanel {
    //This list should represent a deck
    private final List<ViewCard> deckCards;
    private final JLabel label;
    public BoardPanel(List<ViewCard> deckCards){
        this.deckCards = deckCards;
        label = new JLabel("TEST");
        setLayout(new GridBagLayout());
        setComponentConstraints();
        setVisible(true);
    }

    private void setComponentConstraints(){
        // In theory there should be 3 column,
        // but I will display only one for simplicity

        // ROW 1 COLUMN 1
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
        ViewCard topCard = deckCards.get(0);
        // I want to be able to not show a
        // card if null, without changing the layout
        // but that can be dealt with later on
        if(topCard != null) {
            System.out.println("CARD IN GRID: " + topCard.getCardId());
            add(topCard, gbc);
            topCard.setVisible(true);
        }

        // ROW 2 COLUMN 1
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
        ViewCard firstCard = deckCards.get(1);
        if(firstCard != null) {
            System.out.println("CARD IN GRID: " + firstCard.getCardId());
            add(firstCard, gbc);
            firstCard.setVisible(true);
        }

        // In this row there should be another card,
        // but I put a label to just confirm that the panel is displaying
        // same display as in the question

        // ROW 3 COLUMN 1
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;

        add(label, gbc);
    }
}

class ViewCard extends JComponent {
 private final String cardId;
 public ViewCard(String cardId){
  this.cardId = cardId;
 }

 public String getCardId() {
  return cardId;
 }

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

  Graphics2D g2D = (Graphics2D) g;

  g2D.setColor(Color.red);
  g2D.drawString("HERE SHOULD BE CARD " + cardId, 0,  0);

  System.out.println("Image should be drawn");
 }
}

现在,从 System.out.print (

Image should be drawn
) 我知道调用了paintComponent() 方法并且标签显示在框架上,这意味着面板显示正确。 所以我有很多问题:

  1. GridBagLayout 本身应该像对所有其他组件一样重新定位组件的图像,我错了吗?
  2. 如果没有,我该如何让它在右侧网格框中重新绘制组件?
  3. 我写的有问题吗?

很抱歉浪费时间。如果有任何与此类似的特定主题,请链接它,因为我发现了类似的问题,但没有一个具有此代码结构(这让我认为这是一个问题)。

实际 Actual.png

预计 Expected.png

编辑:更正代码。我已经删除了所有可以删除的内容,我认为我不能删除更多的东西,以保留代码结构。我将进一步尝试 Java Swing,找出问题的原因(可能是我对绘画组件工作原理的误解)。

java swing jpanel paintcomponent
1个回答
0
投票

好吧,在测试并使用 GridBagLayout 和其他东西之后,这就是我发现的。

首先,看起来 JPanel 的首选大小是 (0,0),因为执行以下代码后,paintPanel 组件不显示:

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

public class ExampleGUIPanelNotPrinting extends JFrame {
    public ExampleGUIPanelNotPrinting(){
        setSize(new Dimension(1280, 720));
        Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
        center.translate(-1280/2, -720/2);
        setLocation(center);
        setLayout(new GridBagLayout());
        PaintPanel paintPanel = new PaintPanel();
        PaintComponent paintComponent = new PaintComponent();
        paintComponent.setPreferredSize(new Dimension(100,10));
        GridBagConstraints gbc = new GridBagConstraints();

        gbc.gridx = 0;
        gbc.gridy = 0;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
//        gbc.fill = GridBagConstraints.BOTH;

        // paintPanel doesn't print if a preferred size
        // or fill "argument" isn't set.
        add(paintPanel, gbc);

        add(paintComponent, new GridBagConstraints(
                0,1,1,1,1.0,1.0, GridBagConstraints.CENTER,
                GridBagConstraints.BOTH, new Insets(0,0,0,0),
                0,0
        ));
        setVisible(true);
    }

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

}


class PaintComponent extends JComponent {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.setPaint(Color.red);
        graphics2D.setStroke(new BasicStroke(5));
        graphics2D.fillOval(0,0, 100,100);
    }
}


class PaintPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.fillOval(0,0, 100,100);
    }
}

所以是的,正如@matt所说,如果没有设置setPreferredSize(或覆盖getPreferredSize),则无法正确打印customPanel或组件。 此外,我仍然质疑的行为是设置为 BOTH (或 VERTICAL)时的“填充”:设置为此值时,面板和组件都显示在各自网格单元的左上角,即使设置了锚点到中心。

现在,我对此事的看法是,面板和组件都被拉伸,就好像 setPreferredSize 选项设置为所述网格单元的完整宽度和高度一样。当查看面板上应用的这种行为时,这是很直观的。如果面板被“强制”填充其所在单元格的额外空间,它将拉伸以覆盖单元格的所有空间,并且面板的中心仍然是单元格的中心,考虑到链接集锚。

如果组件没有首选大小或具有“填充”属性为两者(或垂直),则将显示它的一部分,如以下代码所示:

package org.example.gridbaglayout.secondLyt;

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

public class ExampleGUIAllPrinting extends JFrame{

    public ExampleGUIAllPrinting(){
        setSize(new Dimension(1280, 720));
        Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
        center.translate(-1280/2, -720/2);
        setLocation(center);
        setLayout(new GridBagLayout());
        PaintPanel paintPanel = new PaintPanel();
        PaintComponent paintComponent = new PaintComponent();
        paintComponent.setPreferredSize(new Dimension(100,10));
        GridBagConstraints gbc = new GridBagConstraints();

        gbc.gridx = 0;
        gbc.gridy = 0;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
//        gbc.fill = GridBagConstraints.BOTH;


        // With these settings a slice of the oval is painted
        add(paintComponent, gbc);

        add(paintPanel, new GridBagConstraints(
                0,1,1,1,1.0,1.0, GridBagConstraints.CENTER,
                GridBagConstraints.BOTH, new Insets(0,0,0,0),
                0,0
        ));
        

        setVisible(true);
    }

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

class PaintComponent extends JComponent {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.setPaint(Color.red);
        graphics2D.setStroke(new BasicStroke(5));
        graphics2D.fillOval(0,0, 100,100);
    }
}

class PaintPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.fillOval(0,0, 100,100);
    }
}

以积极的方式结束,通过仅更改 PaintPanel 的 PaintComponent 方法中的两个参数并将填充约束设置为其默认值(无),会出现一种奇怪的行为:

package org.example.gridbaglayout.thirdLyt;

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

public class ExampleGUIWeirdPrint extends JFrame{

    public ExampleGUIWeirdPrint(){
        setSize(new Dimension(1280, 720));
        Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
        center.translate(-1280/2, -720/2);
        setLocation(center);
        setLayout(new GridBagLayout());
        PaintPanel paintPanel = new PaintPanel();
        PaintComponent paintComponent = new PaintComponent();
        paintComponent.setPreferredSize(new Dimension(100,10));
        GridBagConstraints gbc = new GridBagConstraints();

        gbc.gridx = 0;
        gbc.gridy = 0;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
//        gbc.fill = GridBagConstraints.BOTH;



        add(paintComponent, gbc);

        // With these settings a rectangle is painted in the panel
        add(paintPanel, new GridBagConstraints(
                0,1,1,1,1.0,1.0, GridBagConstraints.CENTER,
                GridBagConstraints.NONE, new Insets(0,0,0,0),
                0,0
        ));
        
        setVisible(true);
    }

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

class PaintComponent extends JComponent {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D) g;
        graphics2D.setPaint(Color.red);
        graphics2D.setStroke(new BasicStroke(5));
        graphics2D.fillOval(0,0, 100,100);
    }
}

class PaintPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2D = (Graphics2D) g;
        //Changed position of the oval to be centered
        graphics2D.fillOval((getWidth()-100)/2,(getHeight()-100)/2, 100,100);
    }
}

为了回到我的问题的正轨,我犯了两个巨大的错误,第一个是忘记了drawString()方法的x,y位置与字符串的右下角相关(意味着在(0, 0)字符串总是超出显示范围。第二个“错误”是不将字符串绘制到面板上,而是将其绘制为组件本身。 事实上,当将字符串绘制为组件且未设置首选大小或未设置填充约束时,字符串将根本不显示或将被切片(就像椭圆形一样)。因此,正如 Matt 所建议的,解决方案是设置首选尺寸或使用填充属性(基于所使用的解决方案,而不是居中具有不同的方法)。这里是在 ViewCard 中将 getPreferredSize 实现为 JComponent 的代码(它不居中)。

package org.example.gridbaglayout.solution;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class GUI extends JFrame {
    public GUI(){
        //Classic JFrame stuff
        setSize(new Dimension(1280,720));
        Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
        center.translate(-1280/2, -720/2);
        setLocation(center);
        setLayout(new BorderLayout());
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        //Create cards to populate panel
        List<ViewCard> cards = new ArrayList<>();
        cards.add(new ViewCard("P0"));
        cards.add(new ViewCard("P1"));
        //Create board panel
        BoardPanel boardPanel = new BoardPanel(cards);

        //add components
        add(boardPanel, BorderLayout.CENTER);
        boardPanel.setVisible(true);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
                GUI::new
        );
    }
}

class BoardPanel extends JPanel {
    //This list should represent a deck
    private final List<ViewCard> deckCards;
    private final JLabel label;
    public BoardPanel(List<ViewCard> deckCards){
        this.deckCards = deckCards;
        label = new JLabel("TEST");
        setLayout(new GridBagLayout());
        setComponentConstraints();
        setVisible(true);
    }

    private void setComponentConstraints(){
        // In theory there should be 3 column,
        // but I will display only one for simplicity

        // ROW 1 COLUMN 1
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
//        gbc.fill = GridBagConstraints.BOTH;

        ViewCard topCard = deckCards.get(0);
//        topCard.setPreferredSize(new Dimension(250, 250));

        System.out.println("CARD IN GRID: " + topCard.getCardId());
        add(topCard, gbc);
        revalidate();


        // In this row there should be another card,
        // but I put a label to just confirm that the panel is displaying
        // same display as in the question

        // ROW 2 COLUMN 1
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;

        add(label, gbc);
    }
}

class ViewCard extends JComponent {
    private final String cardId;
    public ViewCard(String cardId){
        this.cardId = cardId;
    }

    public String getCardId() {
        return cardId;
    }

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

        Graphics2D g2D = (Graphics2D) g;

        g2D.setColor(Color.red);
        String stringToDisplay = "HERE SHOULD BE CARD " + cardId;
        g2D.drawString(stringToDisplay, getWidth()/2,  getHeight()/2);
//        g2D.fillOval(0,0,100,100);

    }

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

这里是将 ViewCard 实现为 JPanel 的代码。它仅显示 H,因为调整大小时仅垂直调整大小(填充属性设置为 VERTICAL)。要正确显示,请将填充属性设置为 BOTH 或设置首选尺寸。

package org.example.gridbaglayout.solution2;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class GUI extends JFrame {
    public GUI(){
        //Classic JFrame stuff
        setSize(new Dimension(1280,720));
        Point center = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
        center.translate(-1280/2, -720/2);
        setLocation(center);
        setLayout(new BorderLayout());
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        //Create cards to populate panel
        List<ViewCard> cards = new ArrayList<>();
        cards.add(new ViewCard("P0"));
        cards.add(new ViewCard("P1"));
        //Create board panel
        BoardPanel boardPanel = new BoardPanel(cards);

        //add components
        add(boardPanel, BorderLayout.CENTER);
        boardPanel.setVisible(true);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
                GUI::new
        );
    }
}

class BoardPanel extends JPanel {
    //This list should represent a deck
    private final List<ViewCard> deckCards;
    private final JLabel label;
    public BoardPanel(List<ViewCard> deckCards){
        this.deckCards = deckCards;
        label = new JLabel("TEST");
        setLayout(new GridBagLayout());
        setComponentConstraints();
        setVisible(true);
    }

    private void setComponentConstraints(){
        // In theory there should be 3 column,
        // but I will display only one for simplicity

        // ROW 1 COLUMN 1
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;
        gbc.fill = GridBagConstraints.VERTICAL;

        ViewCard topCard = deckCards.get(0);
//        topCard.setPreferredSize(new Dimension(250, 250));

        System.out.println("CARD IN GRID: " + topCard.getCardId());
        add(topCard, gbc);
        revalidate();


        // In this row there should be another card,
        // but I put a label to just confirm that the panel is displaying
        // same display as in the question

        // ROW 2 COLUMN 1
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;

        gbc.weightx = 1.0;
        gbc.weighty = 1.0;

        gbc.anchor = GridBagConstraints.CENTER;

        add(label, gbc);
    }
}

class ViewCard extends JPanel {
    private final String cardId;
    public ViewCard(String cardId){
        this.cardId = cardId;
    }

    public String getCardId() {
        return cardId;
    }

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

        Graphics2D g2D = (Graphics2D) g;

        g2D.setColor(Color.red);
        g2D.drawString("HERE SHOULD BE CARD " + cardId, 0,  100);
//        g2D.fillOval(0,0,100,100);

    }
}

我希望我不是唯一一个无法弄清楚这一点并困扰人们的人。我当然希望有人会发现这很有用。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.