# JLabel Icon Pfeilform



## guni (28. Okt 2010)

Hallo,

ich versuch gerade ein JLabel in Pfeilform zu entwickeln.
Dazu habe ich mir eine Klasse geschrieben, die die Pfeile zeichnet:


```
class Arrow extends Polygon
{
	private static final long serialVersionUID = 1L;
	public static final int NOARROW = 1;
	public static final int LEFTARROW = 2;
	public static final int RIGHTARROW = 3;
	public static final int BOTHARROWS = 4;
	
	private int x;
	private int y;
	private int w;
	private int h;
	private double angle;
	private int ht;

	private Arrow()
	{	
		super();
	}
	
	public Arrow(int peekangle, Rectangle bounds, int mode) 
	{
		this();
		x = bounds.x;
		y = bounds.y;
		w = bounds.width;
		h = bounds.height;
		angle = Math.toRadians(peekangle);
		ht = (int) ((h/2) / Math.tan(angle/2));
		
		switch (mode)
		{
		case LEFTARROW:
			leftarrow();
			break;
		case RIGHTARROW:
			rightarrow();
			break;
		case BOTHARROWS:
			botharrows();
			break;
		default:
			noarrow();
			break;
		}
	}
	
	private void leftarrow()
	{
		addPoint(x, y);
		addPoint(w, y);
		addPoint(w, h);
		addPoint(0, h);
		addPoint(ht, h / 2);
	}
	
	private void rightarrow()
	{
		addPoint(x, y);
		addPoint(w - ht, y);
		addPoint(w, h / 2);
		addPoint(w - ht, h);
		addPoint(y, h);
	}
	
	private void botharrows()
	{
		addPoint(x, y);
		addPoint(w - ht, y);
		addPoint(w, h / 2);
		addPoint(w - ht, h);
		addPoint(x, h);
		addPoint(ht, h / 2);
	}
	
	private void noarrow()
	{
		addPoint(x, y);
		addPoint(w, y);
		addPoint(w, h);
		addPoint(x, h);
	}
}
```
danach erstelle ich mein Label wie folgt:

```
JLabel l = new JLabel("Test") {
			private static final long serialVersionUID = 1L;

			public void paint( Graphics g ) {
				Graphics2D g2 = (Graphics2D) g;
				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				Rectangle r = getBounds();
				Polygon p = new Arrow(120, r, Arrow.LEFTARROW);
				g2.drawPolygon(p);
				g2.setPaint(Color.WHITE);
				g2.fillPolygon(p);
				super.paint(g);
			}			
		};
```
ist das richtig so, oder vertoße ich da gegen irgendwelche Regeln?!
Mein Programm wift jetzt 2 Probleme auf:
1. wenn ich mein Polygon fülle, dann geht mein schöner Rahmen verloren weil die Füllung den Rahmen an manchen Stellen überlagert. Das sieht schrecklich aus. Kann man das verhindern?
2. ich möchte mein JLabel nun so umschreiben, dass setHorizontalAlignment(JLabel.CENTER) den Text und / oder das Icon nun nicht mehr genau mittig platziert sondern leicht versetzt (je nach Pfeilspitze).
Ich kriegs einfach nicht auf die Reihe! Hab mir schon den Source von JLabel, JComponent, Container und Component durchgesehen.
Hab schon probiert, in der paint()-Methode vor super.paint() ein setBounds zu setzen. Hab auch schon probiert die setAlignmentX Methode zu überschreiben. Nichts hilft! Wisst ihr da weiter?!

mfg, guni


----------



## L-ectron-X (28. Okt 2010)

Grundsätzlich wird in erbenden Swing-Komponenten die paintComponent()-, statt der paint()-Methode überschrieben.


----------



## guni (28. Okt 2010)

Hallo,

ja. das hab ich auch schon wo gelesen.
Ich verstehe nicht ganz warum ...

aber egal. in meinem Fall geht das ohnehin nicht, da ich mein Label später in eine Oracle Form einbinden will. Und da muss ich dann glaub ich die paint-Methode überscheiben.

Wie auch immer: 
habt ihr einen Vorschlag, wie ich mein Polygon schön gezeichnet bekomme?
und wie ich die Mitte eines Labels versetze? (so, das setHorizontalAlignment(JLabel.CENTER)) nicht mehr mittig platziert?! Wie gesagt: Hab mir schon ein paar der Parent-Klassen durchgesehen; bis jetzt hab ich die Lösung noch nicht gefunden.

