I need help on drawing numbers on top of a rectangle matrix, and have them move together with the rectangle they are on. I tried doing this

public void paintComponent(Graphics g) {
	super.paintComponent(g);
	Graphics2D g2 = (Graphics2D) g;  
	for(int row = 0; row < tiles.length; row++) {
		for(int col = 0; col < tiles[row].length; col++) {
			
			
			g2.setColor(Color.YELLOW);
			g2.fill(tiles[row][col]);
			dx=board[row][col].getX();
			dy = board[row][col].getY();
	        int xdraw = (int)dx;
	        int ydraw = (int)dy;
	        g2.setColor(Color.RED);
			g2.drawString("1", xdraw+30, ydraw+30);
			}			
		}	
	}

but didnt have any succes.

here is the complete code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class GUI extends JPanel implements ActionListener, KeyListener {

	private static final long serialVersionUID = 1L;
	
	// ===========================================================================================x instance Variables

	Timer t = new Timer(0, this);

	private static int ROWS;
	private static int COLS;

	// These variables are meant to check if a movement is legal or not.
	// The isDone determine when the user can move a square again
	
	public boolean rlegal, llegal, ulegal, dlegal, isDone = true;

	public int x = 1, c1 = 0, r1 = 0;
	public Rectangle[][] board;
	private int[][]Xcoordinates;
	private int[][]Ycoordinates;
	private static final int CELL_SIZE = 84; // Pixels
	private double dx=0,dy=0;

	// ===========================================================================================x Constructor Class
	/**
	 * Constructor Class for the GUI class.
	 * 
	 * @param rows
	 * @param columns
	 * */
	public GUI(int number) {
		//Sets the amount of ROWS and COLUMNS and creates an array depending on the last 2.

		ROWS = number;
		COLS = number;
		board = new Rectangle[ROWS][COLS];
		Xcoordinates = new int [ROWS][COLS];
		Ycoordinates = new int [ROWS][COLS];

		t.start();
		addKeyListener(this);
		setFocusable(true);
		setFocusTraversalKeysEnabled(false);

		// This 2 for loops store a rectangle in each element of the 2D array.

		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				board[r][c] = new Rectangle(r * CELL_SIZE + 2, c * CELL_SIZE
						+ 2, CELL_SIZE - 4, CELL_SIZE - 4);

				if (r == ROWS - 1 && c == COLS - 1) {
					board[r][c] = new Rectangle();
				}
			}
		}
		
		      
		      
		        
		      
			
	}

	// ===========================================================================================x method paintComponent

	/**
	 * Paint Component used to update any changes in the board.
	 * */
	public void paintComponent(Graphics g) {

		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;

		// This paints square by square within the array.
		
		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				
				g2.setColor(Color.GRAY);
				g2.fill(board[r][c]);	
			}
		}
		
	}// end paintComponent

	// ===========================================================================================x KeyEvent Methods
	
	/**
	 * This method reacts depending on which key the user has pressed.
	 * */
	@Override
	public void keyPressed(KeyEvent arg0) {
		// TODO Auto-generated method stub

		int code = arg0.getKeyCode();

		// The isDone boolean variable checks to see if the square is done with its translation.
		
		if (isDone) {
			if (code == KeyEvent.VK_UP) {
				Ulegal();
			}

			else if (code == KeyEvent.VK_DOWN) {
				Dlegal();
			}

			else if (code == KeyEvent.VK_RIGHT) {
				Rlegal();
			}

			else if (code == KeyEvent.VK_LEFT) {
				Llegal();
			}
		}
	} // end keyPressed

	// ===========================================================================================x KeyReleased Method

	/**
	 * NOT NEEDED
	 * */
	
	@Override
	public void keyReleased(KeyEvent arg0) {
		// TODO Auto-generated method stub

	}

	// ===========================================================================================x KeyTyped Method

	/**
	 * NOT NEEDED
	 * */
	
	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub

	}

	// ===========================================================================================x Action Performed Method

	/**
	 * This method is to keep updating the board after every movement.
	 * */
	@Override
	public void actionPerformed(ActionEvent arg0) {
		// TODO Auto-generated method stub

		// This are meant to call the movement methods and repaint the square as
		// it moves.

		if (rlegal) {
			right();
		}

		if (dlegal) {
			down();
		}

		if (llegal) {
			left();
		}

		if (ulegal) {
			up();
		}

		this.repaint();
	}

	// ===========================================================================================x Legal Methods

	/**
	 * All of the methods below this point are meant to check if the empty space
	 * is near the square that the user wishes to move.
	 * */
	public void Rlegal() {
		rlegal = false;

		for (int r = 1; r < ROWS; r++) {
			for (int c = 0; c < COLS; c++) {
				if (board[r][c].isEmpty()) {
					r1 = r - 1;
					c1 = c;
					rlegal = true;
					isDone = false;
				}
			}
		}
	}

	public void Llegal() {
		llegal = false;

		for (int r = 0; r < ROWS - 1; r++) {
			for (int c = 0; c < COLS; c++) {
				if (board[r][c].isEmpty()) {
					r1 = r + 1;
					c1 = c;
					llegal = true;
					isDone = false;
				}
			}
		}
	}

	public void Ulegal() {
		ulegal = false;

		for (int r = 0; r < ROWS; r++) {
			for (int c = 0; c < COLS - 1; c++) {
				if (board[r][c].isEmpty()) {
					r1 = r;
					c1 = c + 1;
					ulegal = true;
					isDone = false;
				}
			}
		}
	}

	public void Dlegal() {
		dlegal = false;

		for (int r = 0; r < ROWS; r++) {
			for (int c = 1; c < COLS; c++) {
				if (board[r][c].isEmpty()) {
					r1 = r;
					c1 = c - 1;
					dlegal = true;
					isDone = false;
				}
			}
		}
	}

	// ===========================================================================================x Movement Methods

	/**
	 * This methods are called from the actionPerfomed method and they are meant
	 * to move move the desired square.
	 * */

	public void up() {
		if (x <= CELL_SIZE) {
			board[r1][c1].setLocation(board[r1][c1].x, board[r1][c1].y - 1);
		}

		if (x == CELL_SIZE + 1) {
			board[r1][c1 - 1] = board[r1][c1];
			board[r1][c1] = new Rectangle();
			ulegal = false;
			x = 0;
			isDone = true;
		}
		x++;
	}

	public void down() {
		if (x <= CELL_SIZE) {
			board[r1][c1].setLocation(board[r1][c1].x, board[r1][c1].y + 1);
		}

		if (x == CELL_SIZE + 1) {
			board[r1][c1 + 1] = board[r1][c1];
			board[r1][c1] = new Rectangle();
			dlegal = false;
			x = 0;
			isDone = true;
		}
		x++;
	}

	public void left() {
		if (x <= CELL_SIZE) {
			board[r1][c1].setLocation(board[r1][c1].x - 1, board[r1][c1].y);
		}

		if (x == CELL_SIZE + 1) {
			board[r1 - 1][c1] = board[r1][c1];
			board[r1][c1] = new Rectangle();
			llegal = false;
			x = 0;
			isDone = true;
		}
		x++;
	}

	public void right() {

		if (x <= CELL_SIZE) {
			board[r1][c1].setLocation(board[r1][c1].x + 1, board[r1][c1].y);
		}

		if (x == CELL_SIZE + 1) {
			board[r1 + 1][c1] = board[r1][c1];
			board[r1][c1] = new Rectangle();
			rlegal = false;
			x = 0;
			isDone = true;
		}
		x++;
	}
}

