Reputation: 30
I'm designing an application for configuring a pallet, and I've got a class that extends JLabel
which I use to create JLabels with rotated text. I've found a few examples online of how to do this and my rotation is working well enough, its not perfect but it's a work in progress.
The problem I have at the moment is that the text in my rotated JLabels is duplicated, and I don't know why. Below is a picture showing the duplicate text in each label, its more prominent in some than in others, eg with the height label the duplication can clearly be seen.
Here's the source code for my RotatableText
class that extends JLabel
.
public class RotatableText extends JLabel
{
private static final long serialVersionUID = 1L;
private String text;
private double angle;
public final static String DEGREES = "deg";
public final static String RADIANS = "rad";
private final static double TO_RADIANS = Math.PI/180;
private final static double TO_DEGREES = 180/Math.PI;
/**
* Creates text rotated by angle in a clockwise direction if clockwise is true,
* or anti-clockwise if it's false
* @param angle angle to be rotated by in degrees
* @param clockwise determines direction of rotation@exception Exception if angleUnit is not RotatableText.DEGREES or RotatableText.RADIANS
*/
public RotatableText(String text, double angle, boolean clockwise, final String angleUnit) throws IllegalAngleUnitException
{
if (!(angleUnit.equals(DEGREES) || angleUnit.equals(RADIANS)))
{
throw new IllegalAngleUnitException("Invalid Angle Selected");
}
else if (angleUnit.equals(DEGREES))
{
if (!clockwise)
{
this.angle = -angle * TO_RADIANS;
super.setText(text);
}
else
{
this.angle = angle * TO_RADIANS;
super.setText(text);
}
}
else if (angleUnit.equals(RADIANS))
{
if (!clockwise)
{
this.angle = -angle;
super.setText(text);
}
else
{
this.angle = angle;
super.setText(text);
}
}
setVerticalAlignment(JLabel.TOP);
setHorizontalAlignment(JLabel.LEFT);
}
/**
* Creates text rotated by angle in an anti-clockwise rotation
* @param angle angle to be rotated by in degrees
* @exception Exception if angleUnit is not RotatableText.DEGREES or RotatableText.RADIANS
*/
public RotatableText(String text, double angle, final String angleUnit) throws IllegalAngleUnitException
{
if (!(angleUnit.equals(DEGREES) || angleUnit.equals(RADIANS)))
{
throw new IllegalAngleUnitException("Invalid Angle Selected");
}
else if (angleUnit.equals(DEGREES))
{
this.angle = angle * TO_RADIANS;
super.setText(text);
}
else if (angleUnit.equals(RADIANS))
{
this.angle = angle;
super.setText(text);
}
setVerticalAlignment(JLabel.BOTTOM);
setHorizontalAlignment(JLabel.CENTER);
}
/**
* Draws the Component
*/
@Override
protected void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.rotate(angle, getPreferredSize().width/2, getPreferredSize().height/2);
g2.drawString(super.getText(), 0, 0);
setBounds(getX(), getY());
super.paintComponent(g);
}
/**
* Gets the text of this RotatableText.
* @return text
*/
public String getText()
{
return super.getText();
}
/**
* Set's bounds with a fixed size
*/
public void setBounds(int x, int y)
{
super.setBounds(x, y, 100, 100);
}
public void setText(String text)
{
super.setText(text);
repaint();
}
/**
* Set the angle of this RotatableText in Radians
* @param angle
*/
public void setAngle(double angle)
{
this.angle = angle;
repaint();
}
/**
* Sets the angle of this RotatableText in the specified unit
* @param angle
* @param angleUnit
* @throws IllegalAngleUnitException
*/
public void setAngle(double angle, String angleUnit) throws IllegalAngleUnitException
{
if (!(angleUnit.equals(DEGREES) || angleUnit.equals(RADIANS)))
{
throw new IllegalAngleUnitException("Invalid Angle Selected");
}
else if (angleUnit.equals(DEGREES))
{
this.angle = angle * TO_RADIANS;
}
else if (angleUnit.equals(RADIANS))
{
this.angle = angle;
}
repaint();
}
/**
* Gets the angle of this RotatableText, anti-clockwise from the horizontal, in degrees.
* @return
*/
public double getAngle()
{
return angle * TO_DEGREES;
}
}
Each Label is generated as follows (this is the length label)
lengthLabel = new RotatableText("0", 14, true, RotatableText.DEGREES);
Each label is updated by passing getting the text from its respective text field and passing it as an argument for label.setText()
.
EDIT: printing System.out.println(heightLabel.getText())
prints just one copy of the text.
If anyone has an idea as to why this duplication is occuring, I'd love to hear them.
Thanks,
Sam.
Upvotes: 0
Views: 214
Reputation: 285403
You're drawing your text twice in your code as indicated below:
@Override
protected void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.rotate(angle, getPreferredSize().width/2, getPreferredSize().height/2);
g2.drawString(super.getText(), 0, 0); // ****** here *****
setBounds(getX(), getY());
super.paintComponent(g); // ******* here ******
}
Also, I wouldn't set a component's bounds in a painting method, a dangerous thing to do, but do consider setting the Graphics2D clip there. Also I would do my rotation on a copy of the Graphics object, and then dispose of the copy when done. Otherwise you risk propagating rotational side effects downstream in the painting chain (if not desired)
e.g.,
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.rotate(angle, getPreferredSize().width / 2, getPreferredSize().height / 2);
// g2.drawString(super.getText(), 0, 0);
// setBounds(getX(), getY());
// ***** This is a kludge and needs to be calculated better ****
Rectangle clipBounds = g2.getClipBounds();
int extraBounds = 10;
int x = clipBounds.x - extraBounds;
int y = clipBounds.y - extraBounds;
int width = clipBounds.width + 2 * extraBounds;
int height = clipBounds.height + 2 * extraBounds;
g2.setClip(x, y, width, height);
// ***** end kludge
super.paintComponent(g2);
g2.dispose();
}
Upvotes: 1