mfg, guni


----------



## Gast2 (29. Okt 2010)

Ich würde ImageIcon implementieren und dort zeichnen dann kannst du den Pfeil öfters wieder verwenden.
SwingX oder so hat schon so eine Klasse um Pfeile beim Tabellen sortieren zu zeichnen musst dir mal die sourcen downloaden und anschauen.


----------



## guni (29. Okt 2010)

Ich kann in einem ImageIcon zeichnen?! Das wusste ich nicht!
Ich brauch ihn doch sowieso nur einmal. Pro Label halt einmal. Deswegen will ich ja mein JLabel so überschreiben, dass es dann in Pfeilform verwendbar ist.
selbst wenn ich ein ImageIcon nutze löst das mein eigentliches Problem nicht: ich will die Methode, die sich ums zentrieren des Label-Textes kümmert überschreiben. z.B. wenn man setHorizontalAlignment(CENTER) setzt, dann soll der Text nicht genau mittig sondern rechts ein bisschen über der Mitte beginnen!

Irgendwie hatte ich vermutet, dass das zentrieren des Textes mittels der setAlignmentX-Methode von JComponent funktioniert:

```
public void setAlignmentX(float alignmentX) {
   this.alignmentX = alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f : alignmentX;
   isAlignmentXSet = true;
}
```

ich habe sie mir also überschrieben:

```
// Statt Zentrieren den Text 0,75% vom Rand des Labels
public void setAlignmentX(float alignmentX) {
   super.setAlignmentX(alignmentX*1,5);
}
```

hat natürlich nicht funktioniert.

Dann hab ich mir überlegt, dass ich in der Paint Methode die Bounds ändere (das wär mir eigentlich am Liebsten). Aber das hat noch viel weniger funktioniert!!


Kann mir da irgendjemand weiterhelfen?!

mfg, guni


----------



## L-ectron-X (29. Okt 2010)

So richtig hab ich noch nicht verstanden, wie es aussehen soll... (Bilder sagen mehr als Worte)
Aber vielleicht hilft dir auch JLabel.html#setIconTextGap(int)


----------



## guni (29. Okt 2010)

gut. Versuche es mit der Zeichnung im Anhang zu zeigen.
Ich will einfach den Mittelpunkt meines Labels horizontal versetzen; so, dass die Methode beim Zentrieren des Textes einen "Fehler" macht.


----------



## slawaweis (29. Okt 2010)

versuche es mit EmptyBorder. Du musst nur die Funktion setBorder überschreiben und dafür sorgen, dass eventuelle weitere Border mit deinem internen EmptyBorder kombiniert werden.

Slawa


----------



## guni (31. Okt 2010)

hallo,

wenn ich ein Label erzeuge, dann kann ich dieses Label mit einem bestimmten Text versehen.
Und dann kann ich das horizonalAlignment auf center setzen.
Fazit: es muss irgendeine Methode geben, die sich die Gesamtbreite meines Labels und die Gesamtbreite meines Textes berechnet.
Um den Text dann mittig zu platzieren muss die Methode dann Gesamtbreite_Label / 2 - Gesamtbreite_Text / 2 rechnen.
Diese Methode suche ich! Ich will der Methode eine falsche Gesamtbreite_Label vortäuschen, damit der Text eben nicht mittig platziert!!!
Gibt es irgendwen der sowas schon mal gemacht hat oder weiß wie das geht?!

mfg, guni


----------



## slawaweis (31. Okt 2010)

```
label.setBorder(new EmptyBorder(0, 0, 0, 20));
```

EDIT: alternativ kannst Du auch getInsets() überschreiben.

Slawa


----------



## guni (3. Nov 2010)

hallo slawaweis,

vielen dank für deinen tipp.

mit getInsets() passierte leider gar nichts; allerdings hat setBorder zum gewünschten Ergebnis geführt.
Das einzige Problem, dass ich jetzt noch habe ist, dass mein Polygon beim Füllen mit Farbe irgendwie einen Grafikfehler zu haben scheint. Die Ränder gehen teilweise verloren (siehe Screenshot im Anhang).
Weiß irgendwer, woran das liegen könnte?!

