2012-11-12 12 views
6

Un JLabel contenente testo HTML avvolge automaticamente le linee utilizzando lo spazio disponibile. Se si aggiunge JLabel a un JSrollPane, deve impostare il valore preferito su un valore decente altrimenti non si concluderà. Tutto questo dovrebbe funzionare bene insieme ad altri componenti all'interno di un JPanel usando un LayoutManager.JLabel con testo HTML avvolto come client JScrollPane

Causa Desidero una finestra di applicazione ridimensionabile Ho esteso JScrollPane per tenere traccia degli eventi di ridimensionamento e modificare dinamicamente la dimensione sincronizzata alla larghezza della finestra. Fondamentalmente funziona, ma a volte il calcolo dell'altezza preferita dal gestore di layout è sbagliato (valore troppo grande o troppo piccolo). Ad esempio, la visibilità del bordo rosso che taglia la prima riga indica che il calcolo dell'altezza è errato.

framegrabbing

non riesco a riprodurre l'errore con un unico JLabel avvolgimento.

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 buona domanda – mKorbel

+5

'// aggiungere un po 'di testo here' Uh , no grazie, lascerò che * tu * faccia. In effetti, per un aiuto migliore, pubblicare un [SSCCE] (http://sscce.org/). –

risposta

1

A quanto pare è sia un bug in GridBagLayout, o si utilizza il motore di layout in un modo totalmente inaspettato da parte degli sviluppatori. Diverse etichette multilinea con HTML all'interno, impostando la dimensione preferita e chiedendo immediatamente la dimensione preferita dalla porta sul retro? Ugh!

Ho notato che a volte il layout funziona in modo errato quando si riducono le dimensioni della finestra: il pannello all'interno dello scorrimento non diminuisce e appare la barra di scorrimento orizzontale. (Sto usando Windows tra l'altro).

when decreasing

Inoltre, a volte, se la barra di scorrimento verticale era visibile e l'altezza del pannello era grande, e quindi aumentare la dimensione della finestra, l'altezza del pannello rimane irragionevolmente grande e lacune appaiono attorno all'etichetta:

enter image description here

Per me, il layout è sbagliato ogni volta che diminuisco la finestra; aumentando funziona meglio, ma se va male, è anche errato ogni altra volta. Ho provato il debug e la stampa dei valori sulla console; sembra che view.getLayout().preferredLayoutSize(view) dipenda non solo da view.setPreferredSize ma anche dalle dimensioni correnti del pannello e dello scorrimento. Il codice di GridBagLayout è troppo complicato per entrare.

DIRTY HACK

Dal momento che ogni altro ridimensionamento produce il risultato corretto, perché non ridimensionare due volte? La duplicazione di elementi nel gestore ScrollPane.componentResized non ha avuto esito positivo, probabilmente perché le dimensioni di ScrollPane rimangono invariate. Lo ScrollPane stesso deve essere ridimensionato due volte, con valori diversi. Per testarlo nel modo più semplice, ho creato una sottoclasse di JFrame: ascolta componentResized e ridimensiona due volte la sua finestra figlio. Il secondo ridimensionamento deve essere differito tramite SwingUtilities.invokeLater.

sostituire le linee

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

da

final MyFrame frame = new MyFrame(scroll); 

e aggiungere la seguente classe:

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(); 
     } 
    } 

} 

Lo stesso può essere raggiunto da sottoclasse un gestore di layout.

Ovviamente, questo approccio è poco elegante e inefficiente, ma come una soluzione per un bug JRE, e se non altro aiuta ... ;-)

Problemi correlati