Sorting JPanels based on component - java

My program generates random numbers from 0 to 12 but if the result is 12 it would set dash as the text of JLabel, instead of the number generated.
Now, I wanted to sort my JPanel in ascending order based on the JLabel contents. In case of similarities in numbers, the black JPanels are placed on the left. It works fine except when there are dashes included, in which it doesn't sort properly. I would like to insert the JPanels containing dashes anywhere but it's not working as expected.
Screencaps from a shorter version of my program:
Pure numbers:
Dash included:
Here's the shorter version of my code (using the logic of integer sorting):
import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.Comparator;
public class SortFrames extends JFrame
{
static ArrayList<JPanel> panels = new ArrayList<JPanel>();
JPanel panel = new JPanel();
JPanel sortPane = new JPanel();
int toWrite = 0;
int colorGen = 0;
int comparison = 0;
Random rand = new Random();
public SortFrames()
{
for(int i = 0; i<4;i++)
{
panels.add(new JPanel());
}
for(JPanel p: panels)
{
toWrite = rand.nextInt(13);
colorGen = rand.nextInt(2);
p.add(new JLabel());
JLabel lblToSet = (JLabel)p.getComponent(0);
if(colorGen == 0)
{
p.setBackground(Color.BLACK);
lblToSet.setForeground(Color.WHITE);
}
if(colorGen == 1)
{
p.setBackground(Color.WHITE);
lblToSet.setForeground(Color.BLACK);
}
if(toWrite != 12){lblToSet.setText("" +toWrite);}
if(toWrite == 12){lblToSet.setText("-");}
p.setPreferredSize(new Dimension(30, 30));
panel.add(p);
}
sortMethod();
for(JPanel p: panels)
{
panel.add(p);
panel.revalidate();
}
add(panel);
panel.setPreferredSize(new Dimension(300, 300));
setPreferredSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
pack();
setLocationRelativeTo(null);
}
public void sortMethod()
{
for(int i = 0; i<(panels.size());i++)
{
for(int j = i+1; j<(panels.size());j++)
{
JLabel one = (JLabel)(panels.get(i)).getComponent(0);
JLabel two = (JLabel)(panels.get(j)).getComponent(0);
String lblOne = one.getText();
String lblTwo = two.getText();
if(!lblOne.equals("-") && !lblTwo.equals("-"))
{
int comp1 = Integer.parseInt(lblOne);
int comp2 = Integer.parseInt(lblTwo);
JPanel pnl1 = panels.get(i);
JPanel pnl2 = panels.get(j);
if(comp1 == comp2)
{
if(pnl1.getBackground() == Color.BLACK && pnl2.getBackground() == Color.WHITE)
{
panels.set(i, pnl1);
panels.set(j, pnl2);
}
if(pnl1.getBackground() == Color.WHITE && pnl2.getBackground() == Color.BLACK)
{
panels.set(i, pnl2);
panels.set(j, pnl1);
}
}
if(comp1 != comp2)
{
if(comp1>comp2)
{
panels.set(i, pnl2);
panels.set(j, pnl1);
}
}
}
if(lblOne.equals("-") && !lblTwo.equals("-"))
{
JPanel pnl1 = panels.get(i);
panels.set(rand.nextInt(panels.size()), pnl1);
}
if(!lblOne.equals("-") && lblTwo.equals("-"))
{
JPanel pnl2 = panels.get(j);
panels.set(rand.nextInt(panels.size()), pnl2);
}
}
}
}
public static void main(String args[])
{
new SortFrames();
}
}
I also have another method, which is by using Comparator class which also creates the same problem (this sorts equal numbers based on foreground but still the same as to sort equal numbers based on background so it has no effect on the said issue).
private static class JPanelSort implements Comparator<JPanel>
{
#Override
public int compare(JPanel arg0, JPanel arg1)
{
JLabel one = ((JLabel) arg0.getComponent(0));
JLabel two = ((JLabel) arg1.getComponent(0));
String firstContent = one.getText();
String secondContent = two.getText();
try
{
comparisonRes = Integer.compare(Integer.parseInt(firstContent), Integer.parseInt(secondContent));
if(comparisonRes == 0)
{
if(one.getForeground() == Color.BLACK && two.getForeground() == Color.WHITE)
{
comparisonRes = 1;
}
if(two.getForeground() == Color.BLACK && one.getForeground() == Color.WHITE)
{
comparisonRes = -1;
}
}
}
catch(NumberFormatException e)
{
comparisonRes = 0;
}
return comparisonRes;
}
}
Please tell me your ideas. Thank you.

