JLabel mit Swing drehen - ohne Graphics

Fabio Hellmann

Erfahrenes Mitglied
Hi,
also ich hab damit auch gerade mal ein wenig herumexperimentiert. Ich bin soweit, dass sich das JLabel rotiert, allerdings ist nur der Teil sichtbar, der über setBounds(...) angegeben wurde. Eine Lösung für dieses Problem find ich momentan aber auch nicht.
Java:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;


/**
 * @author FabioH
 */
public class JLabelRotator extends JFrame
{
	/**
	 * 
	 */
	public JLabelRotator() {
		super("JLabelRotator");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setSize(200, 200);
		
		final JPanel p = new JPanel();
		p.setLayout(null);
		add(p);
		
		final JRotationLabel l = new JRotationLabel("Text");
		l.setBounds(50, 75, 50, 20);
		l.setRotationLocation(25, 10);
		l.setBorder(new EtchedBorder(EtchedBorder.RAISED));
		p.add(l);
		
		setVisible(true);
		
		new Timer().scheduleAtFixedRate(new TimerTask()
						{
			private int currAngle = 0;
			
			@Override
			public void run() {
				if(currAngle == 360) {
					currAngle = 0;
				}
				l.setAngle(currAngle);
				currAngle++;
				p.repaint();
			}
		}, 0, 20);
	}
	
	public static void main(final String[] args) {
		new JLabelRotator();
	}
	
	private final class JRotationLabel extends JLabel
	{
		private double rotationPointX = 0;
		private double rotationPointY = 0;
		private double angle = 0;
		
		/**
		 * @param text
		 */
		public JRotationLabel(final String text) {
			super(text);
		}
		
		/**
		 * @param text
		 * @param horizontalalign
		 */
		public JRotationLabel(final String text, final int horizontalalign) {
			super(text, horizontalalign);
		}
		
		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
		 */
		@Override
		protected void paintComponent(final Graphics g) {
			// ---
			// Rotate
			final Graphics2D g2d = (Graphics2D) g;
			g2d.rotate(Math.toRadians(angle), rotationPointX, rotationPointY);
			super.paintComponent(g);
		}
		
		/**
		 * @param angle
		 *            the angle to set
		 */
		public void setAngle(final double angle) {
			this.angle = angle;
		}
		
		/**
		 * @return the angle
		 */
		public double getAngle() {
			return angle;
		}
		
		/**
		 * @param x
		 * @param y
		 */
		public void setRotationLocation(final double x, final double y) {
			rotationPointX = x;
			rotationPointY = y;
		}
	}
}

Vielleicht kannst du den Ansatz aber weiterverfolgen und findest noch eine Lösung für dieses Problem. ;)

Gruß

Fabio
 

genodeftest

Erfahrenes Mitglied
@Fabio: Dann bleibt von meinem Post wohl nur noch die 1. Methode übrig, die Rotation im übergeordneten Container durchzuführen.

@Rhodo: Was dir eventuell noch Probleme bereiten könnte, ist die Tatsache, dass du die ContentPane des JFrames überschreiben musst, nicht den JFrame selbst.
 

Rhodo

Grünschnabel
Danke euch beiden nochmal.
Das Drehen klappt jetzt, nur eben das von Fabio angesprochene Problem mit den Bounds bleibt.
Mal schauen ob ich da noch was find

Edit: ok, ich habs jetzt mehr oder weniger. Ich muss:
1. um die obere linke Ecke von der JLayeredPane drehen
2. die Bounds nach dem drehen neu setzen.
 
Zuletzt bearbeitet:

Fabio Hellmann

Erfahrenes Mitglied
Hi,
also ich hab jetzt doch noch eine Lösung gefunden, die mittels der BasicLabelUI umgesetzt wird.

JLabelRotatorUI (eigenes L&F):
Java:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicLabelUI;


/**
 * A UI delegate for JLabel that rotates the label 90º
 * <P>
 * Extends {@link BasicLabelUI}.
 * <P>
 * The only difference between the appearance of labels in the Basic and Metal L&Fs is the manner in
 * which diabled text is painted. As VerticalLabelUI does not override the method paintDisabledText,
 * this class can be adapted for Metal L&F by extending MetalLabelUI instead of BasicLabelUI.
 * <P>
 * No other changes are required.
 * 
 * @author Darryl
 * @author Fabio (Orginal angepasst)
 */
