tipos - swing fillers java
Hacer una GUI Swing Chess robusta y de tamaño variable (2)
¿Cómo voy a hacer esta GUI de ajedrez de tamaño variable?
A nuestra compañía se le ha encomendado la tarea de hacer un juego de ajedrez. Tiene que funcionar en máquinas con Windows, OS X y Linux / Unix, y hemos elegido Java para lograr esto, al tiempo que mantenemos una base de código común (útil tanto para el mantenimiento como para mantener bajos los costos).
Mi tarea es crear la GUI. El equipo de diseño del usuario ha aprobado las siguientes especificaciones. con el cliente.
El juego de ajedrez (Chess Champ) será resistente al cambio de tamaño y sencillo, incluye:
- Una barra de herramientas en la parte superior, con componentes de UI:
- Nuevo botón
- Botón Guardar
- Botón Restaurar
- Botón de Renunciar
- Una etiqueta para proporcionar mensajes al jugador.
En el lado izquierdo del juego, necesitamos un área que se reservará para su uso futuro, podría incluir cosas como:
- Listas de piezas capturadas
- Un selector para la elección de la pieza cuando se promocionan los peones
- Estadísticas del juego
- Sugerencias, etc.
Los detalles de esto todavía se están resolviendo con el cliente y el equipo lógico. Entonces, por el momento, simplemente márcalo con una etiqueta que contenga
?
como texto.El resto de la GUI consistirá en el tablero de ajedrez en sí. Tendrá:
- El área principal para el tablero de ajedrez. Si el usuario apunta a una pieza de ajedrez, debe mostrar el foco con un borde. También debe ser accesible desde el teclado. El cliente suministrará varias hojas de sprites de ajedrez (de una variedad de tamaños, estilos y colores) para permitir al usuario cambiar el aspecto del juego.
- El tablero de ajedrez tendrá etiquetas que indican las columnas (de izquierda a derecha: A, B, C, D, E, F, G y H) y filas (de arriba a abajo: 8, 7, 6, 5, 4, 3, 2 Y 1).
- El tablero de ajedrez y las etiquetas de columna / fila estarán bordeados por un borde negro de 1px, con un relleno de 8px alrededor.
- A medida que el jugador aumenta el tamaño del juego, el tablero de ajedrez debe permanecer en forma cuadrada, pero llena el espacio disponible.
- El color de fondo detrás del tablero de ajedrez debe ser ocre, pero en las maquetas a continuación hemos hecho que el área detrás del tablero de ajedrez sea verde para resaltar el comportamiento de cambio de tamaño.
Ajedrez Champ en el tamaño mínimo, antes de que se inicie un juego
Chess Champ en el tamaño mínimo, después de que se active el nuevo botón de juego
Chess Champ se extendió más ancho que el tamaño mínimo
Chess Champ se estiró más alto que el tamaño mínimo
Observé que al cambiar el tamaño puede obtener un pequeño espacio entre el tablero de ajedrez y el borde de la línea derecha / inferior. Esto sucede con un GridLayout porque el espacio no siempre es divisible por 9.
Probablemente esté buscando soluciones usando el estándar JDK, pero si desea deshacerse de este pequeño espacio, entonces puede usar el diseño relativo para administrar el tablero de ajedrez y las etiquetas. La brecha seguirá existiendo pero la he movido a las etiquetas para que no pueda ver la diferencia fácilmente.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class ChessGUI2 {
private final JPanel gui = new JPanel(new BorderLayout(3, 3));
private JButton[][] chessBoardSquares = new JButton[8][8];
private Image[][] chessPieceImages = new Image[2][6];
private JPanel chessBoard;
private final JLabel message = new JLabel(
"Chess Champ is ready to play!");
private static final String COLS = "ABCDEFGH";
public static final int QUEEN = 0, KING = 1,
ROOK = 2, KNIGHT = 3, BISHOP = 4, PAWN = 5;
public static final int[] STARTING_ROW = {
ROOK, KNIGHT, BISHOP, KING, QUEEN, BISHOP, KNIGHT, ROOK
};
ChessGUI2() {
initializeGui();
}
public final void initializeGui() {
// create the images for the chess pieces
createImages();
// set up the main GUI
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
JToolBar tools = new JToolBar();
tools.setFloatable(false);
gui.add(tools, BorderLayout.PAGE_START);
Action newGameAction = new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
setupNewGame();
}
};
tools.add(newGameAction);
tools.add(new JButton("Save")); // TODO - add functionality!
tools.add(new JButton("Restore")); // TODO - add functionality!
tools.addSeparator();
tools.add(new JButton("Resign")); // TODO - add functionality!
tools.addSeparator();
tools.add(message);
gui.add(new JLabel("?"), BorderLayout.LINE_START);
// chessBoard = new JPanel(new GridLayout(0, 9)) {
chessBoard = new JPanel() {
/**
* Override the preferred size to return the largest it can, in
* a square shape. Must (must, must) be added to a GridBagLayout
* as the only component (it uses the parent as a guide to size)
* with no GridBagConstaint (so it is centered).
*/
@Override
public final Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
Dimension prefSize = null;
Component c = getParent();
if (c == null) {
prefSize = new Dimension(
(int)d.getWidth(),(int)d.getHeight());
} else if (c!=null &&
c.getWidth()>d.getWidth() &&
c.getHeight()>d.getHeight()) {
prefSize = c.getSize();
} else {
prefSize = d;
}
int w = (int) prefSize.getWidth();
int h = (int) prefSize.getHeight();
// the smaller of the two sizes
int s = (w>h ? h : w);
return new Dimension(s,s);
}
};
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setRoundingPolicy( RelativeLayout.FIRST );
rl.setFill(true);
chessBoard.setLayout( rl );
chessBoard.setBorder(new CompoundBorder(
new EmptyBorder(8,8,8,8),
new LineBorder(Color.BLACK)
));
// Set the BG to be ochre
Color ochre = new Color(204,119,34);
chessBoard.setBackground(ochre);
JPanel boardConstrain = new JPanel(new GridBagLayout());
boardConstrain.setBackground(ochre);
boardConstrain.add(chessBoard);
gui.add(boardConstrain);
// our chess pieces are 64x64 px in size, so we''ll
// ''fill this in'' using a transparent icon..
ImageIcon icon = new ImageIcon(
//new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB));
new BufferedImage(48, 48, BufferedImage.TYPE_INT_ARGB));
// create the chess board squares
Insets buttonMargin = new Insets(0, 0, 0, 0);
for (int ii = 0; ii < chessBoardSquares.length; ii++) {
for (int jj = 0; jj < chessBoardSquares[ii].length; jj++) {
JButton b = new JButton();
b.setMargin(buttonMargin);
b.setIcon(icon);
if ((jj % 2 == 1 && ii % 2 == 1)
//) {
|| (jj % 2 == 0 && ii % 2 == 0)) {
b.setBackground(Color.WHITE);
} else {
b.setBackground(Color.BLACK);
}
chessBoardSquares[jj][ii] = b;
}
}
/*
* fill the chess board
*/
RelativeLayout topRL = new RelativeLayout(RelativeLayout.X_AXIS);
topRL.setRoundingPolicy( RelativeLayout.FIRST );
topRL.setFill(true);
JPanel top = new JPanel( topRL );
top.setOpaque(false);
chessBoard.add(top, new Float(1));
top.add(new JLabel(""), new Float(1));
// fill the top row
for (int ii = 0; ii < 8; ii++) {
JLabel label = new JLabel(COLS.substring(ii, ii + 1), SwingConstants.CENTER);
top.add(label, new Float(1));
}
// fill the black non-pawn piece row
for (int ii = 0; ii < 8; ii++) {
RelativeLayout rowRL = new RelativeLayout(RelativeLayout.X_AXIS);
rowRL.setRoundingPolicy( RelativeLayout.FIRST );
rowRL.setFill(true);
JPanel row = new JPanel( rowRL );
row.setOpaque(false);
chessBoard.add(row, new Float(1));
for (int jj = 0; jj < 8; jj++) {
switch (jj) {
case 0:
row.add(new JLabel("" + (9-(ii + 1)), SwingConstants.CENTER), new Float(1));
default:
row.add(chessBoardSquares[jj][ii], new Float(1));
}
}
}
}
public final JComponent getChessBoard() {
return chessBoard;
}
public final JComponent getGui() {
return gui;
}
private final void createImages() {
try {
URL url = new URL("http://i.stack.imgur.com/memI0.png");
BufferedImage bi = ImageIO.read(url);
for (int ii = 0; ii < 2; ii++) {
for (int jj = 0; jj < 6; jj++) {
chessPieceImages[ii][jj] = bi.getSubimage(
// jj * 64, ii * 64, 64, 64);
jj * 64, ii * 64, 48, 48);
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Initializes the icons of the initial chess board piece places
*/
private final void setupNewGame() {
message.setText("Make your move!");
// set up the black pieces
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][0].setIcon(new ImageIcon(
chessPieceImages[0][STARTING_ROW[ii]]));
}
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][1].setIcon(new ImageIcon(
chessPieceImages[0][PAWN]));
}
// set up the white pieces
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][6].setIcon(new ImageIcon(
chessPieceImages[1][PAWN]));
}
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][7].setIcon(new ImageIcon(
chessPieceImages[1][STARTING_ROW[ii]]));
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
ChessGUI2 cg = new ChessGUI2();
JFrame f = new JFrame("ChessChamp");
f.add(cg.getGui());
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// ensures the minimum size is enforced.
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Requiere más trabajo porque necesita administrar las filas por separado, no en una cuadrícula. Además, cambio el código que usa una imagen de 48x48 para facilitar el cambio de tamaño de prueba en mi monitor más pequeño.
Notas
El tablero de ajedrez completo con columnas a la izquierda y arriba es provisto por un
GridLayout
9x9. La primera celda del diseño de la cuadrícula es una etiqueta sin texto.Sin embargo, para simplificar la lógica del juego, mantenemos una matriz de botones 8x8 por separado.
Para permitir la funcionalidad del teclado, usamos botones para los lugares del tablero de ajedrez. Esto también proporciona una indicación de enfoque incorporada. El margen del botón se elimina para permitir que se contraigan al tamaño del icono. Podemos agregar un
ActionListener
al botón y responderá tanto al teclado como al mouse.Para mantener un tablero cuadrado, empleamos un pequeño truco. El tablero de ajedrez se agrega a un
GridBagLayout
como el único componente sinGridBagContraints
especificado. De esa manera, siempre está centrado. Para obtener el comportamiento de cambio de tamaño requerido, el tablero de ajedrez consulta el tamaño real del componente principal y devuelve un tamaño preferido que es el máximo que puede, mientras que sigue siendo cuadrado y no excede el tamaño más pequeño del ancho o alto del elemento principal.La imagen de la pieza de ajedrez se obtuvo de las imágenes de ejemplo para las preguntas y respuestas sobre el código y el marcado , que a su vez se desarrolló a partir de caracteres ''Rellenar'' Unicode en las etiquetas .
Usar imágenes es más simple, mientras que rellenar caracteres Unicode es más versátil además de ser más "ligero". ¡IE para apoyar 4 colores diferentes en 3 tamaños separados de 3 estilos diferentes de la pieza de ajedrez requeriría 36 hojas de sprite separadas!
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class ChessGUI {
private final JPanel gui = new JPanel(new BorderLayout(3, 3));
private JButton[][] chessBoardSquares = new JButton[8][8];
private Image[][] chessPieceImages = new Image[2][6];
private JPanel chessBoard;
private final JLabel message = new JLabel(
"Chess Champ is ready to play!");
private static final String COLS = "ABCDEFGH";
public static final int QUEEN = 0, KING = 1,
ROOK = 2, KNIGHT = 3, BISHOP = 4, PAWN = 5;
public static final int[] STARTING_ROW = {
ROOK, KNIGHT, BISHOP, KING, QUEEN, BISHOP, KNIGHT, ROOK
};
public static final int BLACK = 0, WHITE = 1;
ChessGUI() {
initializeGui();
}
public final void initializeGui() {
// create the images for the chess pieces
createImages();
// set up the main GUI
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
JToolBar tools = new JToolBar();
tools.setFloatable(false);
gui.add(tools, BorderLayout.PAGE_START);
Action newGameAction = new AbstractAction("New") {
@Override
public void actionPerformed(ActionEvent e) {
setupNewGame();
}
};
tools.add(newGameAction);
tools.add(new JButton("Save")); // TODO - add functionality!
tools.add(new JButton("Restore")); // TODO - add functionality!
tools.addSeparator();
tools.add(new JButton("Resign")); // TODO - add functionality!
tools.addSeparator();
tools.add(message);
gui.add(new JLabel("?"), BorderLayout.LINE_START);
chessBoard = new JPanel(new GridLayout(0, 9)) {
/**
* Override the preferred size to return the largest it can, in
* a square shape. Must (must, must) be added to a GridBagLayout
* as the only component (it uses the parent as a guide to size)
* with no GridBagConstaint (so it is centered).
*/
@Override
public final Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
Dimension prefSize = null;
Component c = getParent();
if (c == null) {
prefSize = new Dimension(
(int)d.getWidth(),(int)d.getHeight());
} else if (c!=null &&
c.getWidth()>d.getWidth() &&
c.getHeight()>d.getHeight()) {
prefSize = c.getSize();
} else {
prefSize = d;
}
int w = (int) prefSize.getWidth();
int h = (int) prefSize.getHeight();
// the smaller of the two sizes
int s = (w>h ? h : w);
return new Dimension(s,s);
}
};
chessBoard.setBorder(new CompoundBorder(
new EmptyBorder(8,8,8,8),
new LineBorder(Color.BLACK)
));
// Set the BG to be ochre
Color ochre = new Color(204,119,34);
chessBoard.setBackground(ochre);
JPanel boardConstrain = new JPanel(new GridBagLayout());
boardConstrain.setBackground(ochre);
boardConstrain.add(chessBoard);
gui.add(boardConstrain);
// create the chess board squares
Insets buttonMargin = new Insets(0, 0, 0, 0);
for (int ii = 0; ii < chessBoardSquares.length; ii++) {
for (int jj = 0; jj < chessBoardSquares[ii].length; jj++) {
JButton b = new JButton();
b.setMargin(buttonMargin);
// our chess pieces are 64x64 px in size, so we''ll
// ''fill this in'' using a transparent icon..
ImageIcon icon = new ImageIcon(
new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB));
b.setIcon(icon);
if ((jj % 2 == 1 && ii % 2 == 1)
//) {
|| (jj % 2 == 0 && ii % 2 == 0)) {
b.setBackground(Color.WHITE);
} else {
b.setBackground(Color.BLACK);
}
chessBoardSquares[jj][ii] = b;
}
}
/*
* fill the chess board
*/
chessBoard.add(new JLabel(""));
// fill the top row
for (int ii = 0; ii < 8; ii++) {
chessBoard.add(
new JLabel(COLS.substring(ii, ii + 1),
SwingConstants.CENTER));
}
// fill the black non-pawn piece row
for (int ii = 0; ii < 8; ii++) {
for (int jj = 0; jj < 8; jj++) {
switch (jj) {
case 0:
chessBoard.add(new JLabel("" + (9-(ii + 1)),
SwingConstants.CENTER));
default:
chessBoard.add(chessBoardSquares[jj][ii]);
}
}
}
}
public final JComponent getGui() {
return gui;
}
private final void createImages() {
try {
URL url = new URL("http://i.stack.imgur.com/memI0.png");
BufferedImage bi = ImageIO.read(url);
for (int ii = 0; ii < 2; ii++) {
for (int jj = 0; jj < 6; jj++) {
chessPieceImages[ii][jj] = bi.getSubimage(
jj * 64, ii * 64, 64, 64);
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Initializes the icons of the initial chess board piece places
*/
private final void setupNewGame() {
message.setText("Make your move!");
// set up the black pieces
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][0].setIcon(new ImageIcon(
chessPieceImages[BLACK][STARTING_ROW[ii]]));
}
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][1].setIcon(new ImageIcon(
chessPieceImages[BLACK][PAWN]));
}
// set up the white pieces
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][6].setIcon(new ImageIcon(
chessPieceImages[WHITE][PAWN]));
}
for (int ii = 0; ii < STARTING_ROW.length; ii++) {
chessBoardSquares[ii][7].setIcon(new ImageIcon(
chessPieceImages[WHITE][STARTING_ROW[ii]]));
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
ChessGUI cg = new ChessGUI();
JFrame f = new JFrame("ChessChamp");
f.add(cg.getGui());
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See https://.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// ensures the minimum size is enforced.
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}