【问题标题】:Java Swing Add JPanel to JPanelJava Swing 将 JPanel 添加到 JPanel
【发布时间】:2019-03-05 02:42:40
【问题描述】:

情况

我目前正在尝试使用 Java 的 Swing 构建 2D 游戏。为此,我有我的主类Puzzle,它是JFrame 的子类。在我的框架中,我添加了我的主要JPanel,它由几个添加在一起的JPanels 组成(每个都是一个新部分)。

编辑 2:PlayingField 是我的模型,它将存储每件的当前位置。 可以用鼠标选择一块(计划是突出显示它)并用箭头键移动它,只要下一步(一个完整的单元格,所以大约 100 像素)不是另一个的位置件。截至目前,PlayingField 不存储任何数据,因为这些片段丢失了。

private void createAndShowGui() {
    // The playing-field with a 4x6 grid.
    PlayingField field = new PlayingField(4, 6);
    JPanel mainPanel = new ComputerView(field);
    
    setTitle("Puzzle");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(400, 600);
    add(mainPanel);
    setVisible(true);
}

上面的方法将创建我的框架并添加主面板。以下方法是我的主面板,它为自己添加了几个JPanels。

public ComputerView(PlayingField field) {
    this.field = field;
    this.setLayout(null);

    JPanel topLeft = new GamingPiece(PlayingField.TOP_LEFT);
    add(topLeft);
    
    JPanel topRight = new GamingPiece(PlayingField.TOP_RIGHT);
    add(topRight);
    
    JPanel bottomLeft = new GamingPiece(PlayingField.BOTTOM_LEFT);
    add(bottomLeft);
}

每个GamingPiece 或者更确切地说是我的子JPanels 都在绘制一个基本块(我只绘制一个并旋转其他块,因为它们都由相同的任意形状组成)。 GamingPiece 类也是JPanel 的子类,并调用JPanel#paintComponent() 方法来绘制棋子。

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

    g2.setColor(Color.YELLOW);
    g2.fillPolygon(pieceX, pieceY, pieceX.length);
}

问题和我的问题

由于我对 Java 还很陌生,我真的不知道如何正确地做到这一点。如果我通过创建一个新对象并将其添加到主面板来添加我的作品,它不会显示所有作品,只会显示最后一个添加的作品。有些甚至似乎不起作用,即使它们是唯一添加的(解释我的情况:我有相同任意形状的碎片,只是旋转方式不同,但使用 Graphics2D#rotate() 似乎不能正常工作) .

我希望我能很好地解释我的情况和我的问题,以便你们帮助我。提前致谢!

编辑:

我的代码

Puzzle.java

package programming.schimmler;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import programming.schimmler.model.PlayingField;
import programming.schimmler.view.ComputerView;

public class Puzzle extends JFrame {
    
...
Invoking the createAndShowGui()
...
    
private void createAndShowGui() {
    // The playing-field with a 4x6 grid.
    PlayingField field = new PlayingField(4, 6);
    JPanel mainPanel = new ComputerView(field);
    
    setTitle("Puzzle");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(400, 600);
    add(mainPanel);
    setVisible(true);
    }
}

ComputerView.java

package programming.schimmler.view;

import java.util.HashSet;
import java.util.Set;

import javax.swing.JPanel;

import programming.schimmler.model.PlayingField;

public class ComputerView extends JPanel {

...
Instance variables
....

public ComputerView(PlayingField field) {
    this.field = field;
    this.setLayout(null);

    JPanel topLeft = new GamingPiece(PlayingField.TOP_LEFT);
    add(topLeft);
    
    JPanel topRight = new GamingPiece(PlayingField.TOP_RIGHT);
    add(topRight);
    
    JPanel bottomLeft = new GamingPiece(PlayingField.BOTTOM_LEFT);
    add(bottomLeft);
    }
}

GamingPiece.java

package programming.schimmler.view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

import programming.schimmler.model.PlayingField;

/**
 * 
 */
public class GamingPiece extends JPanel {

...

public GamingPiece(int type) {
    switch (type) {
    // Need to draw each polygon from different coordinates since rotating did not work yet.
    case PlayingField.TOP_LEFT:
        pieceX = new int[] { 100, 100, 300, 300, 200, 200, 100 };
        pieceY = new int[] { 100, 100, 100, 200, 200, 300, 300 };
        break;
    case PlayingField.TOP_RIGHT:
        pieceX = new int[] { 400, 400, 300, 300, 200, 200 };
        pieceY = new int[] { 0, 200, 200, 100, 100, 0 };
        break;
    case PlayingField.BOTTOM_LEFT:
        pieceX = new int[] { 0, 200, 200, 100, 100, 0 };
        pieceY = new int[] { 400, 400, 300, 300, 200, 200 };
        break;
    case PlayingField.BOTTOM_RIGHT:
        pieceX = new int[] { 400, 400, 300, 300, 200, 200 };
        pieceY = new int[] { 400, 200, 200, 300, 300, 400 };
        break;
    case PlayingField.SQUARE:
        pieceX = new int[] { 100, 300, 300, 100 };
        pieceY = new int[] { 100, 100, 300, 300 };
        break;
    }
    
    setLayout(null);
}

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

    g2.setColor(Color.YELLOW);
    g2.fillPolygon(pieceX, pieceY, pieceX.length);
    }
}

以上这些类是与我的 GUI 交互的类,没有其他类参与。

