Gráfico de posicionamiento absoluto JPanel dentro de JFrame bloqueado por secciones en blanco

Estoy tratando de mejorar mi comprensión de Java, particularmente la GUI de Java, haciendo un progtwig de rompecabezas. Actualmente, el usuario selecciona una imagen, que se corta en un número específico de piezas. Las piezas se dibujan al azar en la pantalla, pero parecen estar cubiertas por porciones en blanco de otras piezas, y no todas aparecen, pero puedo imprimir todas las coordenadas. Estoy usando posicionamiento absoluto porque un LayoutManager no parecía funcionar. Intenté brevemente Paneles en capas, pero me confundieron y no parecieron resolver el problema. Realmente agradecería algo de ayuda.
Aquí están las 2 clases relevantes:

import javax.swing.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; public class PuzzlePieceDriver extends JFrame { private static Dimension SCREENSIZE = Toolkit.getDefaultToolkit().getScreenSize(); private static final int HEIGHT = SCREENSIZE.height; private static final int WIDTH = SCREENSIZE.width; public static int MY_WIDTH; public static int MY_HEIGHT; private static BufferedImage image; private int xPieces = PuzzleMagicDriver.getXPieces(); private int yPieces = PuzzleMagicDriver.getYPieces(); private PuzzlePiece[] puzzle = new PuzzlePiece[xPieces*yPieces]; public Container pane = this.getContentPane(); private JLayeredPane layeredPane = new JLayeredPane(); public PuzzlePieceDriver(ImageIcon myPuzzleImage) { MY_WIDTH = myPuzzleImage.getIconWidth()+(int)myPuzzleImage.getIconHeight()/2; MY_HEIGHT = myPuzzleImage.getIconHeight()+(int)myPuzzleImage.getIconHeight()/2; setTitle("Hot Puzz"); setSize(MY_WIDTH,MY_HEIGHT); setLocationByPlatform(true); pane.setLayout(null); image = iconToImage(myPuzzleImage); //pass image into bufferedImage form puzzle = createClip(image); //pane.add(layeredPane); setVisible(true); }//end constructor public static BufferedImage iconToImage(ImageIcon icon) { Image img = icon.getImage(); int w = img.getWidth(null); int h = img.getHeight(null); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics g = image.createGraphics(); // Paint the image onto the buffered image g.drawImage(img, 0, 0, null); g.dispose(); return image; }//end BufferedImage protected int randomNumber(int min, int max) { int temp = min + (int)(Math.random() * ((max - min) + 1)); return temp; }//end randomNumber private PuzzlePiece[] createClip(BufferedImage passedImage) { int cw, ch; int w,h; w = image.getWidth(null); h = image.getHeight(null); cw = w/xPieces; ch = h/yPieces; int[] cells=new int[xPieces*yPieces]; int dx, dy; BufferedImage clip = passedImage; //layeredPane.setPreferredSize(new Dimension(w,h)); for (int x=0; x<xPieces; x++) { int sx = x*cw; for (int y=0; y<yPieces; y++) { int sy = y*ch; int cell = cells[x*xPieces+y]; dx = (cell / xPieces) * cw; dy = (cell % yPieces) * ch; clip= passedImage.getSubimage(sx, sy, cw, ch); int myX = randomNumber(0,(int)w); int myY = randomNumber(0,(int)h); PuzzlePiece piece=new PuzzlePiece(clip,myX,myY); puzzle[x*xPieces+y]=piece; piece.setBounds(myX,myY,w,h); //layeredPane.setBounds(myX,myY,w,h); //layeredPane.add(piece,new Integer(x*xPieces+y)); pane.add(piece); piece.repaint(); }//end nested for }//end for return puzzle; }//end createClip }//end class 

Lo siento si el espacio está un poco desordenado.

 import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; public class PuzzlePiece extends JPanel { private Point imageCorner; //the image's top-left corner location private Point prevPt; //mouse location for previous event private Boolean insideImage =false; private BufferedImage image; public PuzzlePiece(BufferedImage clip, int x, int y) { image = clip; imageCorner = new Point(x,y); //repaint(); }//end constructor public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image, (int)getImageCornerX(),(int)getImageCornerY(), this); System.out.println("paint "+getImageCornerX()+" "+getImageCornerY()); //repaint(); //g.dispose(); }//end paintComponent public Point getImageCorner() { return imageCorner; }//end getImageCorner public double getImageCornerY() { return imageCorner.getY(); }//end getImageCornerY public double getImageCornerX() { return imageCorner.getX(); }//end getPoint }//end class PuzzlePiece 

Cualquier ayuda sería apreciada, ¡me he quedado realmente atrapada! ¡¡Gracias!!

Estaba realmente intrigado con esta idea, así que hice otro ejemplo, usando un administrador de diseño personalizado.

 public class MyPuzzelBoard extends JPanel { public static final int GRID_X = 4; public static final int GRID_Y = 4; private BufferedImage image; public MyPuzzelBoard(BufferedImage image) { setLayout(new VirtualLayoutManager()); setImage(image); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { removeAll(); generatePuzzel(); } else { Component comp = getComponentAt(e.getPoint()); if (comp != null && comp != MyPuzzelBoard.this) { setComponentZOrder(comp, 0); invalidate(); revalidate(); repaint(); } } } }); } public void setImage(BufferedImage value) { if (value != image) { image = value; removeAll(); generatePuzzel(); } } public BufferedImage getImage() { return image; } protected float generateRandomNumber() { return (float) Math.random(); } protected void generatePuzzel() { BufferedImage image = getImage(); if (image != null) { int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); int clipWidth = imageWidth / GRID_X; int clipHeight = imageHeight / GRID_Y; for (int x = 0; x < GRID_X; x++) { for (int y = 0; y < GRID_Y; y++) { float xPos = generateRandomNumber(); float yPos = generateRandomNumber(); Rectangle bounds = new Rectangle((x * clipWidth), (y * clipHeight), clipWidth, clipHeight); MyPiece piece = new MyPiece(image, bounds); add(piece, new VirtualPoint(xPos, yPos)); } } } invalidate(); revalidate(); repaint(); } public class VirtualPoint { private float x; private float y; public VirtualPoint(float x, float y) { this.x = x; this.y = y; } public float getX() { return x; } public float getY() { return y; } public void setX(float x) { this.x = x; } public void setY(float y) { this.y = y; } } public class VirtualLayoutManager implements LayoutManager2 { private Map mapConstraints; public VirtualLayoutManager() { mapConstraints = new WeakHashMap<>(25); } @Override public void addLayoutComponent(Component comp, Object constraints) { if (constraints instanceof VirtualPoint) { mapConstraints.put(comp, (VirtualPoint) constraints); } } @Override public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } @Override public float getLayoutAlignmentX(Container target) { return 0.5f; } @Override public float getLayoutAlignmentY(Container target) { return 0.5f; } @Override public void invalidateLayout(Container target) { } @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { mapConstraints.remove(comp); } @Override public Dimension preferredLayoutSize(Container parent) { return new Dimension(400, 400); } @Override public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } @Override public void layoutContainer(Container parent) { int width = parent.getWidth(); int height = parent.getHeight(); for (Component comp : parent.getComponents()) { VirtualPoint p = mapConstraints.get(comp); if (p != null) { int x = Math.round(width * p.getX()); int y = Math.round(height * p.getY()); Dimension size = comp.getPreferredSize(); x = Math.min(x, width - size.width); y = Math.min(y, height - size.height); comp.setBounds(x, y, size.width, size.height); } } } } } 

