I'm making custom components for my game and I tried everything I found in Stack Overflow and no matter what I still can't place text in the center of a rectangle. I even read all the documentation of working with java text API.
Can anyone explain to me how to align text to the center (Center of rect or frame or anything) in java swing once and for all? This is not a duplicate question because in all the other questions on Stack Overflow I did not get a solution that works.
So far I have used FontMetrics and I measured the width using stringWidth() method and the height using ascent (None of them are accurate).
package com.isi.uicomponents;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import com.isi.core.Game;
import com.isi.states.GameState;
public class Button extends UIComponent {
private Font font;
private String text;
public Button(Game game, GameState state, int x, int y, int width, int height, String text) {
super(game, state, x, y, width, height);
font = new Font("Arial", Font.BOLD, 20);
this.text = text;
}
public Button(Game game, GameState state, int x, int y, int width, int height) {
super(game, state, x, y, width, height);
text = null;
}
public String getText() {
return text;
}
@Override
public void tick() {
}
@Override
public void draw(Graphics2D g) {
g.setColor(fillColor);
g.fillRect(x, y, width, height);
g.setColor(boundsColor);
g.draw(bounds);
if (text != null) {
FontMetrics fm = g.getFontMetrics();
int textX = x + (width / 2) - (fm.stringWidth(text) / 2);
int textY = y + ((height - fm.getHeight()) / 2) + fm.getAscent();
g.setFont(font);
g.setColor(Color.white);
g.drawString(text, textX, textY);
}
}
}
// =============================================
package com.isi.uicomponents;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import com.isi.core.Game;
import com.isi.states.GameState;
public abstract class UIComponent {
public final static Color DEFAULT_BOUNDS_COLOR = Color.white;
public final static Color DEFAULT_FILL_COLOR = Color.gray;
protected Game game;
protected GameState state;
protected int x;
protected int y;
protected int width;
protected int height;
protected Rectangle bounds;
protected Color boundsColor;
protected Color fillColor;
public UIComponent(Game game, GameState state, int x, int y, int width, int height) {
this.game = game;
this.state = state;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
bounds = new Rectangle(x, y, width, height);
boundsColor = DEFAULT_BOUNDS_COLOR;
fillColor = DEFAULT_FILL_COLOR;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Rectangle getBounds() {
return bounds;
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Color getBoundsColor() {
return boundsColor;
}
public void setBoundsColor(Color boundsColor) {
this.boundsColor = boundsColor;
}
public Color getFillColor() {
return fillColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
}
public abstract void tick();
public abstract void draw(Graphics2D g);
}
// =============================================
package com.isi.states;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import com.isi.core.Game;
import com.isi.tools.ImageLoader;
import com.isi.uicomponents.Button;
import com.isi.uicomponents.UIComponent;
public class MainMenuState extends GameState {
private static BufferedImage bg = ImageLoader.load("Main Menu Background.jpg");
// Background coordinates for animation
private int x;
private int y;
// MainMenu components array
private ArrayList<UIComponent> components;
public MainMenuState(Game game) {
super(game);
x = 0;
y = 0;
components = new ArrayList<UIComponent>();
components.add(new Button(game, this, game.getWidth() / 2 - 80 / 2, game.getHeight() / 2 - 50 / 2, 80, 50, "Play"));
}
public ArrayList<UIComponent> getComponents() {
return components;
}
public void tick() {
y = y >= game.getHeight() ? 0 : y + 2;
}
public void draw(Graphics2D g) {
g.drawImage(bg, 0, -game.getHeight() + y, game.getWidth(), game.getHeight(), null);
g.drawImage(bg, x, y, game.getWidth(), game.getHeight(), null);
for (int i = 0; i < components.size(); i++) {
components.get(i).draw(g);
}
}
}
This is close, you need to increase x by half the width and then reduce by half the string width. You also need to set the font before you get the font metrics otherwise you're getting the metrics of the existing Graphics font.
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
int textX = x + (width / 2) - (fm.stringWidth(text) / 2);
int textY = y + ((height - fm.getHeight()) / 2) + fm.getAscent();
g.setColor(Color.white);
g.drawString(text, textX, textY);