danke, guni

PS.: hier der code:

```
package oracle.forms.sg;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;

class Arrow extends Polygon
{
	private static final long serialVersionUID = 1L;
	public static final int NOARROW = 1;
	public static final int LEFTARROW = 2;
	public static final int RIGHTARROW = 3;
	public static final int BOTHARROWS = 4;
	
	private int x;
	private int y;
	private int w;
	private int h;
	private double angle;
	private int ht;
	private EmptyBorder b;

	private Arrow()
	{	
		super();
	}
	
	public Arrow(int peekangle, Rectangle bounds, int mode) 
	{
		this();
		x = bounds.x;
		y = bounds.y;
		w = bounds.width;
		h = bounds.height;
		angle = Math.toRadians(peekangle);
		ht = (int) ((h/2) / Math.tan(angle/2));
		
		switch (mode)
		{
		case LEFTARROW:
			leftarrow();
			break;
		case RIGHTARROW:
			rightarrow();
			break;
		case BOTHARROWS:
			botharrows();
			break;
		default:
			noarrow();
			break;
		}
	}
	
	public EmptyBorder getBorder() 
	{
		if (b == null) {
			b = new EmptyBorder(0,0,0,0);
		}
		return b;
	}
	
	private void leftarrow()
	{
		addPoint(x, y);
		addPoint(w, y);
		addPoint(w, h);
		addPoint(x, h);
		addPoint(ht, h / 2);
		b = new EmptyBorder(0, ht, 0, 0);
	}
	
	private void rightarrow()
	{
		addPoint(x, y);
		addPoint(w - ht, y);
		addPoint(w, h / 2);
		addPoint(w - ht, h);
		addPoint(y, h);
		b = new EmptyBorder(0, 0, 0, ht);
	}
	
	private void botharrows()
	{
		addPoint(x, y);
		addPoint(w - ht, y);
		addPoint(w, h / 2);
		addPoint(w - ht, h);
		addPoint(x, h);
		addPoint(ht, h / 2);
		b = new EmptyBorder(0, ht, 0, ht);
	}
	
	private void noarrow()
	{
		addPoint(x, y);
		addPoint(w, y);
		addPoint(w, h);
		addPoint(x, h);
		b = new EmptyBorder(0, 0, 0, 0);
	}
}

class ArrowLabel extends JLabel
{
	private static final long serialVersionUID = 1L;
	private int mode = Arrow.NOARROW;
	
	public ArrowLabel(String text, int mode)
	{
		super(text);
		this.mode = mode;
	}
	
	public void paint( Graphics g ) 
	{
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		Arrow arrow = new Arrow(120, getBounds(), this.mode);
		setBorder(arrow.getBorder());
		g2.drawPolygon(arrow);
		g2.setPaint(Color.WHITE);
		g2.fillPolygon(arrow);
		super.paint(g);
	}
}


public class Freak 
{
	
	public static void main(String[] args) 
	{
		JFrame f = new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		ArrowLabel l = new ArrowLabel("Das ist ein Test", Arrow.BOTHARROWS);
		l.setHorizontalAlignment(SwingConstants.CENTER);
		l.setPreferredSize(new Dimension(400,100));

		f.getContentPane().add(l, BorderLayout.CENTER);
		f.pack(); 
		f.setVisible(true);
	}

}
```


----------



## slawaweis (3. Nov 2010)

1. Du solltest paintComponent überschreiben, nach dem Muster:


```
public void paintComponent(Graphics g)
 {
 //...

 super.paintComponent(g);
 }
```

2. Vertausche das drawPolygon und fillPolygon. Man sollte immer zuerst füllen und dann die Ränder zeichnen.

3. In Arrow sollte es eigentlich so aussehen:


```
x = 0;//bounds.x;
        y = 0;//bounds.y;
        w = bounds.width-1;
        h = bounds.height-1;
```

4. Weiterhin wäre es von Vorteil Arrow nicht bei jedem Zeichenvorgang in paint zu erzeugen, sondern nur einmal im Konstruktor.

5. setBorder in paint ist Böse!

Slawa


----------



## guni (4. Nov 2010)

Hallo Slawaweis,



> Du solltest paintComponent überschreiben