Básicamente, esto utiliza un sistema de coordenadas “virtual”, donde en lugar de suministrar las posiciones absolutas x / y en píxeles, las proporcionas como porcentaje del contenedor padre. Ahora, para ser sincero, no se necesitaría mucho para volver a convertirlo en un posicionamiento absoluto, solo así, también obtendrás una escala de diseño.

El ejemplo también muestra Z-reording (por si acaso) y el doble clic simple vuelve a aleatorizar el puzzel

Oh, también hice la pieza transparente ( opaque = false )

Diseño aleatorizado

Oh, una cosa que debería mencionar, mientras examinaba este ejemplo, descubrí que era posible tener piezas ubicadas fuera de la pantalla (completa y parcialmente).

Es posible que desee verificar su código de posicionamiento para asegurarse de que las imágenes cuando se distribuyen no se hayan movido de la pantalla;)

Intente usar setBorder(new LineBorder(Color.RED)) en el constructor de su pieza de rompecabezas para ver dónde están los límites de las piezas de su rompecabezas. Si están donde usted esperaría que estuvieran, es probable que su posición sea incorrecta. También haga que sus piezas de rompecabezas se extiendan JComponent en JComponent lugar, o use setOpaque(false) si está ampliando JPanel .

Hay muchas sugerencias que me gustaría hacer, pero primero …

La forma de elegir una posición aleatoria está desactivada …

 int myX = randomNumber(0,(int)w); int myY = randomNumber(0,(int)h); 

Esto permite generar posiciones duplicadas (y superponer celdas)

ACTUALIZACIONES (usando un administrador de diseño)

Bueno, este es un ligero cambio de paradigma. En lugar de producir un clip y pasarlo a la pieza, permití que la pieza eligiera cómo iba a representar la pieza. En cambio, pasé el Rectángulo del que era responsable.

Esto significa que simplemente podría usar algo como setCell(Rectangle) para hacer que una pieza cambie (a menos que esté empeñado en arrastrar una gota;))

