我想在我的 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() 方法并且标签显示在框架上,这意味着面板显示正确。
所以我有很多问题:
很抱歉浪费时间。如果有任何与此类似的特定主题,请链接它,因为我发现了类似的问题,但没有一个具有此代码结构(这让我认为这是一个问题)。
编辑:更正代码。我已经删除了所有可以删除的内容,我认为我不能删除更多的东西,以保留代码结构。我将进一步尝试 Java Swing,找出问题的原因(可能是我对绘画组件工作原理的误解)。
好吧,在测试并使用 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);
}
}
我希望我不是唯一一个无法弄清楚这一点并困扰人们的人。我当然希望有人会发现这很有用。