ja. werd mal versuchen ob das auch geht. 
Meine Aufgabenstellung ist ja im Prinzip, dass ich den Button später in Oracle Forms einbinde.
Alle Samples die ich diesbzgl. gefunden habe überschreiben die Paint-Methode; mal sehen, ob es mit paintComponent auch geht! Warum soll man die paint-Methode eigentlich nicht überschreiben?!



> Vertausche das drawPolygon und fillPolygon. Man sollte immer zuerst füllen und dann die Ränder zeichnen.


Cool! Das funktioniert. Danke für den Tipp!



> Weiterhin wäre es von Vorteil Arrow nicht bei jedem Zeichenvorgang in paint zu erzeugen, sondern nur einmal im Konstruktor.


Hmm. ja - das hatte ich probiert. Sieht dann so aus:


```
class ArrowLabel extends JLabel
{
	private static final long serialVersionUID = 1L;
	private int mode = Arrow.NOARROW;
	private Arrow arrow;
	
	public ArrowLabel(String text, int mode)
	{
		super(text);
		this.mode = mode;
		this.arrow = new Arrow(120, getBounds(), mode);
	}
		
	public void paintComponent( Graphics g )
	{
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		setBorder(arrow.getBorder());
		g2.setPaint(Color.WHITE);
		g2.fillPolygon(arrow);
		g2.setPaint(Color.BLACK);
		g2.drawPolygon(arrow);
		super.paintComponent(g);
	}
}
```

Das Problem dabei ist, dass ich dem Arrow bounds übergebe; im Konstruktor gibt getBounds() allerdings noch ein Rectangle mit 0,0,0,0 zurück. Ich hatte irgendwie den Eindruck, dass die Bounds erst in der Paint-Methode "herausgefunden" werden ...



> setBorder in paint ist Böse!


Wo muss ich es dann setzen?! Wie gesagt: ich hätte lieber die getInsets() überschrieben; aber das hat aus irgendeinem Grund nicht funktioniert!

mfg, guni


----------



## slawaweis (4. Nov 2010)

guni hat gesagt.:


> ja. werd mal versuchen ob das auch geht.
> Meine Aufgabenstellung ist ja im Prinzip, dass ich den Button später in Oracle Forms einbinde.
> Alle Samples die ich diesbzgl. gefunden habe überschreiben die Paint-Methode; mal sehen, ob es mit paintComponent auch geht! Warum soll man die paint-Methode eigentlich nicht überschreiben?!


weil paint in Swing eine Reihe anderer Funktionen aufruft, darunter paintComponent. So verlagert sich die eigentliche Zeichenlogik in Swing in paintComponent. Es hat was mit der Architektur zu tun. Wenn man rein AWT programmiert, muss man auch weiterhin paint überschreiben.



guni hat gesagt.:


> Das Problem dabei ist, dass ich dem Arrow bounds übergebe; im Konstruktor gibt getBounds() allerdings noch ein Rectangle mit 0,0,0,0 zurück. Ich hatte irgendwie den Eindruck, dass die Bounds erst in der Paint-Methode "herausgefunden" werden ...


der Bounds kann sich ständig ändern. Um das zu erfahren sollte man den ComponentListener benutzen:


```
public ArrowLabel(String text, int mode)
	{
		super(text);
		this.mode = mode;

  this.addComponentListener(new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e)
     {
     arrow = new Arrow(120, getBounds(), mode);
     setBorder(arrow.getBorder());
     }
   });

	}
```



guni hat gesagt.:


> Wo muss ich es dann setzen?! Wie gesagt: ich hätte lieber die getInsets() überschrieben; aber das hat aus irgendeinem Grund nicht funktioniert!


bei mir funktioniert es:


```
@Override
  public Insets getInsets()
   {
   Insets ins = super.getInsets();
   if(ins == null)
     ins = new Insets(0, 0, 0, 0);
   ins.right += 50;
   return ins;
   }

  @Override
  public Insets getInsets(Insets insets)
   {
   Insets ins = super.getInsets(insets);
   if(ins == null)
     ins = new Insets(0, 0, 0, 0);
   ins.right += 50;
   return ins;
   }
```

Slawa


----------



## guni (8. Nov 2010)

Hallo Slawa,

mit deiner Unterstützung habe ich die Polygone nun doch noch zusammengebracht.
Danke!

mfg, guni


----------