It's much easier to sort data than to sort JPanels.
Here's mu GUI displaying your numbers.
So, lets create a Java object to hold the card data.
public class DataModel {
private final int number;
private final int colorNumber;
private final Color backgroundColor;
private final Color foregroundColor;
public DataModel(int number, int colorNumber, Color backgroundColor,
Color foregroundColor) {
this.number = number;
this.colorNumber = colorNumber;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
}
public int getNumber() {
return number;
}
public int getColorNumber() {
return colorNumber;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public Color getForegroundColor() {
return foregroundColor;
}
}
Pretty straightforward. We have fields to hold the information and getters to retrieve the information. We can make all the fields final since we're not changing anything once we set the values.
The sort class is pretty simple as well.
public class DataModelComparator implements Comparator<DataModel> {
#Override
public int compare(DataModel o1, DataModel o2) {
if (o1.getNumber() < o2.getNumber()) {
return -1;
} else if (o1.getNumber() > o2.getNumber()) {
return 1;
} else {
if (o1.getColorNumber() < o2.getColorNumber()) {
return -1;
} else if (o1.getColorNumber() > o2.getColorNumber()) {
return 1;
} else {
return 0;
}
}
}
}
Since we keep the color number, sorting by color is as easy as sorting a number.
Now that we've moved the data to it's own List, we can concentrate on creating the GUI.
package com.ggl.testing;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SortFrames implements Runnable {
private List<DataModel> dataModels;
private JPanel[] panels;
private JLabel[] labels;
private Random random = new Random();
public SortFrames() {
this.dataModels = new ArrayList<>();
this.random = new Random();
for (int i = 0; i < 4; i++) {
int number = random.nextInt(13);
int colorNumber = random.nextInt(2);
Color backgroundColor = Color.BLACK;
Color foregroundColor = Color.WHITE;
if (colorNumber == 1) {
backgroundColor = Color.WHITE;
foregroundColor = Color.BLACK;
}
dataModels.add(new DataModel(number, colorNumber, backgroundColor,
foregroundColor));
}
Collections.sort(dataModels, new DataModelComparator());
}
#Override
public void run() {
JFrame frame = new JFrame("Sort Frames");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
panels = new JPanel[dataModels.size()];
labels = new JLabel[dataModels.size()];
for (int i = 0; i < dataModels.size(); i++) {
DataModel dataModel = dataModels.get(i);
panels[i] = new JPanel();
panels[i].setBackground(dataModel.getBackgroundColor());
labels[i] = new JLabel(getDisplayText(dataModel));
labels[i].setBackground(dataModel.getBackgroundColor());
labels[i].setForeground(dataModel.getForegroundColor());
panels[i].add(labels[i]);
mainPanel.add(panels[i]);
}
frame.add(mainPanel);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
private String getDisplayText(DataModel dataModel) {
if (dataModel.getNumber() == 12) {
return "-";
} else {
return Integer.toString(dataModel.getNumber());
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new SortFrames());
}
public class DataModel {
private final int number;
private final int colorNumber;
private final Color backgroundColor;
private final Color foregroundColor;
public DataModel(int number, int colorNumber, Color backgroundColor,
Color foregroundColor) {
this.number = number;
this.colorNumber = colorNumber;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
}
public int getNumber() {
return number;
}
public int getColorNumber() {
return colorNumber;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public Color getForegroundColor() {
return foregroundColor;
}
}
public class DataModelComparator implements Comparator<DataModel> {
#Override
public int compare(DataModel o1, DataModel o2) {
if (o1.getNumber() < o2.getNumber()) {
return -1;
} else if (o1.getNumber() > o2.getNumber()) {
return 1;
} else {
if (o1.getColorNumber() < o2.getColorNumber()) {
return -1;
} else if (o1.getColorNumber() > o2.getColorNumber()) {
return 1;
} else {
return 0;
}
}
}
}
}
The lessons to be learned here are:
Separate the data from the view.
Focus on one part of the problem at a time. Divide and conquer.

Related

JButtons won't appear until I hover over them with the mouse

i have a problem with a grid i created. the buttons of this smallerGrid will not appear until i hover over them with the mouse.
each smallerGrid consists of a 3x3 grid which fits into a biggerGrid also 3x3 grid that conatins the smaller grids.
that is a brief explanation of how i am constructing the sudoku grid.
You will find the code below.
Thank you in advance.
import java.awt.*;
import javax.swing.*;
public class MyGridLayout {
private int filledFields = 0;
private JFrame mainFrame;
private JPanel panelForSolvingButton;
private JPanel smallerGridPanel;
private boolean solvingButtonAppeared = false;
SudokuCell[][] biggerGrid = new SudokuCell[10][10];
MyGridLayout() {
mainFrame = new JFrame("sudoku-solver");
mainFrame.setLayout(new BorderLayout());
smallerGridPanel = new JPanel(new GridLayout(3, 3));
mainFrame.add(smallerGridPanel, BorderLayout.CENTER);
panelForSolvingButton = new JPanel();
panelForSolvingButton.setLayout(new FlowLayout(FlowLayout.RIGHT));
mainFrame.add(panelForSolvingButton, BorderLayout.SOUTH);
mainFrame.pack();
mainFrame.setSize(600,600);
mainFrame.setLocationRelativeTo(null);
mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainFrame.setResizable(false);
addRegularButtons();
}
void addRegularButtons() {
int currentCol = 0;
int currentRow = 0;
int smallerGridFirstCol = 0;
int firstRowOfLayer = 0;
for (int layerOfGrids = 0; layerOfGrids < 3; ++layerOfGrids) {
for (int gridOfLayer = 0; gridOfLayer < 3; ++gridOfLayer) {
JComponent smallerGrid = new JPanel(new GridLayout(3, 3));
smallerGrid.setBorder(BorderFactory.createLineBorder(Color.BLACK));
smallerGridPanel.add(smallerGrid);
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
var cell = new SudokuCell(currentRow, currentCol);
smallerGrid.add(cell);
biggerGrid[currentRow][currentCol++] = cell;
cell.addActionListener(new CellActionListener(this));
}
currentRow++;
currentCol = smallerGridFirstCol;
}
smallerGridPanel.revalidate();
currentRow = firstRowOfLayer;
smallerGridFirstCol += 3;
currentCol = smallerGridFirstCol;
}
firstRowOfLayer += 3;
currentRow = firstRowOfLayer;
smallerGridFirstCol = currentCol = 0;
}
mainFrame.revalidate();
mainFrame.setVisible(true);
}
// checking if the solving process can begin
// (filled fields must at least reach 17)
void makeSolveButtonAppear() {
JButton solveButton = new JButton();
solveButton.setBackground(Color.white);
solveButton.setFont(new Font("Arial", Font.PLAIN, 20));
solveButton.setOpaque(false);
solveButton.setText("Solve ?");
panelForSolvingButton.add(solveButton);
}
public boolean isSolvingButtonAppeared() {
return solvingButtonAppeared;
}
public void setSolvingButtonAppeared(boolean solvingButtonAppeared) {
this.solvingButtonAppeared = solvingButtonAppeared;
}
public int getFilledFields() {
return filledFields;
}
public void setFilledFields(int filledFields) {
this.filledFields = filledFields;
}
public JPanel getPanelForSolvingButton() {
return panelForSolvingButton;
}
public static void main(String[] args) {
var gridLayout = new MyGridLayout();
}
}
package mainFrame;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.MatteBorder;
public class SudokuCell extends JButton {
private int x;
private int y;
public SudokuCell(int x, int y) {
this.x = x;
this.y = y;
setBackground(Color.white);
setOpaque(true);
setFont(new Font("Arial", Font.PLAIN, 20));
}
#Override
public int getX() {
return x;
}
#Override
public int getY() {
return y;
}
}
package mainFrame;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CellActionListener implements ActionListener {
private int clicked = 0;
MyGridLayout currentGrid;
CellActionListener(MyGridLayout currentGrid) {
this.currentGrid = currentGrid;
}
#Override
public void actionPerformed(ActionEvent e) {
if(clicked ==0) currentGrid.setFilledFields(currentGrid.getFilledFields()+1);
//clicked (number of clicks) must remain between 0 and 9
clicked = (clicked % 9) + 1;
((JButton) e.getSource()).setText(Integer.toString(clicked));
//first click on the button means an entry has been made
//which means one less needed filledField out of 17
if(!currentGrid.isSolvingButtonAppeared() && currentGrid.getFilledFields() >= 17) {
currentGrid.makeSolveButtonAppear();
currentGrid.setSolvingButtonAppeared(true);
}
}
}```

FIXED: Java Memory Game - Java Will not display other images

UPDATE: I just coded each button manually. Thanks Anyways.
I am trying to make a Memory Game written in Java. For some reason Java renders all the images as the same. It appears to render the latest image to all buttons when clicked.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.ArrayList;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
/**
* #author Steven
*
*/
public class Memory extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final int GRIDSIZE = 4;
private PicButton[][] liteBut = new PicButton[GRIDSIZE][GRIDSIZE];
private Random rand = new Random();
private ClassLoader cl = this.getClass().getClassLoader();
private String[] imagelist = {"images/image01.jpg", "images/image02.jpg", "images/image03.jpg", "images/image04.jpg"};
private ArrayList<String> images = new ArrayList<String>();
private volatile String icon = "";
public Memory() {
initGUI();
setTitle("Memory");
setResizable(false);
pack();
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void initGUI() {
assignimages();
TitleLabel framedTitle = new TitleLabel("Memory");
add(framedTitle, BorderLayout.PAGE_START);
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new GridLayout(GRIDSIZE, GRIDSIZE));
add(centerPanel, BorderLayout.CENTER);
for (int row = 0; row < GRIDSIZE; row++) {
for (int col = 0; col < GRIDSIZE; col++) {
liteBut[row][col] = new PicButton(row, col);
if (row == 0) {
if (col == 0) {
icon = images.get(0);
System.out.println(icon);
} else if (col == 1) {
icon = images.get(1);
System.out.println(icon);
} else if (col == 2) {
icon = images.get(2);
System.out.println(icon);
} else if (col == 3) {
icon = images.get(3);
System.out.println(icon);
}
} else if (row == 1) {
if (col == 0) {
icon = images.get(0);
System.out.println(icon);
} else if (col == 1) {
icon = images.get(1);
System.out.println(icon);
} else if (col == 2) {
icon = images.get(2);
System.out.println(icon);
} else if (col == 3) {
icon = images.get(3);
System.out.println(icon);
}
}
liteBut[row][col].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PicButton button = (PicButton) e.getSource();
int row = button.getRow();
int col = button.getCol();
String id = button.getID();
System.out.println("Hi from " + id);
liteBut[row][col].setIcon(new ImageIcon(cl.getResource(icon)));
}
});
centerPanel.add(liteBut[row][col]);
}
}
}
private void assignimages() {
for (int x = 0; x < 4; x++) {
int i = rand.nextInt(GRIDSIZE);
images.add(imagelist[i]);
}
for (int x = 0; x < images.size(); x++) {
System.out.println(images.get(x));
}
}
public static void main(String[] args) {
try {
String className = UIManager.getCrossPlatformLookAndFeelClassName();
UIManager.setLookAndFeel(className);
} catch (Exception e) {
}
EventQueue.invokeLater(new Runnable() {
public void run() {
new Memory();
}
});
}
public class PicButton extends JButton {
private static final long serialVersionUID = 1L;
private static final int MAXSIZE = 150;
private int row = 0;
private int col = 0;
private String id = "";
//private Boolean hasPic;
public PicButton(int row, int col) {
this.row = row;
this.col = col;
id = Integer.toString(row) + Integer.toString(col);
System.out.println(id);
setBackground(Color.BLACK);
Dimension size = new Dimension(MAXSIZE, MAXSIZE);
setPreferredSize(size);
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
public String getID() {
return id;
}
public void setImage() {
setBackground(Color.RED);
//hasPic = true;
}
public void clearImage() {
setBackground(Color.BLACK);
//hasPic = false;
}
}
public class TitleLabel extends JLabel {
private static final long serialVersionUID = 1L;
public TitleLabel(String title) {
Font titleFont = new Font(Font.SERIF, Font.BOLD, 32);
setFont(titleFont);
setHorizontalAlignment(JLabel.CENTER);
setText(title);
setBackground(Color.BLACK);
setForeground(Color.WHITE);
setOpaque(true);
}
}
}
liteBut[row][col].setIcon(new ImageIcon(cl.getResource(icon)));
is assigning the last known value of icon as the image to the button (this is an instance field, so it remembers), this is not the "value" assigned to the button, but the last value assigned to icon by the for-loop in which you create the buttons, so basically, ALL the buttons will get the last icon, instead, supply the icon value the PictureButton itself so you can "update" the button when it's clicked.
This functionality could be self contained to the button itself, further making it easier to manage

Swing blurred drag image

I have a simple task to implement, it works quite ok, but I am facing a very tricky issue regarding custom drag images in Swing.
The idea behind the task is just to allow the user to perform some DND between a list component and a text component, but during the drag operation to display following the mouse the exact same drawing as the renderer inside the list.
For this I use the cell renderer for the selected elements in the list and paint it over a temporary image. Then send this image to the TransferHandler and everything is fine. The problem is evident when I modify the size of the overall component and make it larger. After a certain extent, the picture that is draw no longer appears correct, but instead it has some gradient applied to it, making the content difficult to read. Following is a snippet of code that reproduces the issue:
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class BasicTextListDND {
private JList<String> makeList() {
DefaultListModel<String> m = new DefaultListModel<String>();
for(int i = 0; i<10; i++) {
m.addElement("Element "+i);
}
JList<String> list = new JList<String>(m);
list.setTransferHandler(new BasicListTransferHandler());
list.setDropMode(DropMode.ON_OR_INSERT);
list.setDragEnabled(true);
list.setCellRenderer(new DefaultListCellRenderer() {
/**
* Comment for <code>serialVersionUID</code>
*/
private static final long serialVersionUID = 1L;
/** {#inheritDoc} */
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
Component listCellRendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (cellHasFocus == false && isSelected == false) {
if (index % 2 == 0) {
listCellRendererComponent.setBackground(Color.RED);
} else if (index % 3==0) {
listCellRendererComponent.setBackground(Color.GREEN);
} else {
listCellRendererComponent.setBackground(Color.BLUE);
}
}
return listCellRendererComponent;
}
});
return list;
}
private JTextArea makeTextArea() {
JTextArea textArea = new JTextArea("Drag here from JList!");
return textArea;
}
public JComponent makeUI() {
JPanel panel = new JPanel(new GridLayout(2,1));
panel.add(new JScrollPane(makeTextArea()));
panel.add(new JScrollPane(makeList()));
return panel;
}
private static void createAndShowGUI() {
JFrame f = new JFrame("BasicDnD");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BasicTextListDND app = new BasicTextListDND();
JComponent appContent = app.makeUI();
f.setContentPane(appContent);
f.setSize(600, 320);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
/**
*
*/
public class BasicListTransferHandler extends TransferHandler {
/**
* Comment for <code>serialVersionUID</code>
*/
private static final long serialVersionUID = 1L;
#Override
public boolean canImport(TransferHandler.TransferSupport info) {
if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
return false;
}
JList.DropLocation dl = (JList.DropLocation)info.getDropLocation();
if (dl.getIndex() == -1) {
return false;
}
return true;
}
#Override
public int getSourceActions(JComponent c) {
BufferedImage dragImage = makeImageFromString(c);
if (dragImage != null) {
setDragImage(dragImage);
Point mousePosition = c.getMousePosition();
if (mousePosition != null) {
setDragImageOffset(mousePosition);
}
}
return COPY;
}
private final JPanel tempDrawPanel = new JPanel();
private BufferedImage createDragImage(JList<String> list) {
int width = 0;
int height = 0;
int[] selectedIndices = list.getSelectedIndices();
for(int i =0; i<selectedIndices.length; i++){
int idx = selectedIndices[i];
Rectangle cellBounds = list.getCellBounds(idx, idx);
height += cellBounds.height;
width = Math.max(width, cellBounds.width); // we want to create a drag image as big as the largest cell
}
BufferedImage br = null;
if (width > 0 && height > 0) {
br = list.getGraphicsConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
}
return br;
}
private BufferedImage makeImageFromString(JComponent src) {
JList<String> sourceList = (JList<String>)src;
BufferedImage br = createDragImage(sourceList);
if (br != null) {
int[] selectedIndices = sourceList.getSelectedIndices();
int yD = 0;
Graphics g = br.getGraphics();
try{
for(int idx: selectedIndices) {
ListCellRenderer<? super String> cellRenderer = sourceList.getCellRenderer();
String valueAt = sourceList.getModel().getElementAt(idx);
Component c = cellRenderer.getListCellRendererComponent(sourceList, valueAt, idx, false, false);
Rectangle itemCellBounds = sourceList.getCellBounds(idx, idx);
SwingUtilities.paintComponent(g, c, tempDrawPanel, itemCellBounds.x, yD, itemCellBounds.width, itemCellBounds.height);
yD = itemCellBounds.y+itemCellBounds.height;
}
}finally {
g.dispose();
}
br.coerceData(true);
}
return br;
}
#Override
protected Transferable createTransferable(JComponent c) {
JList<String> list = (JList<String>)c;
List<String> selectedValuesList = list.getSelectedValuesList();
StringBuffer buff = new StringBuffer();
for (int i = 0; i < selectedValuesList.size(); i++) {
String val = selectedValuesList.get(i);
buff.append(val == null ? "" : val.toString());
if (i != selectedValuesList.size()- 1) {
buff.append("\n");
}
}
return new StringSelection(buff.toString());
}
}
The problem lies, I think, somewhere in the makeImageFromString method, but after 2 days of digging through Swing/AWT libraries and understanding how the drag image is drawn, I still fail to fix this issue. The bottom-line question: is there any obscure logic in AWT that applies this gradient if the drag image is over a certain size?
Any help would be greatly appreciated!
Marius.
How about translates the origin of the graphics context:
//SwingUtilities.paintComponent(g, c, tempDrawPanel, itemCellBounds.x, yD, itemCellBounds.width, itemCellBounds.height);
//yD = itemCellBounds.y+itemCellBounds.height;
SwingUtilities.paintComponent(g, c, tempDrawPanel, 0, 0, itemCellBounds.width, itemCellBounds.height);
g.translate(0, itemCellBounds.height);
Edit:
#user3619696: I misunderstood.
I would guess that the "blurred drag image" opacity depend on the Windows desktop theme. So try using translucent JWindow instead of TransferHandler#setDragImage(...).
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.image.*;
import java.util.List;
import javax.swing.*;
public class BasicTextListDND2 {
private JList<String> makeList() {
DefaultListModel<String> m = new DefaultListModel<String>();
for(int i = 0; i<10; i++) {
m.addElement("Element "+i);
}
JList<String> list = new JList<String>(m);
list.setTransferHandler(new BasicListTransferHandler());
list.setDropMode(DropMode.ON_OR_INSERT);
list.setDragEnabled(true);
list.setCellRenderer(new DefaultListCellRenderer() {
/**
* Comment for <code>serialVersionUID</code>
*/
private static final long serialVersionUID = 1L;
/** {#inheritDoc} */
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
Component listCellRendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (cellHasFocus == false && isSelected == false) {
if (index % 2 == 0) {
listCellRendererComponent.setBackground(Color.RED);
} else if (index % 3==0) {
listCellRendererComponent.setBackground(Color.GREEN);
} else {
listCellRendererComponent.setBackground(Color.BLUE);
}
}
return listCellRendererComponent;
}
});
return list;
}
private JTextArea makeTextArea() {
JTextArea textArea = new JTextArea("Drag here from JList!");
return textArea;
}
public JComponent makeUI() {
JPanel panel = new JPanel(new GridLayout(2,1));
panel.add(new JScrollPane(makeTextArea()));
panel.add(new JScrollPane(makeList()));
return panel;
}
private static void createAndShowGUI() {
JFrame f = new JFrame("BasicDnD");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BasicTextListDND2 app = new BasicTextListDND2();
JComponent appContent = app.makeUI();
f.setContentPane(appContent);
f.setSize(600, 320);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
/**
*
*/
class BasicListTransferHandler extends TransferHandler {
/**
* Comment for <code>serialVersionUID</code>
*/
private static final long serialVersionUID = 1L;
private final JLabel label = new JLabel() {
#Override public boolean contains(int x, int y) {
return false;
}
};
private final JWindow window = new JWindow();
public BasicListTransferHandler() {
super();
window.add(label);
//window.setBackground(new Color(0, true));
window.setOpacity(.8f);
DragSource.getDefaultDragSource().addDragSourceMotionListener(new DragSourceMotionListener() {
#Override public void dragMouseMoved(DragSourceDragEvent dsde) {
Point pt = dsde.getLocation();
pt.translate(10, 10); // offset
if (!window.isVisible()) {
window.setVisible(true);
}
window.setLocation(pt);
}
});
}
#Override protected void exportDone(JComponent c, Transferable data, int action) {
super.exportDone(c, data, action);
window.setVisible(false);
}
#Override
public boolean canImport(TransferHandler.TransferSupport info) {
if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
return false;
}
JList.DropLocation dl = (JList.DropLocation)info.getDropLocation();
if (dl.getIndex() == -1) {
return false;
}
return true;
}
#Override
public int getSourceActions(JComponent c) {
BufferedImage dragImage = makeImageFromString(c);
if (dragImage != null) {
//setDragImage(dragImage);
//Point mousePosition = c.getMousePosition();
//if (mousePosition != null) {
// setDragImageOffset(mousePosition);
//}
label.setIcon(new ImageIcon(dragImage));
window.setLocation(-2000, -2000);
window.pack();
}
return COPY;
}
private final JPanel tempDrawPanel = new JPanel();
private BufferedImage createDragImage(JList<String> list) {
int width = 0;
int height = 0;
int[] selectedIndices = list.getSelectedIndices();
for(int i =0; i<selectedIndices.length; i++){
int idx = selectedIndices[i];
Rectangle cellBounds = list.getCellBounds(idx, idx);
height += cellBounds.height;
width = Math.max(width, cellBounds.width); // we want to create a drag image as big as the largest cell
}
BufferedImage br = null;
if (width > 0 && height > 0) {
br = list.getGraphicsConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
}
return br;
}
private BufferedImage makeImageFromString(JComponent src) {
JList<String> sourceList = (JList<String>)src;
BufferedImage br = createDragImage(sourceList);
if (br != null) {
int[] selectedIndices = sourceList.getSelectedIndices();
int yD = 0;
Graphics g = br.getGraphics();
try{
for(int idx: selectedIndices) {
ListCellRenderer<? super String> cellRenderer = sourceList.getCellRenderer();
String valueAt = sourceList.getModel().getElementAt(idx);
Component c = cellRenderer.getListCellRendererComponent(sourceList, valueAt, idx, false, false);
Rectangle itemCellBounds = sourceList.getCellBounds(idx, idx);
//SwingUtilities.paintComponent(g, c, tempDrawPanel, itemCellBounds.x, itemCellBounds.y + yD, itemCellBounds.width, itemCellBounds.height);
//yD = itemCellBounds.y+itemCellBounds.height;
SwingUtilities.paintComponent(g, c, tempDrawPanel, 0, 0, itemCellBounds.width, itemCellBounds.height);
g.translate(0, itemCellBounds.height);
}
}finally {
g.dispose();
}
br.coerceData(true);
}
return br;
}
#Override
protected Transferable createTransferable(JComponent c) {
JList<String> list = (JList<String>)c;
List<String> selectedValuesList = list.getSelectedValuesList();
StringBuffer buff = new StringBuffer();
for (int i = 0; i < selectedValuesList.size(); i++) {
String val = selectedValuesList.get(i);
buff.append(val == null ? "" : val.toString());
if (i != selectedValuesList.size()- 1) {
buff.append("\n");
}
}
return new StringSelection(buff.toString());
}
}

Hello I am creating a TicTacToe game for myself to understand Java better

however I am not sure where I am supposed to enter the whoWins() method. Do I enter this method in the actionperformed Method of the buttons or do i need to something different. Please help.
public class TTT extends JFrame implements ActionListener {
private JButton buttons[] = new JButton[9];
private JButton exitButton;
public JLabel title;
public JPanel titlePanel, panel;
private int count = 0;
int symbolCount = 0;
private boolean win = false;
public TTT() {
title = new JLabel("Welcome to my Tic Tac Toe Game!");
titlePanel = new JPanel();
title.setFont(new Font(Font.SERIF, 0, 30));
titlePanel.add(title);
this.add(titlePanel, BorderLayout.NORTH);
panel = new JPanel(new GridLayout(3, 3));
for (int i = 0; i < buttons.length; i++) {
buttons[i] = new JButton();
panel.add(buttons[i]);
buttons[i].setEnabled(true);
buttons[i].addActionListener(this);
}
this.add(panel, BorderLayout.CENTER);
JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.CENTER));
exitButton = new JButton("Quit");
panel1.add(exitButton);
this.add(panel1, BorderLayout.SOUTH);
exitButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(WIDTH);
}
});
}
public void whoWins() {
//Determines who wins using for the horizontal rows.
if (buttons[0].getText() == buttons[1].getText() && buttons[1].getText() == buttons[2].getText() && buttons[0].getText() != "") {
win = true;
} else if (buttons[3].getText() == buttons[4].getText() && buttons[4].getText() == buttons[5].getText() && buttons[3].getText() != "") {
win = true;
} else if (buttons[6].getText() == buttons[7].getText() && buttons[7].getText() == buttons[8].getText() && buttons[6].getText() != "") {
win = true;
} //Determines the verticles wins
else if (buttons[0].getText() == buttons[3].getText() && buttons[3].getText() == buttons[6].getText() && buttons[0].getText() != "") {
win = true;
} else if (buttons[1].getText() == buttons[4].getText() && buttons[4].getText() == buttons[7].getText() && buttons[1].getText() != "") {
win = true;
} else if (buttons[2].getText() == buttons[5].getText() && buttons[5].getText() == buttons[8].getText() && buttons[2].getText() != "") {
win = true;
}
// Diagnol Wins
else if (buttons[0].getText()==buttons[4].getText()&&buttons[4].getText()==buttons[8].getText()&& buttons[0].getText()!= "") {
win = true;
}else if (buttons[2].getText()==buttons[4].getText()&&buttons[4].getText()==buttons[6].getText()&& buttons[1].getText()!= "") {
win = true;
}else {
win = false;
}
//who won
if (win = true) {
JOptionPane.showMessageDialog(null, "wins");
}else if (count == 9 && win == false) {
JOptionPane.showMessageDialog(null, "Tie game");
}
}
public static void main(String[] args) {
TTT ref1 = new TTT();
ref1.setTitle("Tic Tac Toe");
ref1.setVisible(true);
ref1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ref1.setSize(500, 500);
ref1.setLocationRelativeTo(null);
// ref1.whoWins();
}
#Override
public void actionPerformed(ActionEvent e) {
count++;
for (JButton button : buttons) {
if (button == e.getSource()) {
if (symbolCount % 2 == 0) {
button.setText("X");
button.setEnabled(false);
} else {
button.setText("O");
button.setEnabled(false);
}
}
}
if (count >= buttons.length) {
JOptionPane.showMessageDialog(null, "End");
}
symbolCount++;
}
}
If you really want to do this right, then I suggest making some big changes, some M-V-C type changes:
First and foremost, separate out the logic of the game from the game GUI. This would mean that the code that determines who wins should not be in any code that contains GUI type code. This will be your "model"
Next you should never have GUI code implement listener interfaces, so try to get that out of the GUI and possibly have it go into its own class, the "Control" class.
Finally the GUI or "View" class will concern itself with displaying the model's state and getting input from the user and transmitting this input to the control.
For example,...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class TicTacToeMain {
private static void createAndShowGui() {
TttView view = null;
try {
view = new TttView();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
TttModel model = new TttModel();
new TttControl(model, view);
JFrame frame = new JFrame("Tic Tac Toe");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(view.getMainPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum TttPiece {
EMPTY, X, O
}
class TttView {
public static final String IMAGE = "/imgFolder/TicTacToe.png";
private static final int GAP = 5;
private JPanel mainPanel = new JPanel();
private JPanel tttPanel = new JPanel();
private Map<TttPiece, Icon> iconMap = new EnumMap<>(TttPiece.class);
private JLabel[][] grid = new JLabel[TttModel.ROWS][TttModel.COLS];
private TttControl control;
public TttView() throws IOException {
BufferedImage img = ImageIO.read(getClass().getResourceAsStream(IMAGE));
Icon[] imgIcons = splitImg(img);
iconMap.put(TttPiece.X, imgIcons[0]);
iconMap.put(TttPiece.O, imgIcons[1]);
iconMap.put(TttPiece.EMPTY, createEmptyIcon(imgIcons[0]));
tttPanel.setLayout(new GridLayout(grid.length, grid[0].length, GAP, GAP));
tttPanel.setBackground(Color.black);
MyMouseAdapter mouseAdapter = new MyMouseAdapter();
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
grid[row][col] = new JLabel(iconMap.get(TttPiece.EMPTY));
grid[row][col].setOpaque(true);
grid[row][col].setBackground(Color.LIGHT_GRAY);
grid[row][col].addMouseListener(mouseAdapter);
tttPanel.add(grid[row][col]);
}
}
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0));
btnPanel.add(new JButton(new ClearAction("Clear", KeyEvent.VK_C)));
btnPanel.add(new JButton(new ExitAction("Exit", KeyEvent.VK_X)));
int blGap = 2;
mainPanel.setLayout(new BorderLayout(blGap, blGap));
mainPanel.setBorder(BorderFactory.createEmptyBorder(blGap, blGap, blGap,
blGap));
mainPanel.add(tttPanel, BorderLayout.CENTER);
mainPanel.add(btnPanel, BorderLayout.SOUTH);
}
public void setControl(TttControl control) {
this.control = control;
}
public JComponent getMainPanel() {
return mainPanel;
}
private Icon createEmptyIcon(Icon icon) {
int width = icon.getIconWidth();
int height = icon.getIconHeight();
BufferedImage img = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
return new ImageIcon(img);
}
private Icon[] splitImg(BufferedImage img) {
int w = img.getWidth();
int h = img.getHeight();
int gap = 5;
Icon[] icons = new ImageIcon[2];
icons[0] = new ImageIcon(img.getSubimage(0, 0, w / 2 - gap, h / 2 - gap));
icons[1] = new ImageIcon(img.getSubimage(w / 2 + gap, 0, w / 2 - gap, h
/ 2 - gap));
return icons;
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (control == null) {
return;
}
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
if (grid[row][col] == e.getSource()) {
control.gridPress(row, col);
}
}
}
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent evt) {
if (control != null) {
control.clear();
}
}
}
private class ExitAction extends AbstractAction {
public ExitAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent evt) {
if (control != null) {
control.exit(evt);
}
}
}
public void setGridIcon(int row, int col, TttPiece tttPiece) {
grid[row][col].setIcon(iconMap.get(tttPiece));
}
}
class TttControl {
private TttModel model;
private TttView view;
public TttControl(TttModel model, TttView view) {
this.model = model;
this.view = view;
view.setControl(this);
model.addPropertyChangeListener(new ModelListener());
}
public void exit(ActionEvent evt) {
Window win = SwingUtilities
.getWindowAncestor((Component) evt.getSource());
win.dispose();
}
public void gridPress(int row, int col) {
try {
model.gridPress(row, col);
} catch (TttException e) {
// TODO: notify user
// e.printStackTrace();
}
}
public void clear() {
model.clear();
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (TttModel.GRID_POSITION.equals(evt.getPropertyName())) {
TttPiece[][] tttGrid = model.getTttGrid();
for (int row = 0; row < tttGrid.length; row++) {
for (int col = 0; col < tttGrid[row].length; col++) {
view.setGridIcon(row, col, tttGrid[row][col]);
}
}
}
}
}
}
class TttModel {
public static final int ROWS = 3;
public static final int COLS = ROWS;
public static final String GRID_POSITION = "grid position";
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(
this);
private TttPiece[][] tttGrid = new TttPiece[ROWS][COLS];
private TttPiece player = TttPiece.X;
private boolean gameOver;
public TttModel() {
clear();
}
public void setGridPosition(int row, int col, TttPiece piece)
throws TttException {
if (gameOver) {
return;
}
if (tttGrid[row][col] == TttPiece.EMPTY) {
tttGrid[row][col] = piece;
checkForWin(row, col, piece);
nextPlayer();
pcSupport.firePropertyChange(GRID_POSITION, null, tttGrid);
} else {
String message = "Invalid setGridPosition for row: %d, col: %d, piece: %s. "
+ "Spot already occupied by piece: %s";
message = String.format(message, row, col, piece, tttGrid[row][col]);
throw new TttException(message);
}
}
public TttPiece[][] getTttGrid() {
return tttGrid;
}
public void gridPress(int row, int col) throws TttException {
setGridPosition(row, col, player);
}
public void nextPlayer() {
player = player == TttPiece.X ? TttPiece.O : TttPiece.X;
}
private void checkForWin(int row, int col, TttPiece piece) {
// TODO finish
}
public void clear() {
for (int row = 0; row < tttGrid.length; row++) {
for (int col = 0; col < tttGrid[row].length; col++) {
tttGrid[row][col] = TttPiece.EMPTY;
}
}
player = TttPiece.X;
pcSupport.firePropertyChange(GRID_POSITION, null, tttGrid);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
}
#SuppressWarnings("serial")
class TttException extends Exception {
public TttException() {
super();
}
public TttException(String message) {
super(message);
}
}
Using for my images:
With GUI looking like:
I am also interested in writing a Tic Tac Toe game, so I copied your code, and did a little modification, and it passed test, check following:
package eric.j2se.swing;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
/**
* <p>
* Simple game of Tic Tac Toe.
* </p>
*
* #author eric
* #date Apr 16, 2014 11:03:48 AM
*/
#SuppressWarnings("serial")
public class TicTacToe extends JFrame implements ActionListener {
// 2 players
public static final char playerX = 'X';
public static final char playerO = 'O';
// null player
public static final char playerN = 'N';
// the winer, init to null player
private Character winner = playerN;
// indicate whether game over
private boolean gameOver = false;
// count of button used,
private int count = 0;
private Character buttonPlayers[] = new Character[9];
private JButton buttons[] = new JButton[9];
private JButton exitButton;
public JLabel title;
public JPanel titlePanel, panel;
public TicTacToe() {
// init buttonPlayers
for (int i = 0; i < 9; i++) {
buttonPlayers[i] = playerN;
}
// init title
title = new JLabel("Welcome to Tic Tac Toe!");
titlePanel = new JPanel();
title.setFont(new Font(Font.SERIF, 0, 30));
titlePanel.add(title);
this.add(titlePanel, BorderLayout.NORTH);
// init 9 button
panel = new JPanel(new GridLayout(3, 3));
for (int i = 0; i < buttons.length; i++) {
buttons[i] = new JButton();
panel.add(buttons[i]);
buttons[i].setEnabled(true);
buttons[i].addActionListener(this);
}
// init exit button
this.add(panel, BorderLayout.CENTER);
JPanel panel1 = new JPanel(new FlowLayout(FlowLayout.CENTER));
exitButton = new JButton("Quit");
panel1.add(exitButton);
this.add(panel1, BorderLayout.SOUTH);
exitButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(WIDTH);
}
});
}
public void whoWins() {
// determine winner - horizontal rows
if (!gameOver) {
for (int i = 0; i < 3; i++) {
if ((buttonPlayers[0 + i * 3] != playerN) && (buttonPlayers[0 + i * 3].equals(buttonPlayers[1 + i * 3]))
&& buttonPlayers[1 + i * 3].equals(buttonPlayers[2 + i * 3])) {
winner = buttonPlayers[0 + i * 3];
gameOver = true;
break;
}
}
}
// determine winner - vertical rows
if (!gameOver) {
for (int i = 0; i < 3; i++) {
if ((buttonPlayers[i + 0 * 3] != playerN) && (buttonPlayers[i + 0 * 3].equals(buttonPlayers[i + 1 * 3]))
&& buttonPlayers[i + 1 * 3].equals(buttonPlayers[i + 2 * 3])) {
winner = buttonPlayers[i + 0 * 3];
gameOver = true;
break;
}
}
}
// determine winner - diagonal rows
if (!gameOver) {
int winButtonIndex = -1;
if ((buttonPlayers[0] != playerN) && (buttonPlayers[0].equals(buttonPlayers[4])) && buttonPlayers[4].equals(buttonPlayers[8])) {
winButtonIndex = 0;
} else if ((buttonPlayers[2] != playerN) && (buttonPlayers[2].equals(buttonPlayers[4])) && buttonPlayers[4].equals(buttonPlayers[6])) {
winButtonIndex = 2;
}
if (winButtonIndex >= 0) {
winner = buttonPlayers[winButtonIndex];
gameOver = true;
}
}
// full
if (count == 9) {
gameOver = true;
}
if (gameOver) {
String tip = "";
switch (winner) {
case playerO:
tip = "Player O win!";
break;
case playerX:
tip = "Player X win!";
break;
default:
tip = "Draw game!";
break;
}
JOptionPane.showMessageDialog(null, tip);
}
}
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < buttons.length; i++) {
JButton button = buttons[i];
if (button == e.getSource()) {
Character currentPlayer = (count % 2 == 1 ? playerX : playerO);
button.setText(String.valueOf(currentPlayer));
buttonPlayers[i] = currentPlayer;
button.setEnabled(false);
break;
}
}
count++;
whoWins();
}
public static void main(String[] args) {
TicTacToe ref1 = new TicTacToe();
ref1.setTitle("Tic Tac Toe");
ref1.setVisible(true);
ref1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ref1.setSize(500, 500);
ref1.setLocationRelativeTo(null);
}
}
about when to call the win check:
check each time you click 1 of the 9 buttons,
about the flag:
I use 2 flag instead of 1 flag to indicate game over & winner, because in TTT game, draw game is very usual, after play several times, you always get draw game ...
a little suggestion to your code:
when compare string, use equals(), not ==,
define const values in variable, not write it in logic, e.g. 'O' 'X',
don't repeat code, try use logic control to make it short & easy to read & easy to maintain,

Trying to learn the ins and outs of javax.swing

So I've been trying to get the handle of javax.swing and am having some trouble. I am attempting to implement the '8 puzzle', where there are 8 tiles and one open spot set up in a 3x3 grid, when a tile adjacent to the open spot is clicked it trades spaces with the open spot. My structure consists of a JFrame, which contains a JPanel, and the JPanel contains the 9 tiles as JComponents, however only the JPanel renders, and the tiles are nowhere to be found. Any help with this problem would be greatly appreciated.
import javax.swing.*;
public class MainFrame{
public static void main(String[] args){
JFrame frame = new JFrame("8 Puzzle");
frame.setVisible(true);
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PieceManager pm = new PieceManager();
frame.add(pm);
}
}
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import javax.swing.*;
Second class
public class PieceManager extends JPanel{
int[] possmoves;
GameTile[] pieces;
int openSpot;
public PieceManager(){
this.setSize(600,600);
this.setBackground(new Color(255,255,255));
this.setLayout(new GridLayout(3,3));
pieces = new GameTile[9];
this.init();
this.addMouseListener(new ClickAction());
}
public void init(){
ArrayList<Integer> nums = new ArrayList<Integer>();
Random rand = new Random();
for(int i=0;i<9;i++){
nums.add(i);
}
for(int i=0,j=8;i<8;i++,j--){
int p = rand.nextInt(j);
GameTile x = new GameTile(i,nums.remove(p));
pieces[i]=x;
nums.removeAll(Collections.singleton(null));
}
GameTile z = new GameTile(8,nums.get(0));
pieces[8]=z;
possmoves = new int[4];
boolean found = false;
for(int i=0;i<9||found;i++){
if(pieces[i].getID()==0){
openSpot = pieces[i].getPos();
}
}
setOpenSpot();
paint();
}
public void paint(){
this.removeAll();
for(int i=0;i<9;i++){
this.add(pieces[i]);
pieces[i].setVisible(true);
}
}
public void setOpenSpot(){
Arrays.fill(possmoves,-1);
if(openSpot==0){
possmoves[0]=1;
possmoves[1]=3;
} else if(openSpot==1){
possmoves[0]=0;
possmoves[1]=2;
possmoves[3]=4;
} else if(openSpot==2){
possmoves[0]=1;
possmoves[1]=5;
} else if(openSpot==3){
possmoves[0]=0;
possmoves[1]=4;
possmoves[2]=6;
} else if(openSpot==4){
possmoves[0]=1;
possmoves[1]=3;
possmoves[2]=5;
possmoves[3]=7;
} else if(openSpot==5){
possmoves[0]=2;
possmoves[1]=4;
possmoves[3]=8;
} else if(openSpot==6){
possmoves[0]=3;
possmoves[1]=7;
} else if(openSpot==7){
possmoves[0]=6;
possmoves[1]=4;
possmoves[2]=8;
} else if(openSpot==8){
possmoves[0]=5;
possmoves[1]=7;
}
}
public void checkCorrect(){
}
public class ClickAction implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int pX=(int)Math.floor(x/200);
int pY=(int)Math.floor(y/200);
int piecepressed=(pY*3)+pX;
boolean moveable = false;
int toBeMoved = -1;
for(int i=0;i<4;i++){
if(piecepressed==possmoves[i]){
moveable=true;
toBeMoved=possmoves[i];
}
}
if(moveable){
GameTile saved=pieces[openSpot];
pieces[openSpot]=pieces[toBeMoved];
pieces[toBeMoved]=saved;
openSpot=toBeMoved;
setOpenSpot();
paint();
checkCorrect();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
}
Tile Class (3rd and final)
import java.awt.*;
import javax.swing.JComponent;
public class GameTile extends JComponent{
private int id;
private int position;
public GameTile(int id, int initpos){
if(id==0){
this.id=id;
this.position=initpos;
} else{
this.id=id;
this.position = initpos;
String label = Integer.toString(id);
setSize(200,200);
setBackground(new Color(0,0,0));
Label l = new Label(label,Label.CENTER);
this.add(l);
l.setVisible(true);
}
}
public void setPos(int position){
this.position=position;
}
public int getPos(){
return position;
}
public int getID(){
return id;
}
}
Use of a JComponent is fine, but you'll probably want to set it to be opaque.
Also
Don't mix AWT (i.e., Label) with Swing components.
Don't setSize(...). Instead deal with preferredSize. Best to override getPreferredSize() and return an appropriate Dimension. I've used setPreferredSize(...) in a pinch, but at the risk of the wrath of kleopatra.
Don't override a JComponent or any of its children's (JPanel included) paint method without good reason (you don't have one). Instead override paintComponent. Edit: I see that your paint method is not a true override, so this is OK -- sorry for the misunderstanding on my part.
You almost never call paint or paintComponent directly. Instead you call repaint() and let the JVM call the painting methods for you. Edit: ditto for this as this was my misunderstanding of your code. You will want to call 'revalidate()andrepaint()` after removing and replacing components in your JPanel.
You can see that your JComponents are present and accounted for if you give them borders.
Edit 2: You will need to give your JComponents a layout if you want to have components added to them easily placed and visualized. In this example I gave mine a BorderLayout.
I've combined a bunch of classes into one file for ease with compilation.
I've indicated key changes with // !! comments.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import javax.swing.*;
public class MainFrame {
public static void main(String[] args) {
JFrame frame = new JFrame("8 Puzzle");
frame.setVisible(true);
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PieceManager pm = new PieceManager();
frame.add(pm);
}
}
class PieceManager extends JPanel {
int[] possmoves;
GameTile[] pieces;
int openSpot;
public PieceManager() {
this.setSize(600, 600);
this.setBackground(new Color(255, 255, 255));
this.setLayout(new GridLayout(3, 3));
pieces = new GameTile[9];
this.init();
this.addMouseListener(new ClickAction());
}
public void init() {
ArrayList<Integer> nums = new ArrayList<Integer>();
Random rand = new Random();
for (int i = 0; i < 9; i++) {
nums.add(i);
}
for (int i = 0, j = 8; i < 8; i++, j--) {
int p = rand.nextInt(j);
GameTile x = new GameTile(i, nums.remove(p));
pieces[i] = x;
nums.removeAll(Collections.singleton(null));
}
GameTile z = new GameTile(8, nums.get(0));
pieces[8] = z;
possmoves = new int[4];
boolean found = false;
for (int i = 0; i < 9 || found; i++) {
if (pieces[i].getID() == 0) {
openSpot = pieces[i].getPos();
}
}
setOpenSpot();
paint();
}
public void paint() {
this.removeAll();
for (int i = 0; i < 9; i++) {
this.add(pieces[i]);
pieces[i].setVisible(true);
}
revalidate(); // !!
repaint(); // !!
}
public void setOpenSpot() {
Arrays.fill(possmoves, -1);
if (openSpot == 0) {
possmoves[0] = 1;
possmoves[1] = 3;
} else if (openSpot == 1) {
possmoves[0] = 0;
possmoves[1] = 2;
possmoves[3] = 4;
} else if (openSpot == 2) {
possmoves[0] = 1;
possmoves[1] = 5;
} else if (openSpot == 3) {
possmoves[0] = 0;
possmoves[1] = 4;
possmoves[2] = 6;
} else if (openSpot == 4) {
possmoves[0] = 1;
possmoves[1] = 3;
possmoves[2] = 5;
possmoves[3] = 7;
} else if (openSpot == 5) {
possmoves[0] = 2;
possmoves[1] = 4;
possmoves[3] = 8;
} else if (openSpot == 6) {
possmoves[0] = 3;
possmoves[1] = 7;
} else if (openSpot == 7) {
possmoves[0] = 6;
possmoves[1] = 4;
possmoves[2] = 8;
} else if (openSpot == 8) {
possmoves[0] = 5;
possmoves[1] = 7;
}
}
public void checkCorrect() {
}
public class ClickAction implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
int pX = (int) Math.floor(x / 200);
int pY = (int) Math.floor(y / 200);
int piecepressed = (pY * 3) + pX;
boolean moveable = false;
int toBeMoved = -1;
for (int i = 0; i < 4; i++) {
if (piecepressed == possmoves[i]) {
moveable = true;
toBeMoved = possmoves[i];
}
}
if (moveable) {
GameTile saved = pieces[openSpot];
pieces[openSpot] = pieces[toBeMoved];
pieces[toBeMoved] = saved;
openSpot = toBeMoved;
setOpenSpot();
paint();
checkCorrect();
}
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
}
class GameTile extends JComponent {
private int id;
private int position;
public GameTile(int id, int initpos) {
setBorder(BorderFactory.createTitledBorder("" + id)); // !!
setLayout(new BorderLayout()); // !! so the added JLabel will show
if (id == 0) {
this.id = id;
this.position = initpos;
} else {
this.id = id;
this.position = initpos;
String label = Integer.toString(id);
// !! setSize(200, 200);
setOpaque(true); // !!
setPreferredSize(new Dimension(200, 200)); // !!
setBackground(new Color(0, 0, 0));
// !! Label l = new Label(label, Label.CENTER);
JLabel l = new JLabel(label, SwingConstants.CENTER); // !!
this.add(l);
l.setVisible(true);
}
}
public void setPos(int position) {
this.position = position;
}
public int getPos() {
return position;
}
public int getID() {
return id;
}
}
There's probably a lot more to say, but this is all I've seen so far.

Categories