Like you see in almost all text-editing softwares, css and more, I want to position my Text with java.awt. My test code for this is:
public class Test {
public static void main(String[] args) {
// creating the frame
JFrame frame = new JFrame("Test");
frame.setSize(1000, 800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
// variables
int x1 = 50;
int x2 = 500;
int y1 = 70;
int y2 = 150;
int y3 = 230;
int width = 400;
int height = 60;
int fontsize = 40;
String text = "This is a test";
frame.add(new JPanel() {
public void paint(Graphics g) {
g.setColor(Color.BLACK);
// borders
g.drawRect(x1, y1, width, height); // top
g.drawRect(x1, y2, width, height); // middle
g.drawRect(x1, y3, width, height); // bottom
g.drawRect(x2, y1, width, height); // left
g.drawRect(x2, y2, width, height); // center
g.drawRect(x2, y3, width, height); // right
g.setFont(new Font("arial", Font.PLAIN, fontsize));
// top
g.drawString(text, x1 + 10, y1 + fontsize); // x1 + 10 (10 is a buffer)
// center
g.drawString(text, x1 + 10, y2 + fontsize + (height - fontsize) / 2);
// bottom
g.drawString(text, x1 + 10, y3 + height);
// left
g.drawString(text, x2, y1 + fontsize);
// middle
g.drawString(text, x2 + width / 2 - text.length() * (fontsize/5), y2 + fontsize + (height - fontsize) / 2);
// right
g.drawString(text, x2 + (width - text.length() * (fontsize/5)), y3 + height);
}
});
frame.setVisible(true);
}
}
Of course it kind of looks messy, but I'm working on it in an API project where I'm implementing ux features atm.
You can use the FontMetrics object of the Graphics object to get information about the Font metrics to help with determining the size of the text:
FontMetrics fm = g.getFontMetrics();
See: Measuring Text tutorial.
Why are you doing custom painting? Why are you not using Swing components with layout managers to manage the UI? We can't give specific advice since we don't know what the goal of your posted code is.
Related
I'm trying to render text on top of a rounded rectangle, but I want the part of the rounded rectangle under the text to be cut out. Here's what I want it to look like:
The problem is that I can't find any easy way of doing this. I tried using clearRect, but that just creates a black rectangle, and I want to have an image underneath (for now it's just white).
I then had the idea that maybe I could just fill the area I want to remove of the rectangle with white, then filter out all the white pixels. This didn't work as well as I hoped, as there are still white pixels left over:
Here's the code I have currently:
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
int padding = 50;
g.setColor(Color.BLUE);
g.drawRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textHeight = (int) (stringBounds.getHeight() + g.getFontMetrics(font).getDescent());
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
//g.clearRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.WHITE);
g.fillRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.GREEN);
g.drawString(text, textX, textY);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Color c = new Color(img.getRGB(x, y));
if (c.getRGB() == Color.WHITE.getRGB())
img.setRGB(x, y, new Color(0, 0, 0, 255).getRGB());
}
}
g.dispose();
}
Is there a simpler way of just clearing the part of the rounded rectangle under the text? After that is done I want to overlay the whole thing on top of an image, so I need the background to be transparent.
You could use the subtract method of the Area class to remove a rectangular section from a stroked RoundRectangle2D.
float strokeWidth = 1.5f;
RoundRectangle2D roundedRect = new RoundRectangle2D.Double(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
Rectangle2D rectMask = new Rectangle2D.Double(textX, padding-strokeWidth, textWidth, 2*strokeWidth);
Stroke stroke = new BasicStroke(strokeWidth);
Area roundedRectArea = new Area(stroke.createStrokedShape(roundedRect));
roundedRectArea.subtract(new Area(rectMask));
g.setColor(Color.BLACK);
g.fill(roundedRectArea);
g.drawString(text, textX, textY);
Which produces:
Full code:
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
int padding = 50;
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
float strokeWidth = 1.5f;
RoundRectangle2D roundedRect = new RoundRectangle2D.Double(padding, padding, width - (padding * 2),
height - (padding * 2), 50, 50);
Rectangle2D rectMask = new Rectangle2D.Double(textX, padding - strokeWidth, textWidth, 2 * strokeWidth);
Stroke stroke = new BasicStroke(strokeWidth);
Area roundedRectArea = new Area(stroke.createStrokedShape(roundedRect));
roundedRectArea.subtract(new Area(rectMask));
g.setColor(Color.BLACK);
g.fill(roundedRectArea);
g.drawString(text, textX, textY);
g.dispose();
try
{
ImageIO.write(img, "png", new File("round.png"));
} catch (IOException e)
{
e.printStackTrace();
}
}
Try this.
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D)img.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
int padding = 50;
g.setComposite(AlphaComposite.Clear);
g.fillRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.BLUE);
g.drawRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textHeight = (int) (stringBounds.getHeight() + g.getFontMetrics(font).getDescent());
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
g.setColor(Color.WHITE);
g.fillRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.GREEN);
g.drawString(text, textX, textY);
g.dispose();
}
You could just use a TitledBorder. Since the curve is proportional to the line size I created a RoundedBorder class with most of the code from the paintBorder() method in the API to allow the size of the arc corners to be specified. It is now a simple pixel amount for both width and height of the arc radius.
first, create a RoundedBorder instance. Try 30 for the arc radius.
then, using the BorderFactor, create a TitledBorder instance and pass the rounded instance as the first agument.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
public class TitledBorderDemo extends JPanel {
JFrame frame = new JFrame();
public static void main(String[] args) {
SwingUtilities
.invokeLater(() -> new TitledBorderDemo().start());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public void start() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Border b = new RoundedBorder(Color.black, 2, 30);
Border titled = BorderFactory.createTitledBorder(b, "Text",
TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION,
new Font("Arial", Font.BOLD, 48));
setBorder(titled);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class RoundedBorder extends LineBorder {
private int arc;
public RoundedBorder(Color color, int lineThickness, int arc) {
super(color, lineThickness);
this.arc = arc;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
if ((this.thickness > 0) && (g instanceof Graphics2D)) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color oldColor = g2d.getColor();
g2d.setColor(this.lineColor);
Shape outer;
Shape inner;
int offs = this.thickness;
int size = offs + offs;
outer = new RoundRectangle2D.Float(x, y, width, height,
arc, arc);
inner = new RoundRectangle2D.Float(x + offs, y + offs,
width - size, height - size, arc, arc);
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
path.append(outer, false);
path.append(inner, false);
g2d.fill(path);
g2d.setColor(oldColor);
}
}
}
The above, when run, produces the following image.
I would like to draw a line above a text in Java. I use Graphics and here is my code :
String s = a.getSequent().toString();
FontMetrics fm = getFontMetrics(getFont());
int textHeight = fm.getHeight();
int textWidth= fm.stringWidth(s);
//Text
g.drawString( s,
(int) ((jPanelWidth- textWidth) / 2),
(int) ((jPanelHeight- textHeight ) / 2));
//Draw line
int x1 = (jPanelWidth- textWidth) / 2;
int x2 = x1 + textWidth; //Problem
int y1 = (jPanelHeight- textHeight *4) / 2;
int y2 = y1;
g.drawLine(x1, y1, x2, y2);
Here is what I have :
I don't understand why the line don't have the same length as my text. The problem is on the value of x2, but why ? Could you help me ?
One of the more obscure concepts to come to grips with is understanding how text is actually rendered.
Rather than been rendered from x/y position down, text is rendered from the baseline up.
This means that the x/y position actually represents the baseline ... just take some time to read that again and if that doesn't help, have a read of Measuring Text
The basic concept is, you want take the x/y position, which represents the baseline and then subtract the ascent
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
String text = "This is a test";
FontMetrics fm = g2d.getFontMetrics();
int textHeight = fm.getHeight();
int textWidth = fm.stringWidth(text);
int xPos = (getWidth() - textWidth) / 2;
int yPos = ((getHeight() - textHeight) / 2) + fm.getAscent();
g2d.setColor(Color.BLACK);
g2d.drawString(text, xPos, yPos);
g2d.drawLine(xPos, yPos - fm.getAscent(), xPos + textWidth, yPos - fm.getAscent());
g2d.dispose();
}
}
}
As #luk2302 said, here is the solution :
I had : FontMetrics fm = getFontMetrics(getFont());
Now I have : FontMetrics fm = getFontMetrics(g.getFont());
I didn't use the right font.
So far I have a java app where I draw a circle(player) and then draw a green rectangle on top(gun barrel). I have it so when the player moves, the barrel follows with it. I want it to find where the mouse is pointing and then rotate the barrel accordingly. For an example of what I mean look at this video I found http://www.youtube.com/watch?v=8W7WSkQq5SU See how the player image reacts when he moves the mouse around?
Here's an image of what the game looks like so far:
So how do I rotate it like this? Btw I don't like using affinetransform or Graphics2D rotation. I was hoping for a better way. Thanks
Using the Graphics2D rotation method is indeed the easiest way. Here's a simple implementation:
int centerX = width / 2;
int centerY = height / 2;
double angle = Math.atan2(centerY - mouseY, centerX - mouseX) - Math.PI / 2;
((Graphics2D)g).rotate(angle, centerX, centerY);
g.fillRect(...); // draw your rectangle
If you want to remove the rotation when you're done so you can continue drawing normally, use:
Graphics2D g2d = (Graphics2D)g;
AffineTransform transform = g2d.getTransform();
g2d.rotate(angle, centerX, centerY);
g2d.fillRect(...); // draw your rectangle
g2d.setTransform(transform);
It's a good idea to just use Graphics2D anyway for anti-aliasing, etc.
Using AffineTransform, sorry, only way I know how :P
public class RotatePane extends javax.swing.JPanel {
private BufferedImage img;
private Point mousePoint;
/**
* Creates new form RotatePane
*/
public RotatePane() {
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();
}
}
Will produce this effect
The red line (point out from the center) will want to follow the cursor.
I'm trying to draw to filled circles, centered at random locations, with a line connecting the circles. The distance between the to centers is displayed on the line and whenever your resize the frame, the circles are redisplayed in new random locations.
I'm stuck in how to display the distance?
Any help is appreciated and thx for advance.
This's the code (what i managed to do):
public class Test extends JFrame {
public Test() {
add(new LineConnectingTheTwoCircles());
}
// Panel class
class LineConnectingTheTwoCircles extends JPanel {
//Default constructor
LineConnectingTheTwoCircles() {
}
/* Override paintComponent (getting access to the panel's Graphics
class) */
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int radius = 15;
// getting coordinates of circle 1
int x1 = (int) (Math.random() * (getWidth()));
int y1 = (int) (Math.random() * (getHeight()));
// getting coordinates of circle 2
int x2 = (int) (Math.random() * (getWidth()));
int y2 = (int) (Math.random() * (getWidth()));
// Setting color and drawing a filled circles (1 & 2)
g.setColor(Color.BLUE);
g.fillOval(x1 - radius, y1 - radius, 2 * radius, 2 * radius);
g.drawString("1", x1 - 25, y1);
g.setColor(Color.RED);
g.fillOval(x2 - radius, y2 - radius, 2 * radius, 2 * radius);
g.drawString("2", x2 - 25, y2);
connectingTheTwoCircles(g, x1, y1, x2, y2);
}
// Connecting the two circles from the center
private void connectingTheTwoCircles(Graphics g, int x1, int y1,
int x2, int y2) {
//Distance between the circles centered
double D = Math.sqrt((Math.pow((y2 - y1), 2))
+ (Math.pow((x2 - x1), 2)));
//Getting the coordinates for the line l
int x11 = x1;
int y11 = y1;
int x21 = x2;
int y21 = y2;
g.setColor(Color.BLACK);
g.drawLine(x11, y11, x21, y21);
}
public double getDistance(double x1, double y1, double x2, double y2) {
return Math.sqrt((Math.pow((y2 - y1), 2))
+ (Math.pow((x2 - x1), 2)));
}
}
public static void main(String[] args) {
// Frame declaration
Test frame = new Test();
/*
* Invoking some methods, to set a title on the title bar, to specifier
* the size of the frame to centre it on the screen, to tell the program
* to terminate when the frame is closed and finally to display it
*/
frame.setTitle("This is a test");
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Try next code draws distance at line center
double distance = getDistance(x11, y11, x21, y21);
g.drawString(distance+" ",
x11> x21 ? x21 + (x11-x21)/2 : x11 + (x21 - x11)/2 ,
y11> y21 ? y21 + (y11-y21)/2 : y11 + (y21 - y11)/2 );
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 ;)