Dani AI

Generated

A few focused points that address the problems in the posts by (and the cross-post noted by ).

Your first snippet references tiles[][] while the rest of the class uses board[][]. That mismatch will prevent drawing where you expect. Also remember Graphics.drawString(...) uses the text baseline for the y coordinate (not the top), so a simple rect.x+30, rect.y+30 can misplace the text. Finally, draw the string after filling the rectangle so the text renders on top.

A compact, reliable approach: inside paintComponent iterate the same board array, skip empty rectangles, then use FontMetrics to center the label inside each rectangle and draw it. Example pattern (does not duplicate your posted code):

for (int r = 0; r < rows; r++) {
  for (int c = 0; c < cols; c++) {
    Rectangle cell = board[r][c];
    if (cell.isEmpty()) continue;
    g2.setColor(Color.GRAY);
    g2.fill(cell);
    String label = Integer.toString(r * cols + c + 1);
    FontMetrics fm = g2.getFontMetrics();
    int tx = cell.x + (cell.width - fm.stringWidth(label)) / 2;
    int ty = cell.y + (cell.height - fm.getHeight()) / 2 + fm.getAscent();
    g2.setColor(Color.RED);
    g2.drawString(label, tx, ty);
  }
}

Extra practical notes

  • Ensure the same array (board) is used for both shapes and labels; avoid a separate uninitialized tiles variable.
  • If a tile can be "empty", either use null or test cell.isEmpty() before drawing the label.
  • Call repaint() after you update positions (your Timer can do that). Use a reasonable Timer delay (e.g., 16–40 ms) instead of 0.
  • For reliable key handling prefer Swing Key Bindings and ensure the panel has focus (or call requestFocusInWindow() after display).
  • If labels still misalign, print cell.x, cell.y, cell.width, cell.height to debug coordinates, and remember you might have swapped row/column when creating rectangles (x should normally be col * size, y row * size).

These changes will make the text follow the moving rectangles and produce consistent, centered labels.

Cross posted at

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.