【问题讨论】:

  • 我看到了问题.. this.setLayout(null); 使用。布局。 一般提示:为了尽快获得更好的帮助,edit 添加minimal reproducible exampleShort, Self Contained, Correct Example
  • 请添加minimal reproducible example。没有一个,我们只能猜测问题。 setLayout(null) 是一种可能性。
  • 我想要一匹会排泄冰淇淋的飞行小马。现在我们都讨论了我们无法实现的梦想,您应该使用布局来修复该代码。或者,如果您无法管理它,请将您的尝试展示为 MCVE / SSCCE。
  • 什么是PlayingField?是 enum 还是带有静态成员的 class?它是一个模型,将碎片放在哪里,或者它们应该去哪里?你想用鼠标拖动碎片吗?或者棋子是否像 4x4“魔术 16”拼图一样移动,点击棋子会导致移动一个完整的 100 像素网格步长?在我们了解您要做什么之前,我们还不如戴着烤箱手套穿针;可能,但真的很难。
  • “我很抱歉让你们这么难”:我只能重复你得到的建议:mvce。不是整个代码(这就是 M 的意思),而是我们可以复制粘贴并运行的代码(E)。它比试图描述代码更清晰、更简短。

标签: java swing jpanel layout-manager null-layout-manager


【解决方案1】:

您试图将JPanel 拼图添加到您的JPanel 拼图板上,使事情变得过于复杂。使用非矩形拼图,问题将很快变得明显,例如 TOP_LEFT 形状,它从拼图的右下侧有一个切口。当需要重叠拼图块以将切掉的部分组合在一起时,稍后添加的拼图将遮挡之前绘制的拼图。

例如,如果您尝试将 TOP_LEFT 块 (A) 和 BOTTOM_RIGHT 块 (B) 嵌套在一起。

 AAAAAA BBB   
 AAAAAA BBB
 AAA BBBBBB
 AAA BBBBBB

你只会看到:

+---+------+
|AAA|   BBB|
|AAA|   BBB|
|AAA|BBBBBB|
|AAA|BBBBBB|
+---+------+

重叠区域将仅由其中一个面板绘制。 B 块的整个区域,包括空白区域,将被绘制在第二块区域中,在 A 块希望显示的任何内容之上。

您可以通过将JPanel 设置为透明而不调用super.paintComponent() 来解决此问题,后者将整个组件绘制为背景色。但是你仍然需要在JPanel 上绘制形状,并将JPanel 正确定位在父JPanel 上。

忘记面板中的面板!只需绘制JPanel上的碎片。

例子:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Puzzle {

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

    private final static int shape_x[] = { -100, 100, 100, 0, 0, -100 };
    private final static int shape_y[] = { -100, -100, 0, 0, 100, 100 };

    public Puzzle() {
        JFrame frame = new JFrame("Puzzle");
        frame.setSize(600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        View view = new View();
        frame.setContentPane(view);

        Shape l_shape = new Polygon(shape_x, shape_y, shape_x.length);
        view.pieces.add(new Piece(Color.YELLOW, l_shape,   0, 100, 100));
        view.pieces.add(new Piece(Color.GREEN,  l_shape, 180, 300, 300));
        view.pieces.add(new Piece(Color.RED,    l_shape,  65, 450, 145));

        frame.setVisible(true);
    }

}

@SuppressWarnings("serial")
class View extends JPanel {

    final List<Piece> pieces = new ArrayList<>();

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

        Graphics2D gc = (Graphics2D) g;
        for (Piece piece : pieces)
            piece.draw(gc);
    }
}

class Piece {
    final Color color;
    final Shape shape;
    final int angle;

    int x, y;   // Move pieces by by changing these

    Piece(Color color, Shape shape, int angle, int x, int y) {
        this.color = color;
        this.shape = shape;
        this.angle = angle;
        this.x = x;
        this.y = y;
    }

    void draw(Graphics2D gc) {
        AffineTransform tf = gc.getTransform();
        gc.translate(x, y);
        gc.rotate(Math.toRadians(angle));
        gc.setColor(color);
        gc.fill(shape);
        gc.setTransform(tf);
    }
}

请注意,这也是一个最小、完整和可验证的示例。最小:没有package 声明;没有额外的 HashSetSet 未使用的导入;除了显示 3 个游戏棋子,不尝试做任何事情。它是完整的:您可以编译和运行的单个文件。它是可验证的:您可以运行它并看到它显示了 3 个游戏片段。

作为奖励,它显示Graphics2D.rotate() 正在工作,其中一个旋转了 180°,另一个旋转了一个不适合您的拼图的角度,但有助于证明旋转工作正常。

【讨论】:

  • 非常感谢您提供的代码(就像一个魅力!)和提示,非常感谢! :-) 我又遇到了一个问题:我需要通过鼠标单击来选择一件。我认为getComponentAt() 可以解决问题,但这会返回整个视图。是否可以只获取相应的元素(例如左上角的部分)并将其保存到变量中,例如换个位置?
  • “我又遇到了一个问题”,请发布一个新问题。
  • @AJNeufeld 也许你能告诉我如何移动绘制的形状?例如。当与KeyEvents 互动时,如果我向上按箭头键,它应该向上移动 100 像素?
  • 这个发布的问题是“Java Swing Add JPanel to JPanel”。询问、回答和接受。它不是关于命中检测来确定鼠标在哪一块,或者关于如何移动/动画块。为每个问题发布一个新问题会做几件事。 1) 保持问答方面的整洁;在JPanel 中搜索JPanel 的人不必对不相关的答案/信息进行分类。 2) 允许其他人在新帖子的主题回答中赢得声誉。 3) 通过发布更多优质问题并接受每个问题的优质答案来提高自己的声誉。
猜你喜欢
  • 1970-01-01
  • 2013-02-14
  • 1970-01-01
  • 2016-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-25
相关资源
最近更新 更多