How to work with a specific element in GridLayout? - java

I have a problem adding an icon to a spesific element(grid?) in gridlayout. My gridlayout contains 64 "bricks" which is intended to work as a chessboard.
My gridcode looks like this:
ChessBoard
public class SjakkBrett extends JFrame implements Config {
public ChessBoard() {
setSize(800,800);
setLayout(new GridLayout(BRICKS/ ROWS, 0) );
for (int id = 0; id < BRICKS; id++)
add(new Brick(id));
setVisible(true);
}
Config
public interface Config {
public int ROWS= 8;
public int BRICKS= 64;
}
My problem is that I can't seem to find a way to add icons to a specific brick in the board, such as setIcon(new ImageIcon("pawn.png")); because I don't know how to use the brick ID's I'm making.
Anyone who could help me out here?

To answer your specific question:
List<Brick> bricks = new ArrayList<Brick>();
public ChessBoard() {
setSize(800,800);
setLayout(new GridLayout(BRICKS/ ROWS, 0) );
for (int id = 0; id < BRICKS; id++) {
Brick brick = new Brick(id);
add(brick);
bricks.add(brick);
}
setVisible(true);
}
public void setBrick(int id, int piece) {
bricks.get(id).setPiece(piece);
}
To answer your unasked questions, let's think about a game of chess for a bit.
A chess board already has a notation. A typical first move is e4. Since a piece is not specified, that means a pawn. The only pawn that can move to e4 is the pawn sitting on e2. So, e4 is a shorthand way of saying "move the pawn from e2 to e4".
So, we have bricks (squares) that are arranged to make a board. We have pieces with the ability to move from one brick to another, according to rules that are different for each piece. We also have capture rules and rules for determining who wins.
All of these elements have to be present in the game as either objects or methods.
So, let's talk about objects.
We have a brick (square).
We have a collection of bricks called a board.
We have pieces.
These objects are interrelated. What they all have in common is the idea of location.
A brick is located in a specific spot (e2).
A board needs to know how to translate a spot (e2) into something meaningful (row 1, column 4; assuming row 0, column 0 is the lower left corner).
A piece needs to know where it's located (e2), where it can legally go (e3, e4), and where it will go (e4).
This should be enough to get you started.

Adding labels with the icon would probably make it slightly easier to
make moveable pieces to the game later I guess, but still I don't know
how to add the labels to spesific ID's in the gridlayout. BRICKS is
just getting it's information from Config, where it's declared(?) the
value 64. Sorry if I use the wrong names and stuff here, but I'm way
to fresh with java to actually work with it.
have look at put/getClientProperty, then any action/event from Keyboard or MouseXxxListener returns proper coordinates from the array of JLabels or JButtons
you can to multiple numbers of put/getClientProperty, there isn't any limits
I'd be to use JButton (implemented setXxxIcon in API) instead of JLabel (there required call for repaint() for MouseMotionListener)

Ive had this code lying around which I used as foundation to my own chess game.
Basically consists of 6 classes:
Test basically holds main and creates the GUI as well as loading a single piece to the chessboard.
NotationPanel which is used for row and coloumns to be shown on the side of the board.
Chessboard which holds all the ChessboardBlocks which make up the board also lays out the board with black and white labels in their appropriate locations.
ChessboardBlock which has a set location(i.e A4 etc) and can hold a ChessPiece instance.
ChessPiece which holds data/image of the Piece instance.
ChessPieceMouseAdapter to handle dragging and dropping of Pieces:
The output from a typical move:
From Location: A1 Piece Type: knight Piece Color: White
To Location: D3 Piece Type: knight Piece Color: White
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
ChessPieceMouseAdapter chessPieceMouseAdapter = new ChessPieceMouseAdapter() {
#Override
boolean chessPieceSelected(ChessPiece chessPiece, ChessboardBlock cb) {
System.out.println("From Location: " + chessPiece.getLocation()
+ " Piece Type: " + chessPiece.getType()
+ " Piece Color: " + chessPiece.getColor());
return true;
}
#Override
void chessPiecePlaced(ChessPiece chessPiece, ChessboardBlock cb) {
cb.setPiece(new ChessPiece(chessPiece.getImage(),
chessPiece.getType(),
cb.getBlockLocation(),
chessPiece.getColor()));
System.out.println("To Location: " + cb.getChessPiece().getLocation()
+ " Piece Type: " + cb.getChessPiece().getType()
+ " Piece Color: " + cb.getChessPiece().getColor());
}
};
Chessboard chessBoard = new Chessboard(chessPieceMouseAdapter);
chessPieceMouseAdapter.setChessboard(chessBoard);//or else NPE will be thrown when press/drag/release on chessboard occurs
BufferedImage knightImage = null;
try {
knightImage = ImageIO.read(new URL("http://i.stack.imgur.com/qdppY.png"));
} catch (Exception e) {
e.printStackTrace();
}
ChessPiece knightPiece = new ChessPiece(knightImage, "Knight", null, "White");//location parameter can be null or anything will be set if matching block is found
chessBoard.setChessPiece("A1", knightPiece);
NotationPanel rows = new NotationPanel(new String[]{"8", "7", "6", "5", "4", "3", "2", "1"}, NotationPanel.VERTICAL);
NotationPanel cols = new NotationPanel(new String[]{"A", "B", "C", "D", "E", "F", "G", "H"}, NotationPanel.HORIZONTAL);
frame.add(rows, BorderLayout.WEST);
frame.add(cols, BorderLayout.SOUTH);
frame.add(chessBoard);
frame.pack();
frame.setVisible(true);
}
});
}
}
class NotationPanel extends JPanel {
final static String HORIZONTAL = "horizontal";
final static String VERTICAL = "vertical";
public NotationPanel(String[] strings, String direction) {
if (direction.equals(VERTICAL)) {
setLayout(new GridLayout(8, 0));
} else {
setLayout(new GridLayout(0, 8));
}
for (String string : strings) {
this.add(new JLabel(string, JLabel.CENTER));
}
}
}
class Chessboard extends JPanel {
private final ArrayList<ChessboardBlock> chessBoardBlocks;
ChessPieceMouseAdapter chessPieceMouseAdapter;
public Chessboard(ChessPieceMouseAdapter chessPieceMouseAdapter) {
super(new GridLayout(8, 8));
chessBoardBlocks = new ArrayList<>(64);
layoutBoard();
this.chessPieceMouseAdapter = chessPieceMouseAdapter;
addMouseListener(this.chessPieceMouseAdapter);
addMouseMotionListener(this.chessPieceMouseAdapter);
}
private void layoutBoard() {
String[] cols = new String[]{"A", "B", "C", "D", "E", "F", "G", "H"};
int[] rows = new int[]{1, 2, 3, 4, 5, 6, 7, 8};
int NUMBER_OF_BLOCKS = 64;
String row, col;
int rowCount = 7, colCount = 0, trigger = 8;
for (int i = 0; i < NUMBER_OF_BLOCKS; i++) {
if (trigger == 0) {
colCount = 0;
trigger = 8;
rowCount--;
}
col = cols[colCount++];
row = String.valueOf(rows[rowCount]);
trigger--;
Color pieceHolderColor = ((rowCount + colCount) % 2 == 0 ? Color.WHITE : Color.BLACK);
String pieceHolderLocation = col + row;
ChessboardBlock pieceHolder = new ChessboardBlock(pieceHolderLocation, pieceHolderColor);
pieceHolder.setPiece(null);
add(pieceHolder);//add to the board
chessBoardBlocks.add(pieceHolder);//add to piece holder array
}
}
boolean setChessPiece(String location, ChessPiece piece) {
for (int i = 0; i < chessBoardBlocks.size(); i++) {
if (chessBoardBlocks.get(i).getBlockLocation().equalsIgnoreCase(location)) {
chessBoardBlocks.get(i).setPiece(new ChessPiece(piece.getImage(),
piece.getType(), chessBoardBlocks.get(i).getBlockLocation(),
piece.getColor()));
return true;
}
}
return false;
}
public ArrayList<ChessboardBlock> getChessBoardBlocks() {
return chessBoardBlocks;
}
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
if (chessPieceMouseAdapter.isDragging()) {
if (chessPieceMouseAdapter.getDraggedPiece() != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.drawImage(chessPieceMouseAdapter.getDraggedPiece().getImage(),
chessPieceMouseAdapter.getDraggedPieceLocation().x, chessPieceMouseAdapter.getDraggedPieceLocation().y, this);
}
}
}
}
class ChessboardBlock extends JLabel {
private final Dimension labelDimensions = new Dimension(50, 50);
private ChessPiece chessPiece;
private String location;
public ChessboardBlock(String location, Color backgroundColor) {
//super(location,JLabel.CENTER);//puts location as text on label
this.location = location;
setBackground(backgroundColor);
setOpaque(true);
}
#Override
public Dimension getPreferredSize() {
return labelDimensions;
}
void setPiece(ChessPiece p) {
this.chessPiece = p;
if (chessPiece == null) {
setIcon(null);
} else if (chessPiece.getImage() != null) {
setIcon(new ImageIcon(chessPiece.getImage()));
}
}
String getBlockLocation() {
return location;
}
public ChessPiece getChessPiece() {
return chessPiece;
}
}
class ChessPiece {
private BufferedImage image;
private String location;
private String type;
private final String color;
public ChessPiece(BufferedImage image, String type, String location, String color) {
this.image = image;
this.type = type;
this.location = location;
this.color = color;
}
public ChessPiece(ChessPiece p) {
this.image = p.getImage();
this.type = p.getType();
this.location = p.getLocation();
this.color = p.getColor();
}
public String getLocation() {
return location;
}
public String getColor() {
return color;
}
public void setLocation(String location) {
this.location = location;
}
public BufferedImage getImage() {
return image;
}
String getType() {
return type;
}
}
abstract class ChessPieceMouseAdapter extends MouseAdapter {
private Chessboard chessboard;
private ChessPiece draggedChessPiece;
private boolean dragging;
private Rectangle pieceRectangle;
private Point draggedPieceInitialLocation;
private Point pressedPoint;
public ChessPieceMouseAdapter() {
dragging = false;
draggedPieceInitialLocation = new Point();
pressedPoint = new Point();
}
public Point getDraggedPieceLocation() {
return new Point(pieceRectangle.x, pieceRectangle.y);
}
public ChessPiece getDraggedPiece() {
return draggedChessPiece;
}
#Override
public void mousePressed(MouseEvent me) {
pressedPoint = me.getPoint();
ArrayList<ChessboardBlock> chessBoardBlocks = chessboard.getChessBoardBlocks();
for (int i = 0; i < chessBoardBlocks.size(); i++) {
if (chessBoardBlocks.get(i).getChessPiece() != null) {
pieceRectangle = chessBoardBlocks.get(i).getBounds();
if (pieceRectangle.contains(pressedPoint)) {
ChessPiece chessPiece = chessBoardBlocks.get(i).getChessPiece();
if (chessPieceSelected(chessPiece, chessBoardBlocks.get(i))) {
draggedChessPiece = new ChessPiece(chessPiece);
chessBoardBlocks.get(i).setPiece(null);
draggedPieceInitialLocation.x = pieceRectangle.x;
draggedPieceInitialLocation.y = pieceRectangle.y;
dragging = true;
chessboard.repaint();
}
break;
}
}
}
}
#Override
public void mouseReleased(MouseEvent me) {
ArrayList<ChessboardBlock> chessBoardBlocks = chessboard.getChessBoardBlocks();
for (int i = 0; i < chessBoardBlocks.size(); i++) {
pieceRectangle = chessBoardBlocks.get(i).getBounds();
if (pieceRectangle.contains(me.getPoint())) {
if (draggedChessPiece != null) {
chessPiecePlaced(draggedChessPiece, chessBoardBlocks.get(i));
}
}
}
dragging = false;
draggedChessPiece = null;
chessboard.repaint();
}
#Override
public void mouseDragged(MouseEvent me) {
dragging = true;
pieceRectangle.x = me.getX() - (pressedPoint.x - draggedPieceInitialLocation.x);
pieceRectangle.y = me.getY() - (pressedPoint.y - draggedPieceInitialLocation.y);
chessboard.repaint();
}
boolean isDragging() {
return dragging;
}
abstract boolean chessPieceSelected(ChessPiece chessPiece, ChessboardBlock cb);
abstract void chessPiecePlaced(ChessPiece chessPiece, ChessboardBlock cb);
void setChessboard(Chessboard chessBoard) {
this.chessboard = chessBoard;
}
}

