drawString with Rectangle border - java

I want to draw string using Graphics with Rectangle border outside the string.
This is what I already do:
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
FontMetrics fontMetrics = g2d.getFontMetrics();
String str = "aString Test";
int width = fontMetrics.stringWidth(str);
int height = fontMetrics.getHeight();
int x = 100;
int y = 100;
// Draw String
g2d.drawString(str, x, y);
// Draw Rectangle Border based on the string length & width
g2d.drawRect(x - 2, y - height + 2, width + 4, height);
}
My problem is, I want to draw string with new line ("\n") with Rectangle border outside:
This is the code for the new line:
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
FontMetrics fontMetrics = g2d.getFontMetrics();
String str = "aString\nTest";
int width = fontMetrics.stringWidth(str);
int height = fontMetrics.getHeight();
int x = 100;
int y = 100;
// Drawing string per line
for (String line : str.split("\n")) {
g2d.drawString(line, x, y += g.getFontMetrics().getHeight());
}
}
Can anyone help me for this problem? I appreciate your help & suggestion...
Final Code
int numberOfLines = 0;
for (String line : str.split("\n")) {
if(numberOfLines == 0)
g2d.drawString(line, x, y);
else
g2d.drawString(line, x, y += g.getFontMetrics().getHeight());
numberOfLines++;
}
g2d.drawRect(x - 2, y - height * numberOfLines + 2, width + 4, height * numberOfLines);

If I understand correctly, your issues is with the rectangle's height.
Try keeping a record of how many lines you have eg:
int numberOfLines=0;
for (String line : str.split("\n")) {
g2d.drawString(line, x , y + (numberOfLines * height));
numberOfLines++;
}
g2d.drawRect(x - 2, y - height + 2, width + 4, height * numberOfLines);
This also changes how it works out the y value for drawing the string.
Would something like that work?

You could also create a regular JLabel object, and then set its text with html and include a tag. e.g. myLabel.setText("<html>aString<br>Test</html>");, then add a border with a single line to the JLabel.

Related

Java - Change the color of some squares created with Graphics2D

