2012-11-12 7 views
6

Ein JLabel mit HTML-Text umschließt Linien automatisch mit dem verfügbaren Platz. Wenn man JLabel zu einem JSrollPane hinzufügt, muss er preferredSize auf einen vernünftigen Wert setzen, sonst wird es nicht umbrochen. All dies sollte mit anderen Komponenten in einem JPanel funktionieren, die einen LayoutManager verwenden.JLabel mit eingebundenem HTML-Text als JScrollPane-Client

Ursache Ich möchte ein in der Größe veränderbares Anwendungsfenster Ich erweiterte JScrollPane, um die Größenänderungsereignisse zu verfolgen und die Größe synchron zur Breite des Ansichtsfensters zu ändern. Grundsätzlich funktioniert es, aber manchmal ist die Berechnung der bevorzugten Höhe durch den Layout-Manager falsch (Wert zu groß oder zu klein). Zum Beispiel zeigt die Sichtbarkeit des roten Randes, der durch die erste Linie schneidet, an, dass die Berechnung der Höhe falsch ist.

framegrabbing

ich den Fehler mit einer einzigen Verpackung JLabel nicht wiedergeben kann.

import java.awt.Color; 
import java.awt.Container; 
import java.awt.Dimension; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.event.ComponentEvent; 
import java.awt.event.ComponentListener; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.SwingUtilities; 

public class WrappedLabel implements Runnable { 

    public static void main(String[] args){ 
     SwingUtilities.invokeLater(new WrappedLabel()); 
    } 

    @Override 
    public void run(){ 
     final JPanel panel = new JPanel(new GridBagLayout()); 
     final GridBagConstraints gc = new GridBagConstraints(); 
     gc.fill = GridBagConstraints.BOTH; 
     gc.weightx = 1.0; 
     gc.weighty = 1.0; 
     { 
      gc.gridx = 0; 
      gc.gridy = 0; 
      final JLabel label = new JLabel(
       "<html>" + "please add some more text here" 
      ); 
      label.setBorder(BorderFactory.createLineBorder(Color.RED)); 
      panel.add(label, gc); 
     } 
     { 
      gc.gridx = 0; 
      gc.gridy = 1; 
      final JLabel label = new JLabel(
       "<html>" + "please add some more text here" 
      ); 
      label.setBorder(BorderFactory.createLineBorder(Color.RED)); 
      panel.add(label, gc); 
     } 
     final JFrame frame = new JFrame(); 
     frame.add(new ScrollPane(panel)); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.setSize(256, 256); 
     frame.setVisible(true); 
    } 

    private class ScrollPane extends JScrollPane implements ComponentListener { 

     ScrollPane(Container view){ 
      super(view); 
      this.viewport.addComponentListener(this); 
     } 

     @Override 
     public void componentHidden(ComponentEvent ce){ 
     } 

     @Override 
     public void componentMoved(ComponentEvent ce){ 
     } 

     /** calculating required height is a 3 step process 
      * 1. sync width of client and viewport, set height of client to high value 
      * 2. let GridbagManager calculate required minimum size 
      * 3. set preferredSize and revalidate 
     **/ 
     @Override 
     public void componentResized(ComponentEvent ce){ 
      assert(this.viewport == ce.getSource()); 
      final Container view = (Container) this.viewport.getView(); 
      final int width = this.viewport.getExtentSize().width; 
      view.setPreferredSize(new Dimension(width, Integer.MAX_VALUE)); 
      final int height = view.getLayout().preferredLayoutSize(view).height; 
      view.setPreferredSize(new Dimension(width, height)); 
      view.revalidate(); 
     } 

     @Override 
     public void componentShown(ComponentEvent ce){ 
     } 

    } 

} 
+0

+1 gute Frage – mKorbel

+5

'// fügen Sie etwas Text hier' Uh Nein, danke, das lasse ich für dich. In der Tat, für eine bessere Hilfe, post ein [SSCCE] (http://sscce.org/). –

Antwort

1

Anscheinend ist es entweder um einen Fehler in GridBagLayout, oder Sie verwenden die Layout-Engine in einer Art und Weise völlig unerwartet von den Entwicklern. Mehrere mehrzeilige Etiketten mit HTML im Inneren, bevorzugte Größe und sofortige Nachfrage bevorzugte Größe von der Hintertür? Pfui!

Mir ist aufgefallen, dass das Layout manchmal nicht korrekt funktioniert, wenn die Fenstergröße verkleinert wird: Das Fenster in der Bildlaufleiste wird nicht verkleinert und die horizontale Bildlaufleiste erscheint. (Ich benutze übrigens Windows).

when decreasing

auch manchmal, wenn die vertikale Bildlaufleiste sichtbar war und die Plattenhöhe war groß, und dann erhöhe ich die Fenstergröße bleibt die Wandhöhe unverhältnismäßig groß und Lücken erscheinen um das Label:

enter image description here

Für mich ist das Layout falsch jedes andere Mal, wenn ich das Fenster verkleinern; Erhöhen funktioniert besser, aber wenn es schief geht, ist es auch jedes andere Mal falsch. Ich habe versucht, Werte zu debuggen und zu drucken. es scheint, dass view.getLayout().preferredLayoutSize(view) hängt nicht nur von view.setPreferredSize sondern auch von der aktuellen Größe des Panels und Scrollpane. Der Code von GridBagLayout ist zu kompliziert, um darin zu tauchen.

DIRTY HACK

Da jeder andere Resize das richtige Ergebnis liefert, warum es nicht zweimal die Größe? Duplikate im ScrollPane.componentResized Handler waren nicht erfolgreich, wahrscheinlich weil die Größe des ScrollPanes gleich bleibt. Das ScrollPane selbst muss zweimal mit unterschiedlichen Werten skaliert werden. Um es auf die einfachste Weise zu testen, habe ich JFrame unterklassifiziert: Es hört componentResized und ändert sein unteres Fenster zweimal. Die zweite Größenänderung muss über SwingUtilities.invokeLater zurückgestellt werden.

Ersetzen Sie die Linien

final JFrame frame = new JFrame(); 
frame.add(scroll); 

von

final MyFrame frame = new MyFrame(scroll); 

und die folgende Klasse hinzu:

private class MyFrame extends JFrame implements ComponentListener { 

    private Component child; 

    public MyFrame(Component child){ 
     this.child=child; 
     setLayout(null); 
     getContentPane().add(child); 
     addComponentListener(this); 
    } 

    public void componentResized(ComponentEvent e) { 
     Dimension size=getContentPane().getSize(); 
     child.setSize(new Dimension(size.width-1,size.height)); 
     validate(); 
     SwingUtilities.invokeLater(new ResizeRunner(size)); 
    } 

    public void componentMoved(ComponentEvent e) {} 
    public void componentShown(ComponentEvent e) {} 
    public void componentHidden(ComponentEvent e) {} 

    private class ResizeRunner implements Runnable { 
     private Dimension size; 
     public ResizeRunner(Dimension size){ 
      this.size=size; 
     } 
     public void run() { 
      child.setSize(size); 
      validate(); 
     } 
    } 

} 

Das gleiche kann durch Subklassen ein Layout-Manager erreicht werden.

Offensichtlich ist dieser Ansatz ist unelegant und ineffizient, sondern als Behelfslösung für ein JRE Fehler, und wenn nichts anderes hilft ... ;-)