First of all you have new GridLayout(BRICKS/ ROWS, 0) which shows you are setting layout to have 0 Columns. (The API specifies GridLayout(int rows, int cols))
As for the problem with finding the x, y coords, you don't need to. If you have a placeholders (i.e. custom labels) you can set the background color to correspond to the board cell and hold the image in the cell. The images can be set on top of the background that way for the chess look and feel.
Have click events on the labels so that when the user clicks the label is does whatever you need it to do (move a piece to/from one cell to the other, player takes a piece, checks the board for mate/check mate, etc.)
You could implement the MouseListener's mousePressed and mouseReleased methods on the labels to get the desired start and end labels and if you want drag and drop functionality maybe implement MouseMotionListener's mouseDragged method and a custom paint method to the board (not labels).

Related

In Java how would I combine a grid of buttons with a grid of integers to create a minesweeper board?

So far all I have managed to do is combine my two files into one file that creates a GUI with the grid of buttons and runs an instance of minesweeper separately. I need the values from the grid of integers to reflect onto the grid of buttons so that after I click a button it reveals the integer underneath. Any help would be much appreciated.
import javax.swing.*;
import java.awt.event.*;
import java.awt.GridLayout;
import java.awt.Component;
import java.awt.Label;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JOptionPane;
public class Minesweeper extends JFrame implements MouseListener
{
private JButton [][] gridz;
private int [][] g;
private int max;
public Minesweeper()
{
gridz= new JButton [10][10];
builder();
g=new int[10][10];
setRandom(10);
}
public void setRandom(int max)
{
int z=0;
while(z<max)
{
int r=(int)(Math.random()*g.length);
int c=(int)(Math.random()*g[r].length);
if(g[r][c]==0)
{
g[r][c]=-1;
z++;
}
}
}
public int count(int r, int c)
{
int x=0;
if((r-1)>=0 && (c-1)>=0 && (r-1)<g.length && (c-1)<g.length && g[r-1][c-1]==-1)
{
x++;
}
if((r-1)>=0 && (r-1)<g.length && g[r-1][c]==-1)
{
x++;
}
if((r-1)>=0 && (c+1)>=0 && (r-1)<g.length && (c+1)<g.length && g[r-1][c+1]==-1)
{
x++;
}
if((c-1)>=0 && (c-1)<g.length && g[r][c-1]==-1)
{
x++;
}
if((c+1)>=0 && (c+1)<g.length && g[r][c+1]==-1)
{
x++;
}
if((r+1)>=0 && (c-1)>=0 && (r+1)<g.length && (c-1)<g.length && g[r+1][c-1]==-1)
{
x++;
}
if((r+1)>=0 && (r+1)<g.length && g[r+1][c]==-1)
{
x++;
}
if((r+1)>=0 && (c+1)>=0 && (r+1)<g.length && (c+1)<g.length && g[r+1][c+1]==-1)
{
x++;
}
return x;
}
public void setCounts()
{
for(int r=0; r<g.length; r++)
{
for(int c=0; c<g[r].length; c++)
{
if(g[r][c]==0)
{
g[r][c]=count(r,c);
}
String formatted = String.format("%2d", g[r][c]);
System.out.print(formatted + " ");
}
System.out.println();
}
}
public void builder()
{
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(10, 10));
for(int r=0; r<gridz.length; r++)
{
for(int c=0; c<gridz[r].length; c++)
{
gridz[r][c]=new JButton("*");
gridz[r][c].setActionCommand(r+":"+c);
gridz[r][c].addMouseListener(this);
getContentPane().add(gridz[r][c]);
}
}
setVisible(true);
}
public void mousePressed(MouseEvent e)
{
Component c = e.getComponent();
JButton b = (JButton)c;
System.out.println(b.getActionCommand());
String s=b.getActionCommand();
int f=s.indexOf(':');
if(f>=0)
{
String row=s.substring(0, f);
String col=s.substring(f+1);
String text=row+ '!' +col;
b.setText(text);
}
}
public void mouseClicked(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public static void main(String[] args)
{
Minesweeper a = new Minesweeper();
a.setCounts();
//JOptionPane.showMessageDialog(null, "BOOM");
}
}
One of the (many) things you want to do, is decouple your code. This means that the "logic" for the "game" should be independent of the "view", meaning that you can easily change the logic without adversely effecting the view.
This is where things like model-view-controller and single responsibility concepts come into play.
What you should (try) and do is start by describing the "data" or "model". What properties does it have and what functionality can be executed by other parties.
Since the models state could change, independently, you'll also need some way to notify interested parties, this is where an observer is helpful.
For example, a model for the game might be represented like...
public interface MineSweeperModel {
public interface Observer {
public void cellValueDidChange(MineSweeperModel model, int col, int row, int value);
}
public int getRows();
public int getColumns();
public int getValueAt(int col, int row);
public void expose(int col, int row);
public void setObserver(Observer observer);
}
Now, that's cool and everything, but what we need is some kind of implementation, which might look something like...
public class DefaultMineSweeperModel implements MineSweeperModel {
private int rows;
private int cols;
private int[][] bombsMap;
private int[][] visibleState;
private Observer observer;
public DefaultMineSweeperModel(int rows, int cols, int maxBombs) {
this.rows = rows;
this.cols = cols;
bombsMap = new int[cols][rows];
visibleState = new int[cols][rows];
Random rnd = new Random();
int count = 0;
while (count < maxBombs) {
int row = rnd.nextInt(rows);
int col = rnd.nextInt(cols);
if (bombsMap[col][row] == 0) {
bombsMap[col][row] = -1;
count++;
}
}
}
#Override
public void setObserver(Observer observer) {
this.observer = observer;
}
#Override
public int getRows() {
return rows;
}
#Override
public int getColumns() {
return cols;
}
#Override
public int getValueAt(int col, int row) {
return visibleState[col][row];
}
#Override
public void expose(int col, int row) {
if (visibleState[col][row] == 0 && bombsMap[col][row] == -1) {
// You've found a mine, might want to do something about it...
visibleState[col][row] = -1;
fireCellValueDidChange(col, row, -1);
} else if (visibleState[col][row] == 0) {
// Empty cell
visibleState[col][row] = 1;
fireCellValueDidChange(col, row, 1);
}
}
protected void fireCellValueDidChange(int col, int row, int value) {
if (observer == null) {
return;
}
observer.cellValueDidChange(this, col, row, value);
}
}
Now, we could just as easily create a "easy", "hard" and "you're going to die" implementations and seed the properties internally, but this basic implementation provides us with enough capacity to configure it.
But why (use a interface)? One of the principles of OO is "information hiding", which is supported by the concept of Polymorphism.
This means we can create any kind of model, with any kind of internal logic, but any one wanting to make use of the model, can easily do so by simply accepting a instance of the interface.
Now, the UI. This is basically responsible for providing a visual representation of the state of the model.
public class MineSweeperPane extends JPanel {
private JButton[] buttons;
private MineSweeperModel model;
public MineSweeperPane(MineSweeperModel model) {
this.model = model;
model.setObserver(new MineSweeperModel.Observer() {
#Override
public void cellValueDidChange(MineSweeperModel model, int col, int row, int value) {
int index = (model.getRows() * row) + col;
if (index >= buttons.length) {
System.err.println("No view for cell # " + col + "x" + row);
return;
}
buttons[index].setText(Integer.toString(value));
}
});
// You should be using ActionListener
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
JComponent source = (JComponent) e.getComponent();
// Here's something they won't teach you...
GridBagConstraints gbc = ((GridBagLayout)getLayout()).getConstraints(source);
System.out.println(gbc.gridx + "x" + gbc.gridy);
// Use class based Integer so we don't get NullPointerException
Integer row = (Integer)source.getClientProperty("row");
Integer col = (Integer)source.getClientProperty("col");
if (row == null || col == null) {
System.err.println("!! Invalid cell");
return;
}
System.out.println("Clicked " + col + "x" + row);
getModel().expose(col, row);
}
};
int rowCount = model.getRows();
int colCount = model.getColumns();
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
buttons = new JButton[rowCount * colCount];
for (int row = 0; row < rowCount; row++) {
for (int col = 0; col < colCount; col++) {
JButton btn = makeButton(col, row, model);
btn.addMouseListener(mouseListener);
gbc.gridx = col;
gbc.gridy = row;
add(btn, gbc);
buttons[row * rowCount + col] = btn;
}
}
}
public MineSweeperModel getModel() {
return model;
}
protected JButton makeButton(int col, int row, MineSweeperModel model) {
JButton btn = new JButton(Integer.toString(model.getValueAt(col, row))) {
#Override
public Dimension getPreferredSize() {
return new Dimension(35, 35);
}
};
btn.setFocusable(false);
btn.setFocusPainted(false);
btn.putClientProperty("row", row);
btn.putClientProperty("col", col);
return btn;
}
}
You will note two things.
I use get/setClientProperty to get and set the cell coordinates for the button within the model. This allows me to seed the int values without needing to format/parse them to/from a String, which is, frankly, messy
I also cheated and demonstrated the ability to pull the buttons grid coordinates directly from the GridBagLayout itself. I prefer to the first, but this is a nice side effect not many people know or make use of it.
When "clicked" (and you should be using an ActionListener, but you're apparently not meant to be using an ActionListener because you've not learnt about them ... which begs the question of, why are you using a JButton when ActionListener is the primary mechanism for monitoring for when it's triggered 🙄), the UI updates the model. If there are any changes to the model, the model's observer is notified, which then allows the UI to update its visual state.
I've not put any logic in to the model, apart from exposing the underlying value of the cell (difference between the visialMap and bombMap), so you'll need to do that
There are a number of ways to achieve this. You could simply include a line in your builder method that matches a button to a value from the g array and stores it in a hashmap or some other data structure like so mineHashMap.put(gridz[r][c], g[r][c]);, this way we can check the hashmap later in the mouse event method to find the correct value from the hashmap like so:
int value = mineHashMap.get(b);
b.setText(value+"");`
If starting from scratch, then a quick and easy/dirty solution would be to extend the JButton class to create a customJBotton which would store the integer value inside the class when you create the custom button, and you could contain other logic in it too. Then later when the button is clicked you can easily get the number in the mouse event like so b.setText(b.getMineValue()); or call a custom method you created in the custom button like so b.revealMine(); which would effectively do this.setText(mineValue);
There are many many other ways to do this, but the two options above would slot into your code quite easily. Custom painting on a JPanel rather than using buttons would also be a smart option but it does require more technical understanding and code.

Why do the panels disappear when i press UP?

This is my code for a game im making. At the moment im not worried about how the game functions I've been more so worried about the fact that each time I hit the UP button the panels disappear and sometimes when i hit the LEFT button as well. Is there an explanation to this can anyone help me understand why this happens?? I have a feeling it has something to do with my if statements but im not really sure. also, im messing around with the key listener and if you could give me some advice on key listeners like some dos and donts I really appreciate the help!!
import javax.swing.*;
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
public class Game extends Applet implements ActionListener,KeyListener {
Image image;
MediaTracker tr;
JLabel label,computerLabel;
JPanel panel,computerPanel;
Button start,up,down;
Label result;
Dimension SIZE = new Dimension(50,50);
int x = 0;
int y = 0;
int w = 100;
int q = 100;
int WIDTH = 50;
int HEIGHT = 50;
//Player Integers
int zeroPosX,zeroPosY,xLeft,xUp;
//Computer integers
int compZeroPosX,compZeroPosY,compXLeft,compXUp;
//--------------------------------------
public void init() {
setLayout(new FlowLayout());
start = new Button("Start");
up = new Button("UP");
down = new Button("LEFT");
//PlayerPiece stuff
ImageIcon icon = new ImageIcon("playerpiece.png");
label = new JLabel(icon);
panel = new JPanel();
label.setVisible(true);
panel.add(label);
panel.setPreferredSize(SIZE);
//ComputerPiece Stuff
ImageIcon computerIcon = new ImageIcon("computerPiece.png");
computerPanel = new JPanel();
computerLabel = new JLabel(computerIcon);
computerLabel.setVisible(true);
computerPanel.add(computerLabel);
computerPanel.setPreferredSize(SIZE);
//===============================================
result = new Label("=========");
addKeyListener(this);
setSize(650,650);
up.addActionListener(this);
down.addActionListener(this);
start.addActionListener(this);
label.setSize(WIDTH,HEIGHT);
label.setLocation(0,0);
add(computerPanel);
add(panel);
add(start);
add(up);
add(down);
add(result);
}
//--------------------------------------
public void paint(Graphics g) {
Graphics2D firstLayer = (Graphics2D)g;
Graphics2D secondLayer = (Graphics2D)g;
Graphics2D thirdLayer = (Graphics2D)g;
secondLayer.setColor(Color.BLACK);
for(x=100; x<=500; x+=100)
for(y=100; y <=500; y+=100)
{
firstLayer.fillRect(x,y,WIDTH,HEIGHT);
}
for(w=150; w<=500; w+=100)
for(q=150; q <=500; q+=100)
{
secondLayer.fillRect(w,q,WIDTH,HEIGHT);
}
}
//--------------------------------------
public void actionPerformed(ActionEvent ae) {
int [] range = {50,0,0,0,0};
int selection = (int)Math.random()*5 ;
//~~~~~~~~~~~~~~~~~~~~~~~~~
//PlayerPositioning
zeroPosX = panel.getX();
zeroPosY = panel.getY();
xLeft = zeroPosX - 50;
xUp = zeroPosY - 50;
//ComputerPositioning
compZeroPosX = computerPanel.getX();
compZeroPosY = computerPanel.getY();
compXLeft = compZeroPosX - range[selection];
compXUp = compZeroPosY - range[selection];
//~~~~~~~~~~~~~~~~~~~~~~~~~~
Button user = (Button)ae.getSource();
//Starting the game
if(user.getLabel() == "Start") {
result.setText("=========");
//playersetup
label.setLocation(0,0);
panel.setLocation(300,500);
//============================
//npc setup
computerLabel.setLocation(0,0);
computerPanel.setLocation(500,300);
}
if(compZeroPosX >= 150) {
if(compZeroPosY >= 150) {
if(zeroPosX >= 150) {
if(zeroPosY >=150) {
if(user.getLabel() == "UP") {
panel.setLocation(zeroPosX,xUp);
}
else
computerPanel.setLocation(compZeroPosX,compXUp);
if(user.getLabel() == "LEFT") {
panel.setLocation(xLeft,zeroPosY);
}
else
computerPanel.setLocation(compXLeft,compZeroPosY);
if(panel.getX() < 150)
result.setText("GAME-OVER");
if(panel.getY() < 150)
result.setText("GAME-OVER");
}
}
}
}
}
#Override
public void keyPressed(KeyEvent kp) {
int keycode = kp.getKeyCode();
switch (keycode) {
case KeyEvent.VK_W:
panel.setLocation(xLeft,zeroPosY);
break;
}
}
#Override
public void keyReleased(KeyEvent kr) {
}
#Override
public void keyTyped(KeyEvent kt) {
}
}
Issues and suggestions:
You're mixing AWT (e.g., Applet, Button, Label) with Swing (e.g., JPanel, JLabel) dangerously and without need. Stick with Swing and get rid of all vestiges of AWT.
You're painting directly in a top-level window, here the Applet, a dangerous thing to do. Don't. Follow the Swing graphics tutorials and do your drawing in a JPanel's paintComponent method.
You're not calling the super method within your painting method override, another dangerous thing to do, and another indication that you're trying to do this without reading the important relevant tutorials.
Don't compare Strings using == or !=. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two object references are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here.
You're trying to directly set the location of a component such as a JPanel without regard for the layout managers. Don't do this. Instead move logical (non-component) entities and display the movement in your graphics.
You can find links to the Swing tutorials and to other Swing resources here: Swing Info
Later we can talk why you should avoid applets of all flavors...
Myself, I'd move ImageIcons around a grid of JLabels and not directly use a painting method at all. For example,
To see, run the following code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Game2 extends JPanel {
private static final String CPU_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/"
+ "Gorilla-thinclient.svg/50px-Gorilla-thinclient.svg.png";
private static final String PERSON_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/"
+ "Emblem-person-blue.svg/50px-Emblem-person-blue.svg.png";
private static final int SQR_WIDTH = 50;
private static final int SIDES = 10;
private static final Dimension SQR_SIZE = new Dimension(SQR_WIDTH, SQR_WIDTH);
private static final Color DARK = new Color(149, 69, 53);
private static final Color LIGHT = new Color(240, 220, 130);
private JLabel[][] labelGrid = new JLabel[SIDES][SIDES];
private Icon playerIcon;
private Icon computerIcon;
public Game2() throws IOException {
// would use images instead
playerIcon = createIcon(PERSON_PATH);
computerIcon = createIcon(CPU_PATH);
JPanel buttonPanel = new JPanel();
buttonPanel.add(new JButton(new StartAction("Start", KeyEvent.VK_S)));
buttonPanel.add(new JButton(new UpAction("Up", KeyEvent.VK_U)));
buttonPanel.add(new JButton(new LeftAction("Left", KeyEvent.VK_L)));
JPanel gameBrd = new JPanel(new GridLayout(SIDES, SIDES));
gameBrd.setBorder(BorderFactory.createLineBorder(Color.BLACK));
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
JLabel label = new JLabel();
label.setPreferredSize(SQR_SIZE);
label.setOpaque(true);
Color c = i % 2 == j % 2 ? DARK : LIGHT;
label.setBackground(c);
gameBrd.add(label);
labelGrid[i][j] = label;
}
}
setLayout(new BorderLayout());
add(buttonPanel, BorderLayout.PAGE_START);
add(gameBrd);
// random placement, just for example
labelGrid[4][4].setIcon(computerIcon);
labelGrid[5][5].setIcon(playerIcon);
}
private Icon createIcon(String path) throws IOException {
URL url = new URL(path);
BufferedImage img = ImageIO.read(url);
return new ImageIcon(img);
}
private abstract class MyAction extends AbstractAction {
public MyAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
}
private class StartAction extends MyAction {
public StartAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO start game code
}
}
// move all icons up
private class UpAction extends MyAction {
public UpAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// collection to hold label that needs to be moved
Map<JLabel, Icon> labelMap = new HashMap<>();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
Icon icon = labelGrid[i][j].getIcon();
if (icon != null) {
int newI = i == 0 ? labelGrid.length - 1 : i - 1;
labelGrid[i][j].setIcon(null);
labelMap.put(labelGrid[newI][j], icon);
}
}
}
// move the icon after the iteration complete so as not to move it twice
for (JLabel label : labelMap.keySet()) {
label.setIcon(labelMap.get(label));
}
}
}
// move all icons left
private class LeftAction extends MyAction {
public LeftAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Map<JLabel, Icon> labelMap = new HashMap<>();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
Icon icon = labelGrid[i][j].getIcon();
if (icon != null) {
int newJ = j == 0 ? labelGrid[i].length - 1 : j - 1;
labelGrid[i][j].setIcon(null);
labelMap.put(labelGrid[i][newJ], icon);
}
}
}
for (JLabel label : labelMap.keySet()) {
label.setIcon(labelMap.get(label));
}
}
}
private static void createAndShowGui() {
Game2 mainPanel = null;
try {
mainPanel = new Game2();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("Game2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}

Mouse Listener Not Registering for JScrollPane within JLayeredPane

I am working on a project involving JSwing.
The project consists of a JLayeredPane that contains a JScrollPane and a game board (JPanel with JPanels with JLabel).
My problem is that I added a custom mouse listener to the JLayeredPane. However, when I click on the JScrollPane area the listener doesn't seem to register. However, if I click other places, it does.
Can anyone explain why this is occuring?
Thanks in advance!
Code to create panels + add listener to JLayeredPane:
private void initPanels() {
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setLayout(new BorderLayout());
layeredPane.add(makeBoardPanel(), BorderLayout.CENTER, JLayeredPane.DEFAULT_LAYER);
JPanel panel = new JPanel(new BorderLayout());
panel.add(makeButtonPanel(), BorderLayout.WEST);
panel.add(makeDeckPanel(), BorderLayout.EAST);
layeredPane.add(panel, BorderLayout.SOUTH, JLayeredPane.DEFAULT_LAYER);
layeredPane.add(makeMeeplePanel(), BorderLayout.NORTH, JLayeredPane.DEFAULT_LAYER);
//TODO: fix adapter + add others;
TileMouseAdapter tileMouseAdapter = new TileMouseAdapter(layeredPane);
layeredPane.addMouseListener(tileMouseAdapter);
layeredPane.addMouseMotionListener(tileMouseAdapter);
add(layeredPane);
myController.reset(TileDeck.getDemo(), NUM_PLAYERS); //sets up game
}
Code to make Board with JScrollPane:
private JScrollPane makeBoardPanel() {
JScrollPane scrollPane = new JScrollPane(CarcassoneBoard.getInstance());
CarcassoneBoard.getInstance().setScrollPane(scrollPane);
return scrollPane;
}
Here is the code for that last line (myController.reset( ... ))
public void reset(Map<TileFactory, Integer> deck, int numPlayers) {
myDeck.reset(deck);
myBoard.reset(myDeck.getDeckSize()); //only panel contained in JScrollPane
myMeeplePanel.reset(numPlayers);
}
Reset method for myBoard ...
public void reset(int num_tiles) {
super.removeAll();
NUM_TILES = num_tiles;
BOARD_SIZE = (int) Math.ceil(num_tiles / 2.0);
setLayout(new GridLayout(BOARD_SIZE, BOARD_SIZE, 1, 1));
setPreferredSize(new Dimension(BOARD_SIZE * TILE_WIDTH, BOARD_SIZE * TILE_HEIGHT));
STARTING_TILE = new BubbleCity((int) Math.ceil(BOARD_SIZE / 2.0 - 1), (int) Math.ceil(BOARD_SIZE / 2.0 - 1));
myTiles = new TilePanel[BOARD_SIZE][BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
myTiles[i][j] = new TilePanel(i, j, new EmptyTile(i, j));
add(myTiles[i][j]);
}
}
myTiles[STARTING_TILE.getRow()][STARTING_TILE.getCol()].setTile(STARTING_TILE);
revalidate();
repaint();
}
Here is the code for the mouse listener:
public class TileMouseAdapter extends MouseAdapter {
private JLayeredPane myLayeredPane;
private Tile myTile;
private TilePanel myClickedPanel;
public TileMouseAdapter(JLayeredPane layeredPane) {
myLayeredPane = layeredPane;
}
private void reset() {
if (myTile != null) {
myLayeredPane.remove(myTile);
myLayeredPane.revalidate();
myLayeredPane.repaint();
}
myTile = null;
myClickedPanel = null;
}
//point p is a point relative to myLayeredPane...
public Point translatePoint(Component component, Point p) {
return SwingUtilities.convertPoint(myLayeredPane, p, component);
}
//point p is relative to the screen (event.getLocationOnScree()). We want to check if that point is in component.
public boolean containsPoint(Component component, Point p) {
Point translated = translatePoint(component, p );
return translated.getX() > 0 && translated.getY() > 0
&& translated.getX() < component.getWidth() && translated.getY() < component.getHeight();
}
#Override
public void mouseClicked(MouseEvent event) {
}
#Override
public void mousePressed(MouseEvent event) {
//no registering jscrollpane?
CarcassoneBoard board = CarcassoneBoard.getInstance();
TileDeck deck = TileDeck.getInstance();
System.out.println("pressed!");
if (containsPoint(deck, event.getPoint())) {
System.out.println("deck");
myClickedPanel = deck;
myTile = myClickedPanel.getTile();
}
else if (containsPoint(board.getScrollPane().getViewport(), event.getPoint())) {
System.out.println("board");
Component component = board.getComponentAt(translatePoint(board.getScrollPane().getViewport(), event.getPoint()));
if (component instanceof CarcassoneBoard) {return;} //user clicked in between tiles...do nothing.
myClickedPanel = (TilePanel) component;
myTile = myClickedPanel.getTile();
}
if (myTile == null || myTile instanceof EmptyTile || !myTile.isDraggable()) {
//TODO: scroll with drag!
reset();
return;
}
myClickedPanel.setEmpty(); //panel will set an empty tile automatically and remove myTile...
int x = event.getPoint().x - myTile.getWidth() / 2;
int y = event.getPoint().y - myTile.getHeight() / 2;
myTile.setLocation(x, y);
myLayeredPane.revalidate();
myLayeredPane.repaint();
try {
myLayeredPane.add(myTile, JLayeredPane.DRAG_LAYER);
myLayeredPane.revalidate();
myLayeredPane.repaint();
} catch (IllegalArgumentException e) {
//TODO: deal with this?
//gives error for some unknown reason, but doesnt effect anything? ignore...dumb error cus jswing sucks
}
}
#Override
public void mouseDragged(MouseEvent event) {
if (myTile == null) {
return;
}
int x = event.getPoint().x - myTile.getWidth() / 2;
int y = event.getPoint().y - myTile.getHeight() / 2;
myTile.setLocation(x, y);
myLayeredPane.revalidate();
myLayeredPane.repaint();
}
#Override
public void mouseReleased(MouseEvent event) {
if (myTile == null) {// || !myTile.isDraggable()) {
return; //do nothing...board scrolling
}
CarcassoneBoard board = CarcassoneBoard.getInstance();
myLayeredPane.remove(myTile);
TilePanel dropped = (TilePanel) board.getComponentAt(translatePoint(board.getScrollPane().getViewport(), event.getPoint()));
if (dropped == null) {
//reset tile to original spot
myClickedPanel.setTile(myTile);
reset();
return;
}
dropped.setTile(myTile);
reset();
}
}
NOTE:
I changed makeBoardPanel() to this:
private JPanel makeBoardPanel() {
return CarcassoneBoard.getInstance();
}
and the listener seemed to register clicks on the board which is what led me to believe
that it has something to do with the JScrollPane. I determined this by putting a print statement right after the mousePressed method header. When I clicked on the board it printed the statement right before complaining about the fact that I was referencing a JScrollPane that didn't exist later in the code :/

How to get the background color of a JList's unselected cell?

I am trying to find the color used by cells that are unselected. This is usually white, however when I call UIManager.getColor("List.background"), it appears to be the same grey color used for JPanels. When I call new JList().getBackground(), I get the same horrid grey color back, but when I actually use the list, it's white. How do I get this white color from JList or UIManager? What I am currently doing to find the background color is this:
String[] contents = {"Foo", "Bar"};
JList list = new JList(contents);
// Prints true
System.out.println(list.getBackground().equals(new Color(237, 236, 235)));
Since List.selectionBackground gives me the blue color I expect to see when I click on a cell, I figured List.background would give me the color of an unselected cell. What is List.background actually returning a value for then?
On related note, is there a listing somewhere of what these keys means? I've found a related question, but none of the answers provide descriptions of the keys.
EDIT: It appears this is the correct way to do this. However, at least in GNOME the problem arises when calling setLookAndFeel.
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
String[] contents = {"Foo", "Bar"};
JList list = new JList(contents);
// Prints true
System.out.println(list.getBackground().equals(new Color(237, 236, 235)));
// Add list to a pane and display it, and it will actually be white
Looks like this might be a bug, sorry guys.
SwingUtilities.updateComponentTreeUI(myFrame);
EDIT:
hmmm, can you clarify me (us) what is your issue based on this example (from died Old.Good.Forum.Sun.com, :-) and I know that not Jeanette resist)
Note: better would be change this value by using getListCellRendererComponent
EDIT2: please for another play with that check camickr UI Defaluts
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JListDisabledItemDemo implements ItemListener, Runnable {
private static final String ITEMS[] = {"Black", "Blue", "Green", "Orange", "Purple", "Red", "White", "Yellow"};
private JList jList;
private JCheckBox[] checkBoxes;
private boolean[] enabledFlags;
#Override
public void run() {
JPanel pnlEnablers = new JPanel(new GridLayout(0, 1));
pnlEnablers.setBorder(BorderFactory.createTitledBorder("Enabled Items"));
checkBoxes = new JCheckBox[ITEMS.length];
enabledFlags = new boolean[ITEMS.length];
for (int i = 0; i < ITEMS.length; i++) {
checkBoxes[i] = new JCheckBox(ITEMS[i]);
checkBoxes[i].setSelected(true);
checkBoxes[i].addItemListener(this);
enabledFlags[i] = true;
pnlEnablers.add(checkBoxes[i]);
}
jList = new JList(ITEMS);
jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jList.setSelectionModel(new DisabledItemSelectionModel());
jList.setCellRenderer(new DisabledItemListCellRenderer());
jList.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
System.out.println("selection");
}
}
});
JScrollPane scroll = new JScrollPane(jList);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
JFrame f = new JFrame("Colors");
Container contentPane = f.getContentPane();
contentPane.setLayout(new GridLayout(1, 2));
contentPane.add(pnlEnablers);
contentPane.add(scroll);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(240, 280);
f.setLocationRelativeTo(null);
UIManager.put("List.background", Color.red);
UIManager.put("List.selectionBackground", Color.orange);
UIManager.put("List.selectionForeground", Color.blue);
UIManager.put("Label.disabledForeground", Color.magenta);
SwingUtilities.updateComponentTreeUI(f);
f.setVisible(true);
}
#Override
public void itemStateChanged(ItemEvent event) {
JCheckBox checkBox = (JCheckBox) event.getSource();
int index = -1;
for (int i = 0; i < ITEMS.length; i++) {
if (ITEMS[i].equals(checkBox.getText())) {
index = i;
break;
}
}
if (index != -1) {
enabledFlags[index] = checkBox.isSelected();
jList.repaint();
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new JListDisabledItemDemo());
}
private class DisabledItemListCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 1L;
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (enabledFlags[index]) {
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
Component comp = super.getListCellRendererComponent(list, value, index, false, false);
comp.setEnabled(false);
return comp;
}
}
private class DisabledItemSelectionModel extends DefaultListSelectionModel {
private static final long serialVersionUID = 1L;
/**
* No need to override addSelectionInterval(int index0, int index1)
* since we're using SINGLE_SELECTION mode for this demo.
*/
#Override
public void setSelectionInterval(int index0, int index1) {
if (enabledFlags[index0]) {
super.setSelectionInterval(index0, index0);
} else {
/*
* The previously selected index is before this one,
* so walk forward to find the next selectable item.
*/
if (getAnchorSelectionIndex() < index0) {
for (int i = index0; i < enabledFlags.length; i++) {
if (enabledFlags[i]) {
super.setSelectionInterval(i, i);
return;
}
}
} /*
* Otherwise, walk backward to find the next selectable item.
*/ else {
for (int i = index0; i >= 0; i--) {
if (enabledFlags[i]) {
super.setSelectionInterval(i, i);
return;
}
}
}
}
}
}
}

Displaying Large JLIST formatted as HTML

I have a java applet in which I have to display a large amount of items (dictionary entries). The user needs to be able to select individual items in the list, hence it is implemented as a JList. Generating the list was very quick until I decided to make the display more asthetically pleasing by formatting the individual items using HTML. Now the list looks pretty but it takes between 10 and 15 seconds to generate it each time the user accesses the dictionary (without formatting it occurs almost instantly). I suppose I could take the performance hit up front by generating the list when the user first enters the application and just hiding and unhiding the list as needed. But, I'm wondering if there is a better way. Perhaps a more efficient way to generate the list.
Here is the section of code where the slow down occurrs (Between the display of C and D):
DefaultListModel dictCodeModel = new DefaultListModel();
System.out.println("C");
while (e.hasMoreElements()) {
String currentEntry = "";
DEntry dentry = (DEntry) e.nextElement();
if (!filter)
dictCodeModel.addElement(dentry.theToken); // tokens have embedded html tags
}
System.out.println("D");
As you can see it is pretty simple. When "theToken" is formatted as HTML, I get a real performance hit. Any ideas of what I can do to get around this?
Thanks,
What kind of HTML formatting are you using? If it's just some text styling (font, color), you can use a JLabel, set its properties accordingly and set it as ListCellRenderer for the JList.
The links above are a bit out of date so here's something more up to date.
Simply using JTable is a huge improvement in speed on initial load but a little slow when you first start scrolling. And you have the new problem that the row height needs adjusting. This can be done inside of a custom renderer easy enough by implementing TableCellRenderer since the getTableCellRendererComponent method gives you access to the row, the table and the component. This will however fire a update of the table which will call the same code. If you code appropriately, this won't be a problem. Still, it's better practice to put it somewhere else. I added a listener to the JViewport and only updated the rows that are currently in view. The code I based this on is here
Alternatively, you can use write a ListCellRenderer that returns a JPanel that looks like the HTML. If you only need a JTextArea then you'll need to set its width to ensure it's preferred height is set correctly like in this answer. Again, you have to update the row's width and it makes sense to do this based on the JViewport.
If you're curious about the performance of both approaches, then a custom renderer returning a JPanel is faster than JLabels rendering HTML. Both are reasonably quick though even with lists with a few thousand items. As mentioned, they can be a little slow when you initially scroll.
Finally, here's some code that lets you make a quick comparison yourself:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.Timer;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class JTableHtmlTest extends JFrame {
protected static final long serialVersionUID = 1L;
public static class Item {
public int id;
public String msg;
}
static class TableModel extends AbstractTableModel {
private static final long serialVersionUID = JListTest.serialVersionUID;
private Item[] items = new Item[] {};
public int getRowCount() {
return items.length;
}
public int getColumnCount() {
return 1;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return items[rowIndex];
}
#Override
public String getColumnName(int column) {
return "";
}
public void updateItems() {
SwingWorker<Item[], Void> worker = new SwingWorker<Item[], Void>() {
#Override
protected Item[] doInBackground() throws Exception {
final Item[] tempList = new Item[3000];
for (int i = 0; i < tempList.length; i++) {
Item item = new Item();
item.id = (int) (Math.random() * 10000);
item.msg = "This is the default message that has to be"
+ " long enough to wrap around a few times so that"
+ " we know things are working. It's rather tedious to write.";
tempList[i] = item;
}
return tempList;
}
#Override
protected void done() {
try {
items = get();
fireTableDataChanged();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
worker.execute();
}
}
public static class TableRenderer implements TableCellRenderer {
private static final String strColor = "#EDF5F4";
private static final Color strideColor = Color.decode(strColor);
JLabel htmlLabel = new JLabel();
JPanel noHtmlPanel = new JPanel();
JLabel noHtmlLabel = new JLabel();
JTextArea noHTMLTextArea = new JTextArea();
Item toRender = null;
boolean useHtml = false;
public TableRenderer() {
noHTMLTextArea.setWrapStyleWord(false);
noHTMLTextArea.setLineWrap(true);
noHTMLTextArea.setOpaque(false);
Font defaultFont = noHtmlLabel.getFont();
Font boldFont = defaultFont.deriveFont(Font.BOLD);
noHtmlLabel.setFont(boldFont);
noHtmlLabel.setOpaque(false);
noHtmlPanel.setLayout(new BorderLayout());
noHtmlPanel.add(noHtmlLabel, BorderLayout.NORTH);
noHtmlPanel.add(noHTMLTextArea, BorderLayout.SOUTH);
}
public void setUseHtml(boolean useHtml) {
this.useHtml = useHtml;
}
public Component getJlabelRenderer(JTable table, Item value, int row) {
String colorString = "";
if (row % 2 == 0) {
colorString = "background-color:" + strColor + ";";
}
if (toRender != value) {
toRender = value;
htmlLabel.setText("<html><div style='padding:2px;" + "width:"
+ table.getWidth() + ";" + colorString
+ "color:black;'>"
+ "<div style='padding:2px;font-weight:500;'>"
+ "Item " + value.id + "</div>" + value.msg
+ "</div></html>");
}
return htmlLabel;
}
public Component getNoHtmlRenderer(JTable table, Item value, int row) {
if (toRender != value) {
toRender = value;
noHtmlLabel.setText("Item " + value.id);
noHTMLTextArea.setText(value.msg);
if (row % 2 == 0) {
noHtmlPanel.setBackground(strideColor);
noHtmlPanel.setOpaque(true);
} else {
noHtmlPanel.setOpaque(false);
}
}
return noHtmlPanel;
}
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
if (useHtml) {
return getJlabelRenderer(table, (Item) value, row);
} else {
return getNoHtmlRenderer(table, (Item) value, row);
}
}
}
public JTableHtmlTest() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel controlPanel = new JPanel();
JButton updaterControl = new JButton("Update 3000");
final JCheckBox useHtmlControl = new JCheckBox("Use HTML");
final TableModel model = new TableModel();
final JTable table = new JTable(model);
final TableRenderer renderer = new TableRenderer();
JScrollPane scrollPane = new JScrollPane(table);
final JLabel durationIndicator = new JLabel("0");
controlPanel.add(useHtmlControl, BorderLayout.WEST);
controlPanel.add(updaterControl, BorderLayout.EAST);
getContentPane().add(controlPanel, BorderLayout.PAGE_START);
getContentPane().add(scrollPane, BorderLayout.CENTER);
getContentPane().add(durationIndicator, BorderLayout.PAGE_END);
table.setDefaultRenderer(Object.class, renderer);
// Only update the JTable row heights when they are in view
final JViewport viewport = scrollPane.getViewport();
viewport.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
Rectangle viewRect = viewport.getViewRect();
int first = table.rowAtPoint(new Point(0, viewRect.y));
if (first == -1) {
return;
}
int last = table.rowAtPoint(new Point(0, viewRect.y
+ viewRect.height - 1));
if (last == -1) {
last = model.getRowCount() - 1;
}
int column = 0;
for (int row = first; row <= last; row++) {
Component comp = table.prepareRenderer(
table.getCellRenderer(row, column),
row, column);
int rowHeight = comp.getPreferredSize().height;
table.setRowHeight(row, rowHeight);
}
}
});
updaterControl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
renderer.setUseHtml(useHtmlControl.isSelected());
model.updateItems();
}
});
Timer counter = new Timer();
counter.schedule(new TimerTask() {
#Override
public void run() {
String previousCounter = durationIndicator.getText();
final String newCounter = Integer.toString(Integer
.parseInt(previousCounter) + 1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
durationIndicator.setText(newCounter);
setTitle(newCounter);
}
});
}
}, 0, 100);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JTableHtmlTest jlt = new JTableHtmlTest();
jlt.pack();
jlt.setSize(300, 300);
jlt.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}

Categories