I just want to create a simple game with 100 x 100 square, each square is 5 pixels.
I created a class:
public class Draw extends JComponent{
private List<Graphics2D> recList = new ArrayList<Graphics2D>();
public void paint(Graphics g) {
//THIS TO SET (0,0) PANEL START AT BOTTOM LEFT
Graphics2D g2 = (Graphics2D)g;
AffineTransform at = g2.getTransform();
at.translate(0, getHeight());
at.scale(1, -1);
g2.setTransform(at);
//THIS TO DRAW ALL THE SQUARES
for (int i = 0;i<100;i++){
for (int j=0;j<100;j++){
g2.setColor(Color.red);
g2.drawRect(5*i, 5*j, 5, 5);
recList.add(g2); //Store each square to the list to change the color
}
}
}
}
Then I just drag it to the design windows of netbeans and the squares are painted, looks good...
But it seems like I made a wrong move. At the first time I wanted to get a specific square from the list using their location, but the Graphic2d doesn't have any method to get the location (x and y) or to change the color.
I don't know if there is any other way I can make it come true?
PS: One more thing, can I set the location of each square to its center?
You could create your own Tile class, which stores info such as x, y, width, height, and color. Each Tile object could also be in charge of painting itself:
class Tile {
private int x, y, width, height;
private Color color;
public Tile(int x, int y, int width, int height, Color color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
public void paint(Graphics g) {
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
Create the tiles before-hand:
List<Tile> tiles = ...;
void createTiles() {
for(int x = 0; x < 100; x++) {
for(int y = 0; y < 100; y++) {
Color color = ...; //choose color
int size = 5;
int tileX = x * size;
int tileY = y * size;
tiles.add(new Tile(tileX, tileY, size, size, color));
}
}
}
Then render by passing the graphics object to them in the paint method:
void paint(Graphics g) {
tiles.forEach(tile -> tile.paint(g));
}

How can I separate the letter in string when drawing

I want to draw a string that I take from a jtextarea, but I want to draw with 20px space to each letter, but I think I can't do this with drawstring and I can't draw a char per time with drawchars, so what can I do?
I want to write a letter above each trace, but how I'm doing know is not working
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i=0;i<PalavraAdivinha.length();i++)
{
g.fillRect(InicioTraco+(i*40), 240, 40, 5);
}
Font titulo = new Font("Arial",Font.BOLD,40);
g.setFont(titulo);
g.drawString("Adivinhe a palavra", 0,100 );
if(PalavraDigitada != null)
{
g.drawString(PalavraDigitada, InicioTraco, 235);
}
}
You can draw each character seperately. By retrieving the FontMetrics currently used by the Graphics object, you can measure the width of each character, so you can advance using this width and a given seperation.
public static void drawString(Graphics g, String string, int x, int y,
int seperation) {
FontMetrics metrics = g.getFontMetrics();
int drawx = x;
for (int i = 0; i < string.length(); ++i) {
String character = "" + string.charAt(i);
g.drawString(character, drawx, y);
drawx += seperation + metrics.stringWidth(character);
}
}
Example Code
public static void main(String[] args) throws IOException {
BufferedImage image = new BufferedImage(512, 256,
BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.setColor(Color.BLACK);
for (int i = 0; i < 10; ++i)
drawString(g, "i am drawn with seperation " + i, 24, 24 + 16 * i, i);
ImageIO.write(image, "png", new File("output.png"));
}
Result

Drawing a character with a specific size in Java

I have a Java programming which displays the grid of 10x10 cells. In each cell I would like to draw a single character and have it take up the whole cell.
I am currently using the following code, but it isn't quite the right size.
graphics.setFont(new Font("monospaced", Font.PLAIN, 12));
for(int x = 0; x < GRID_WIDTH; x++) {
for(int y = 0; y < GRID_HEIGHT; y++) {
graphics.drawString(Character.toString(grid[x][y]), x * CELL_WIDTH, (y + 1) * CELL_HEIGHT);
}
}
Is there any way in Java to draw a 10x10 (or CELL_WIDTHxCELL_HEIGHT) character?
I build these methods in a project I happened to have open when reading this question =D. Do note that the method pickOptimalFontSize should be adapted for your specfic case. The default size is 130 which would likely be far to high for your case. You can tweak it as you need but this demonstrates the basics. In your case use them like this:
Font baseFont = new Font("monospaced", Font.PLAIN, 12);
for(int x = 0; x < GRID_WIDTH; x++) {
for(int y = 0; y < GRID_HEIGHT; y++) {
graphics.setFont(pickOptimalFontSize(graphics, Character.toString(grid[x][y]), CELL_WIDTH, CELL_HEIGHT, baseFont));
drawString(graphics, Character.toString(grid[x][y]), x * CELL_WIDTH, (y + 1) * CELL_HEIGHT, "left", "center");
}
}
public static void drawString(Graphics g, String str, double x, double y, String hAlign, String vAlign) {
FontMetrics metrics = g.getFontMetrics();
double dX = x;
double dY = y;
if(hAlign == null || "left".equals(hAlign.toLowerCase())) {
} else if("center".equals(hAlign.toLowerCase())) {
dX -= metrics.getStringBounds(str, g).getWidth()/2;
} else if("right".equals(hAlign.toLowerCase())) {
dX -= metrics.getStringBounds(str, g).getWidth();
}
if(vAlign == null || "bottom".equals(vAlign.toLowerCase())) {
} else if("center".equals(vAlign.toLowerCase())) {
dY += metrics.getAscent()/2;
} else if("top".equals(vAlign.toLowerCase())) {
dY += metrics.getAscent();
}
g.drawString(str, (int)dX, (int)dY);
}
private static Font pickOptimalFontSize (Graphics2D g, String title, int width, int height, Font baseFont) {
Rectangle2D rect = null;
float fontSize = 130; //initial value
Font font;
do {
fontSize-=1;
font = baseFont.deriveFont(fontSize);
rect = getStringBoundsRectangle2D(g, title, font);
} while (rect.getWidth() >= width || rect.getHeight() >= height);
return font;
}
public static Rectangle2D getStringBoundsRectangle2D (Graphics g, String title, Font font) {
g.setFont(font);
FontMetrics fm = g.getFontMetrics();
Rectangle2D rect = fm.getStringBounds(title, g);
return rect;
}
I found a solution that works as I wanted: I created a class called CharacterImageGenerator that generates (and caches) Images of characters. I then draw and scale these images whenever I want to draw a character.
public class CharacterImageGenerator {
private FontMetrics metrics;
private Color color;
private Map<Character, Image> images;
public CharacterImageGenerator(FontMetrics metrics, Color color) {
this.metrics = metrics;
this.color = color;
images = new HashMap<Character, Image>();
}
public Image getImage(char c) {
if(images.containsKey(c))
return images.get(c);
Rectangle2D bounds = new TextLayout(Character.toString(c), metrics.getFont(), metrics.getFontRenderContext()).getOutline(null).getBounds();
if(bounds.getWidth() == 0 || bounds.getHeight() == 0) {
images.put(c, null);
return null;
}
Image image = new BufferedImage((int)bounds.getWidth(), (int)bounds.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = image.getGraphics();
g.setColor(color);
g.setFont(metrics.getFont());
g.drawString(Character.toString(c), 0, (int)(bounds.getHeight() - bounds.getMaxY()));
images.put(c, image);
return image;
}
}
Which I then initialize with a large font to get decent looking characters.
// During initialization
graphics.setFont(new Font("monospaced", Font.PLAIN, 24));
characterGenerator = new CharacterImageGenerator(graphics.getFontMetrics(), Color.WHITE);
And then scale and draw to the size I want.
private void drawCharacter(int x, int y, char c) {
graphics.drawImage(characterGenerator.getImage(c), PADDING + (x * TILE_WIDTH), PADDING + (y * TILE_HEIGHT), TILE_WIDTH, TILE_HEIGHT, null);
}

Why is my image rotating off center?

I'm using Java/Slick 2D to play with graphics and using the mouse to rotate an image. Something strange happens though: the image doesn't necessarily face the mouse. At 45 degrees from the normal line it does, but the further you get, the further your are off. See the images below (the white circle being the mouse, the text being the angle):
Here is the rotation code I used:
int mX = Mouse.getX();
int mY = HEIGHT - Mouse.getY();
int pX = sprite.x;
int pY = sprite.y;
int tempY, tempX;
double mAng, pAng = sprite.angle;
double angRotate=0;
if(mX!=pX){
mAng = Math.toDegrees(Math.atan2(mY - pY, mX - pX));
if(mAng==0 && mX<=pX)
mAng=180;
}
else{
if(mY>pY)
mAng=90;
else
mAng=270;
}
sprite.angle = mAng;
sprite.image.setRotation((float) mAng);
Any ideas what's going on? I'm assuming it has something to do with the fact that the image coordinates come from the top left, but I don't know how to counter it. FYI: screen 640x460, image 128x128 and centered in window.
EDIT: Unfortunately, nothing there really worked. Here is a picture with some more information:
EDIT2: Found the answer! had to change: int px/py = sprite.x/y to
int pX = sprite.x+sprite.image.getWidth()/2;
int pY = sprite.y+sprite.image.getHeight()/2;
It looks like your getting the value of your mouse from the left and setting that distance to your rotation... Here's something that might help:
http://www.instructables.com/id/Using-Java-to-Rotate-an-Object-to-Face-the-Mouse/?ALLSTEPS
This is some example code I wrote of a similar question which might help.
Now it doesn't use slick, it uses Swing and Graphics2D but it might help you gain some ideas.
public class TestRotatePane extends JPanel {
private BufferedImage img;
private Point mousePoint;
public TestRotatePane() {
try {
img = ImageIO.read(getClass().getResource("/MT02.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
double rotation = 0f;
int width = getWidth() - 1;
int height = getHeight() - 1;
if (mousePoint != null) {
int x = width / 2;
int y = height / 2;
int deltaX = mousePoint.x - x;
int deltaY = mousePoint.y - y;
rotation = -Math.atan2(deltaX, deltaY);
rotation = Math.toDegrees(rotation) + 180;
}
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
g2d.drawImage(img, x, y, this);
x = width / 2;
y = height / 2;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.drawLine(x, y, x, y - height / 4);
g2d.dispose();
}
}
You will, obviously, need to supply your own image ;)

How can I put the circles at the center of the line?

I have a diagonal line and I have also a circles having a 100 meters in distance. The problem is that the circles are not really to the center of the line. I know this is quiet easy but I'm just confused on how to do it.. Could someone help me how to put the circles at the center of the line?
Here's what I've tried so far :
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.white);
int x0_pixel = 0;
int y0_pixel = 0;
int x1_pixel = getWidth();
int y1_pixel = getHeight();
int x0_world = 0;
int y0_world = 0;
double x1_world = 2000; // meters
double y1_world = 1125; // meters
double x_ratio = (double) x1_pixel / x1_world;
double y_ratio = (double) y1_pixel / y1_world;
int xFrom = 0;
int yFrom = 0;
double xTo = x1_world;
double yTo = y1_world;
int FromX_pixel = convertToPixelX(xFrom, x_ratio);
int FromY_pixel = convertToPixelY(y1_pixel, yFrom, y_ratio);
int ToX_pixel = convertToPixelX((int) xTo, x_ratio);
int ToY_pixel = convertToPixelY(y1_pixel, (int) yTo, y_ratio);
g2d.setColor(Color.RED);
g2d.drawLine(FromX_pixel, FromY_pixel, ToX_pixel, ToY_pixel);
double theta = Math.atan(yTo / xTo);
int len = (int) Math.sqrt(xTo * xTo + yTo * yTo);
int interval = 100;
final double cosTheta = Math.cos(theta);
final double sinTheta = Math.sin(theta);
for (int distance = xFrom; distance <= len; distance += interval)
{
double distance_x = distance * cosTheta;
double distance_y = distance * sinTheta;
int x_circle_pixel = convertToPixelX(distance_x, x_ratio);
int y_circle_pixel = convertToPixelY(y1_pixel, distance_y, y_ratio);
g2d.drawOval(x_circle_pixel, y_circle_pixel, 50, 50);
g2d.setColor(Color.BLUE);
}
Toolkit.getDefaultToolkit().
sync();
g2d.dispose();
}
private static int convertToPixelY(int y_offset, double y_world, double y_ratio)
{
return (int) (y_offset - (y_world * y_ratio));
}
private static int convertToPixelX(double x_world, double x_ratio)
{
return (int) (x_world * x_ratio);
}
When you draw an oval, the first two parameters are the upper-left corner of the rectangle that holds the oval. The next two parameters are the width and height of this same bounding rectangle. Your current code places the upper-left corner on the line itself, but what you actually want is that the center of the bounding rectangle be placed on the line. The solution to your problem is to simply shift the upper-left corner over by 1/2 the diameter. Your code should have something like so:
public class GraphicsFoo extends JPanel {
// avoid using magic numbers:
private static final int CIRCLE_DIAMETER = 50;
//....
// override a JComponent's paintComponent method, not its paint method
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.white);
// make your graphics smooth
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// ...
final double cosTheta = Math.cos(theta);
final double sinTheta = Math.sin(theta);
for (int distance = xFrom; distance <= len; distance += interval)
{
//....
// *** here's the key: ***
g2d.drawOval(
x_circle_pixel - CIRCLE_DIAMETER / 2,
y_circle_pixel - CIRCLE_DIAMETER / 2,
CIRCLE_DIAMETER, CIRCLE_DIAMETER);
g2d.setColor(Color.BLUE);
}

Categories