public class JLabelRotatorUI extends BasicLabelUI
{
	private final boolean clockwise;
	private double angle = 0;
	// see comment in BasicLabelUI
	Rectangle verticalViewR = new Rectangle();
	Rectangle verticalIconR = new Rectangle();
	Rectangle verticalTextR = new Rectangle();
	protected static JLabelRotatorUI verticalLabelUI =
			new JLabelRotatorUI();
	private final static JLabelRotatorUI SAFE_VERTICAL_LABEL_UI =
			new JLabelRotatorUI();
	
	/**
	 * Constructs a <code>VerticalLabelUI</code> with the default anticlockwise
	 * rotation
	 */
	public JLabelRotatorUI() {
		this(true);
	}
	
	/**
	 * Constructs a <code>VerticalLabelUI</code> with the desired rotation.
	 * <P>
	 * 
	 * @param clockwise
	 *            true to rotate clockwise, false for anticlockwise
	 */
	public JLabelRotatorUI(final boolean clockwise) {
		this.clockwise = clockwise;
	}
	
	/**
	 * @see ComponentUI#createUI(javax.swing.JComponent)
	 */
	public static ComponentUI createUI(final JComponent c) {
		if(System.getSecurityManager() != null) {
			return SAFE_VERTICAL_LABEL_UI;
		} else {
			return verticalLabelUI;
		}
	}
	
	/**
	 * Overridden to always return -1, since a vertical label does not have a
	 * meaningful baseline.
	 * 
	 * @see ComponentUI#getBaseline(JComponent, int, int)
	 */
	@Override
	public int getBaseline(final JComponent c, final int width, final int height) {
		super.getBaseline(c, width, height);
		return -1;
	}
	
	/**
	 * Overridden to always return Component.BaselineResizeBehavior.OTHER,
	 * since a vertical label does not have a meaningful baseline
	 * 
	 * @see ComponentUI#getBaselineResizeBehavior(javax.swing.JComponent)
	 */
	@Override
	public Component.BaselineResizeBehavior getBaselineResizeBehavior(
			final JComponent c) {
		super.getBaselineResizeBehavior(c);
		return Component.BaselineResizeBehavior.OTHER;
	}
	
	/**
	 * Transposes the view rectangles as appropriate for a vertical view
	 * before invoking the super method and copies them after they have been
	 * altered by
	 * {@link SwingUtilities#layoutCompoundLabel(FontMetrics, String, Icon, int, int, int, int, Rectangle, Rectangle, Rectangle, int)}
	 */
	@Override
	protected String layoutCL(final JLabel label, final FontMetrics fontMetrics,
			String text, final Icon icon, Rectangle viewR, Rectangle iconR,
			Rectangle textR) {
		
		verticalViewR = transposeRectangle(viewR, verticalViewR);
		verticalIconR = transposeRectangle(iconR, verticalIconR);
		verticalTextR = transposeRectangle(textR, verticalTextR);
		
		text = super.layoutCL(label, fontMetrics, text, icon,
				verticalViewR, verticalIconR, verticalTextR);
		
		viewR = copyRectangle(verticalViewR, viewR);
		iconR = copyRectangle(verticalIconR, iconR);
		textR = copyRectangle(verticalTextR, textR);
		return text;
	}
	
	/**
	 * Transforms the Graphics for vertical rendering and invokes the
	 * super method.
	 */
	@Override
	public void paint(final Graphics g, final JComponent c) {
		final Graphics2D g2 = (Graphics2D) g.create();
		if(clockwise) {
			g2.rotate(Math.toRadians(angle), c.getSize().width / 2, c.getSize().width / 2);
		} else {
			g2.rotate(Math.toDegrees(angle), c.getSize().width / 2, c.getSize().width / 2);
		}
		super.paint(g2, c);
	}
	