Terminé usando el panel de la Board debido a un comportamiento interesante en Java 7, pero esa es otra pregunta;)

 package puzzel; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.*; public class PuzzlePieceDriver extends JFrame { public PuzzlePieceDriver(ImageIcon myPuzzleImage) { setTitle("Hot Puzz"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLayout(new BorderLayout()); add(new Board(myPuzzleImage)); pack(); setVisible(true); }//end constructor public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException ex) { } catch (InstantiationException ex) { } catch (IllegalAccessException ex) { } catch (UnsupportedLookAndFeelException ex) { } ImageIcon image = new ImageIcon(PuzzlePieceDriver.class.getResource("/issue459.jpg")); PuzzlePieceDriver driver = new PuzzlePieceDriver(image); driver.setLocationRelativeTo(null); driver.setVisible(true); } }); } }//end class 

Un panel de pieza … El panel anula los métodos de tamaño preferred y minimum … mientras funciona para este ejemplo, es mejor usar setPreferredSize y setMiniumumSize 😉

 /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package puzzel; import javax.swing.*; import java.awt.*; import java.awt.image.*; public class PuzzlePiece extends JPanel { private BufferedImage masterImage; private Rectangle pieceBounds; private BufferedImage clip; public PuzzlePiece(BufferedImage image, Rectangle bounds) { masterImage = image; pieceBounds = bounds; // Make sure the rectangle fits the image int width = Math.min(pieceBounds.x + pieceBounds.width, image.getWidth() - pieceBounds.x); int height = Math.min(pieceBounds.y + pieceBounds.height, image.getHeight() - pieceBounds.y); clip = image.getSubimage(pieceBounds.x, pieceBounds.y, width, height); }//end constructor @Override public Dimension getPreferredSize() { return pieceBounds.getSize(); } @Override public Dimension getMinimumSize() { return getPreferredSize(); } public void paintComponent(Graphics g) { super.paintComponent(g); int x = 0; int y = 0; g.drawImage(clip, x, y, this); g.setColor(Color.RED); g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); }//end paintComponent }//end class PuzzlePiece 

El panel de la placa … usado principalmente debido a algunos problemas interesantes que estaba teniendo con Java 7 … Implementa un MouseListener , cuando ejecutas el progtwig, haces clic en la placa, es divertido;)

 package puzzel; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JPanel; /** * * @author shane */ public class Board extends JPanel { public static final int X_PIECES = 4; public static final int Y_PIECES = 4; private PuzzlePiece[] puzzle = new PuzzlePiece[X_PIECES * Y_PIECES]; private static BufferedImage image; public Board(ImageIcon myPuzzleImage) { image = iconToImage(myPuzzleImage); //pass image into bufferedImage form puzzle = createClip(); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { removeAll(); invalidate(); createClip(); // doLayout(); invalidate(); revalidate(); repaint(); } }); } public static BufferedImage iconToImage(ImageIcon icon) { Image img = icon.getImage(); int w = img.getWidth(null); int h = img.getHeight(null); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics g = image.createGraphics(); // Paint the image onto the buffered image g.drawImage(img, 0, 0, null); g.dispose(); return image; }//end BufferedImage protected int randomNumber(int min, int max) { int temp = min + (int) (Math.random() * ((max - min) + 1)); return temp; }//end randomNumber private PuzzlePiece[] createClip() { int cw, ch; int w, h; w = image.getWidth(null); h = image.getHeight(null); cw = w / X_PIECES; ch = h / Y_PIECES; // Generate a list of cell bounds List lstBounds = new ArrayList<>(25); for (int y = 0; y < h; y += ch) { for (int x = 0; x < w; x += cw) { lstBounds.add(new Rectangle(x, y, cw, ch)); } } BufferedImage clip = image; setLayout(new GridBagLayout()); for (int x = 0; x < X_PIECES; x++) { for (int y = 0; y < Y_PIECES; y++) { // Get a random index int index = randomNumber(0, lstBounds.size() - 1); // Remove the bounds so we don't duplicate any positions Rectangle bounds = lstBounds.remove(index); PuzzlePiece piece = new PuzzlePiece(clip, bounds); puzzle[x * X_PIECES + y] = piece; GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = x; gbc.gridy = y; gbc.fill = GridBagConstraints.BOTH; add(piece, gbc); piece.invalidate(); piece.repaint(); }//end nested for }//end for invalidate(); repaint(); return puzzle; }//end createClip } 

Ahora sé que finalmente va a preguntar cómo mover una pieza, GridBagLayout tiene este maravilloso método llamado getConstraints que le permite recuperar las restricciones utilizadas para diseñar el componente en cuestión. A continuación, puede modificar los valores de gridx y gridy y usar setConstraints para actualizarlo (no olvide llamar a invalidate y volver a repaint ;))

Recomiendo tener una lectura de Cómo utilizar GridBagLayout para obtener más información;)

Eventualmente, terminarás con algo como:

Puzzle 1Puzzle 2