Related
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.
I want to add MouseListener to a class which extends JPanel. The Listener references to another class where the JPanel itself is created and added to contentPane. By pressing the mouse on JPanel, it should be removed. It is working well but I cannot call repaint() because my removing methode is static. How I can solve this?
This is the extended class:
public class PanelDraggable extends JPanel {
public PanelDraggable () {
.
.
.
MouseInputAdapter closeMouseAdapter = new CloseMouseHandler();
addMouseListener(closeMouseAdapter);
}
private class CloseMouseHandler extends MouseInputAdapter {
public void mouseClicked(MouseEvent e) {
TblManagement.RemoveTable();
}
}
}
And in this class I want to create the panel and remove it:
public void AddTables() {
String dbShortName = combo.getSelectedItem().toString();
String dbName = prop.get(dbNamesFile, dbShortName);
int x = 50; int y = 150; int width = 150; int height = 220; //set Size and location
int [] indices = tblList.getSelectedIndices();
panel = new PanelDraggable [indices.length];
for (int i = 0; i < indices.length ; i++) {
String tblName = tblList.getModel().getElementAt(i).toString();
String sql = "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE "+
"`TABLE_SCHEMA`='"+dbName+"' AND `TABLE_NAME`='"+tblName+"'";
JList<?> columnslist = new JList<String>();
columnslist.setModel(getListModel(dbName, sql, 1));
JScrollPane sc = new JScrollPane(columnslist);
panel[i] = new PanelDraggable(x, y , width, height, tblName, sc);
MouseInputAdapter mousehandler = new TableMouseHandler();
panel[i].addMouseListener(mousehandler);
mainPanel.add(panel[i]);
x+=15;
y+=20;
}
revalidate();
repaint();
}
private class TableMouseHandler extends MouseInputAdapter {
public void mouseEntered(MouseEvent e) {
removeComp = (JComponent) e.getComponent();
}
}
public static void RemoveTable() {
mainPanel.remove(removeComp);
}
Thank you for help.
You need to take in an instance of the TableManagement class as an argument for each panel, that way you don't have to do it in the static context. Also, I removed the unnecessary sub-classes for your mouseAdapters you were creating and created them in the more accepted way.
public class PanelDraggable extends JPanel {
public PanelDraggable (..., final TableManagement tblManagement) {
.
.
.
addMouseListener(new MouseAdapter() {
#Override
public void (MouseEvent e) {
tblManagement.removeTable();
}
});
}
}
Then in your TableManagement Class
public void AddTables() {
String dbShortName = combo.getSelectedItem().toString();
String dbName = prop.get(dbNamesFile, dbShortName);
int x = 50; int y = 150; int width = 150; int height = 220; //set Size and location
int [] indices = tblList.getSelectedIndices();
panel = new PanelDraggable [indices.length];
for (int i = 0; i < indices.length ; i++) {
String tblName = tblList.getModel().getElementAt(i).toString();
String sql = "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE "+
"`TABLE_SCHEMA`='"+dbName+"' AND `TABLE_NAME`='"+tblName+"'";
JList<?> columnslist = new JList<String>();
columnslist.setModel(getListModel(dbName, sql, 1));
JScrollPane sc = new JScrollPane(columnslist);
// There is an extra argument here. Whatever your
// mainPanel used to be in the static method
panel[i] = new PanelDraggable(x, y , width, height, tblName, sc, mainPanel);
panel[i].addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered() {
removeComp = (JComponent) e.getComponent();
}
});
mainPanel.add(panel[i]);
x+=15;
y+=20;
}
revalidate();
repaint();
}
public void RemoveTable() {
this.remove(removeComp);
}
Side-Note: You should look into Java naming conventions. Only classes have their first character capitalized.
Alright, I am working on a rather large program.
I will try to put in only what you need.
So right now, the comboBox is filling (with the override toString), and the first item is selected.
When I choose different parameters and force the contents of the comboBox to change, the new list is put into the comboBox and the first item is selected again.
I can see the update, so I know it is filling properly.
The problem is that when I select anything in the comboBox, nothing happens.
The first item remains selected, none of my System.out.println lines are printing, so nothing is being executed.
When I remove my override toString, everything works as intended.
The strange part, past that, is when having this override toString removed, it falls back on the parent class who has an override toString.
What is going on?
To my understanding the toString literally changes what is displayed, it does not change the data.
I am adding the objects to the comboBox, but displaying a bit of the information.
public class Belt extends Part{
//variable initialization and methods
#override
public String toString(){
String display = this.getCode() + " - " + this.color;
return display;
}
public final class Something implements ActionListener{
//variable initialization and methods
//there are several methods that call the fillBeltCombo()
GridBagConstraints c = new GridBagConstraints();
private void pad(GridBagConstraints c){
c.anchor = GridBagConstraints.NORTHWEST;
c.weightx = 1;
c.insets.left = 10;
c.insets.right = 10;
c.insets.top = 5;
c.insets.bottom = 5;
}
beltCombo = new JComboBox();
beltCombo.setVisible(true);
c.gridwidth = 2;
c.gridx = 4;
c.gridy = 9;
beltCombo.addActionListener((ActionEvent eventBelt) -> {
JComboBox beltCodeCombo1 = (JComboBox) eventBelt.getSource();
if(beltCombo.getItemCount()>0){
currentProduct.setBelt((Belt)beltCodeCombo1.getSelectedItem());
}else{/*do nothing*/}
});
pane.add(beltCombo, c);
public static void fillBeltCombo(ArrayList<Belt> list){
beltCombo.removeAllItems();
int size = list.size();
for(int x=0; x<size; x++){
beltCombo.addItem(list.get(x));
}
}
}
So taking the advice of markspace, I created a MCVE (the best I could while maintaining what my program was doing). Everything down there is done in my real program, although more complicated. I am not sure yet what my program is doing wrong, however I did find out that what I am trying to do is possible, I am able to select an item from the second comboBox while using an Override toString in the subclass. Now I just have to track the problem down in the actual code...
package minitest;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainClass {
JFrame frame;
JPanel pane;
JLabel label1, label2;
JComboBox comboA, comboB;
ArrayList<Parent> parents = new ArrayList<>();
ArrayList<Grandchild> grandkids = new ArrayList<>();
Parent parent1 = new Parent(1);
Parent parent2 = new Parent(2);
Grandchild grandkid1 = new Grandchild(1,1,"oneone");
Grandchild grandkid2 = new Grandchild(1,2,"onetwo");
Grandchild grandkid3 = new Grandchild(2,1,"twoone");
Grandchild grandkid4 = new Grandchild(2,2,"twotwo");
public class Parent{
protected int num;
public Parent(int num){
this.num = num;
}
#Override public String toString(){
return String.valueOf(num);
}
}
public class Child extends Parent{
private double dbl;
public Child(int num, double dbl){
super(num);
this.dbl = dbl;
}
}
public class Grandchild extends Child{
private String str;
public Grandchild(int num, double dbl, String str){
super(num, dbl);
this.str = str;
}
#Override public String toString(){
return this.num + " - " + this.str;
}
}
//Fill passed combo with each item from passed ArrayList
public void fillCombo(ArrayList list, JComboBox target){
target.removeAllItems();
int size = list.size();
for(int x = 0; x < size; x++){
target.addItem(list.get(x));
}
}
//remove all items from combobox except selected item
public void comboUpdate(JComboBox combo){
if(combo.getSelectedItem()==null){
/*donothing*/
}else{
int size = (combo.getItemCount()-1);
System.out.println("size: "+size);
for (int x=size; x>=0; x--) {
if(combo.getSelectedItem().equals(combo.getItemAt(x))){
/*donothing*/
System.out.println(combo.getSelectedItem()+" is selected");
}else{
System.out.println(combo.getItemAt(x)+" is now removed");
combo.removeItemAt(x);
}
}
}
}
//Add each item from passed ArrayList if not equal to the selected item
public void removeDuplicate(JComboBox combo, ArrayList array){
System.out.println("removeDuplicate: "+array);
if(combo.getSelectedItem()==null){
System.out.println("combo.getSelectedItem==null");
fillCombo(array, combo);
}else{
boolean validChoice = false;
int arraySize = array.size();
for(int x = 0; x < arraySize; x++){
if(combo.getSelectedItem().equals(array.get(x))){
System.out.println(combo.getSelectedItem()+" == "+array.get(x));
validChoice = true;
}else{
System.out.println(combo.getSelectedItem()+" != "+array.get(x));
combo.addItem(array.get(x));
}
}
if(validChoice == false){
System.out.println("removeItemAt(0)");
combo.removeItemAt(0);
}
}
}
//fill comboB with grandchild objects whos int match the selected partent objects int
public void fillComboB(){
System.out.println("grandkids: "+grandkids);
ArrayList<Grandchild> temp = copyArrayList(grandkids);
System.out.println("temp: "+temp);
comboUpdate(comboB);
int size = (temp.size()-1);
for(int x=size; x>=0; x--){
if(temp.get(x).num==((Parent)comboA.getSelectedItem()).num){
/*donothing*/
System.out.println("donothing: temp.get("+x+")="+temp.get(x));
}else{
System.out.println("temp.remove("+x+")="+temp.get(x));
temp.remove(x);
}
}
removeDuplicate(comboB, temp);
}
public ArrayList copyArrayList(ArrayList list){
ArrayList<Object> temp = new ArrayList<>();
int size = list.size();
for(int x=0; x<size; x++){
temp.add(list.get(x));
}
return temp;
}
public MainClass(){
parents.add(parent1);
parents.add(parent2);
grandkids.add(grandkid1);
grandkids.add(grandkid2);
grandkids.add(grandkid3);
grandkids.add(grandkid4);
frame = new JFrame("frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane = new JPanel();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
comboA = new JComboBox();
fillCombo(parents, comboA);
c.gridx = 0;
c.gridy = 0;
comboA.addActionListener((ActionEvent eventComboA) -> {
System.out.println("eventComboA");
JComboBox comboA1 = (JComboBox) eventComboA.getSource();
if(comboA.getItemCount()>0){
System.out.println("comboA.getItemCount>0: "+(Parent)comboA1.getSelectedItem());
fillComboB();
}
});
pane.add(comboA, c);
comboB = new JComboBox();
c.gridx = 0;
c.gridy = 1;
comboB.addActionListener((ActionEvent eventComboB) -> {
System.out.println("eventComboB");
JComboBox comboB1 = (JComboBox) eventComboB.getSource();
if(comboB.getItemCount()>0){
System.out.println("comboB.getItemCount>0: "+(Grandchild)comboB1.getSelectedItem());
}
});
pane.add(comboB, c);
frame.setContentPane(pane);
frame.setSize(150,150);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static void runGUI(){
JFrame.setDefaultLookAndFeelDecorated(false);
MainClass create = new MainClass();
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(() -> {
runGUI();
});
}
}
Aright, so based on yesterdays successful creation (which I was frustrated by) I continued to fill in the skeleton until it broke again.
- Adjusted classes to reflect the actual types of data that I was dealing with.
- Added my override equals method <-------problem
What happened was that when I originally created the equals override, all of the toStrings were the same, and that was displayed in the comboBox.
So the equals override looked like
#Override public boolean equals(Object other){
if (other == null) return false;
return (String.valueOf(other)).equals(this.description);
}
The problem with this, was that when I was asked to change what was displayed in the comboBox, I did not realize that the comboBox seems to use that equals check. The new other Object was
this.getCode() + " - " + this.color;
and it was being compared to num, which returned false.
Now correct me if I am wrong, but the moral of the story seems to be: JComboBox will not let you select something from the list that does not equal something on the list. So because my equals override changed how the objects were compared:
string value of object, compared to string variable of the object-
the same object was not equaling itself.
The adjusted code:
#Override public boolean equals(Object other){
if (other == null) return false;
return ((Parent)other).description.equals(this.description);
}
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).
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();
}
}
});
}
}