	/**
	 * Returns a Dimension appropriate for vertical rendering
	 * 
	 * @see ComponentUI#getPreferredSize(javax.swing.JComponent)
	 */
	@Override
	public Dimension getPreferredSize(final JComponent c) {
		return transposeDimension(super.getPreferredSize(c));
	}
	
	/**
	 * Returns a Dimension appropriate for vertical rendering
	 * 
	 * @see ComponentUI#getMaximumSize(javax.swing.JComponent)
	 */
	@Override
	public Dimension getMaximumSize(final JComponent c) {
		return transposeDimension(super.getMaximumSize(c));
	}
	
	/**
	 * Returns a Dimension appropriate for vertical rendering
	 * 
	 * @see ComponentUI#getMinimumSize(javax.swing.JComponent)
	 */
	@Override
	public Dimension getMinimumSize(final JComponent c) {
		return transposeDimension(super.getMinimumSize(c));
	}
	
	private Dimension transposeDimension(final Dimension from) {
		return new Dimension(from.height, from.width + 2);
	}
	
	private Rectangle transposeRectangle(final Rectangle from, Rectangle to) {
		if(to == null) {
			to = new Rectangle();
		}
		to.x = from.y;
		to.y = from.x;
		to.width = from.height;
		to.height = from.width;
		return to;
	}
	
	private Rectangle copyRectangle(final Rectangle from, Rectangle to) {
		if(to == null) {
			to = new Rectangle();
		}
		to.x = from.x;
		to.y = from.y;
		to.width = from.width;
		to.height = from.height;
		return to;
	}
	
	/**
	 * @param angle
	 *            the angle to set
	 */
	public void setAngle(final double angle) {
		this.angle = angle;
	}
	
	/**
	 * @return the angle
	 */
	public double getAngle() {
		return angle;
	}
}

Testklasse:
Java:
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


/**
 * @author FabioH
 */
public class JLabelRotator extends JFrame
{
	/**
	 * 
	 */
	public JLabelRotator() {
		super("JLabelRotator");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setSize(200, 200);
		
		final JPanel p = new JPanel();
		p.setLayout(null);
		setContentPane(p);
		
		final JLabel label = new JLabel("Text", SwingConstants.CENTER);
		label.setBounds(50, 75, 50, 50);
		final JLabelRotatorUI labelRotator = new JLabelRotatorUI();
		label.setUI(labelRotator);
		p.add(label);
		
		setVisible(true);
		
		new Timer().scheduleAtFixedRate(new TimerTask()
						{
			private int currAngle = 0;
			
			@Override
			public void run() {
				if(currAngle == 360) {
					currAngle = 0;
				}
				labelRotator.setAngle(currAngle);
				currAngle++;
				p.repaint();
			}
		}, 0, 20);
	}
	
	public static void main(final String[] args) {
		new JLabelRotator();
	}
}

Ich denke mal, dass das die angenehmste Variante ist. Allerdings musst du darauf achten, dass du die Bounds in der richtigen größe setzt, da der Text sonst abgeschnitten wird. ;)

Gruß

Fabio
 
Zuletzt bearbeitet:

Rhodo

Grünschnabel
Also sehr angenehm sieht das auf den ersten Blick nich aus :D
Aber danke schonmal :)
Ich werds mir später mal angucken. Theoretisch fehlt mir eig nur die Sache mit den Bounds, weil meine Labels etwas abgeschnitten sind
 

Rhodo

Grünschnabel
Ok, habs mir jetzt nochmal durchgelesen aber so wirklich verstanden hab ich nix (die UIs hab ich mir noch nie angeschaut).
Aber mal unabhängig davon: wo genau ist der entscheidende Unterschied zu deinem anderen Code?
die Label-Bounds hätte man ja auch noch setzen können.

Find das irgendwie so viel Code :D

Edit: Ok, bin jetzt doch dahinter gekommen, allerdings hat es in meinem Projekt nicht funktioniert (dein Beispiel aber schon). Brauchs jetzt auch nicht mehr. Hab anstatt um 0,0 um w/2, h/2 gedreht und alles is perfekt :)

Damit sollte das Problem gelöst sein.
Danke nochmal euch beiden für die Hilfe :)
 
Zuletzt bearbeitet: