2012-11-12 14 views
6

JLabel zawierający tekst HTML automatycznie owija linie przy użyciu dostępnego miejsca. Jeśli dodać JLabel do JSrollPane, musi ustawić preferowaną wartość na przyzwoitą wartość, w przeciwnym razie nie zostanie ona opakowana. Wszystko to powinno działać dobrze razem z innymi komponentami wewnątrz JPanel za pomocą LayoutManager.JLabel z owiniętym tekstem HTML jako klient JScrollPane

Przyczyna Chcę, aby okno aplikacji z możliwością zmiany rozmiaru Rozszerzyłem JScrollPane, aby śledzić zmiany rozmiaru zdarzeń i dynamicznie zmieniać rozmiar zsynchronizowany z szerokością rzutni. Zasadniczo działa, ale czasami obliczenie preferowanej wysokości przez menedżera układu jest nieprawidłowe (wartość za duża lub za mała). Na przykład widoczność czerwonej granicy przecinającej pierwszą linię wskazuje, że obliczenie wysokości jest błędne.

framegrabbing

Nie mogę odtworzyć błąd z jednym owijania JLabel.

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 dobre pytanie – mKorbel

+5

'// dodać here' tekstu Uh , nie, dziękuję, zostawię to dla * ciebie * do zrobienia. W rzeczywistości, aby uzyskać lepszą pomoc wcześniej, opublikuj [SSCCE] (http://sscce.org/). –

Odpowiedz

1

Widocznie to albo błąd w GridBagLayout lub używasz silnika układ w sposób całkowicie nieoczekiwany przez deweloperów. Kilka wielowierszowych etykiet z wewnątrz HTML, ustawienie preferowanego rozmiaru i natychmiastowe zapytanie o preferowany rozmiar przez tylne drzwi? Ugh!

Zauważyłem, że czasami układ działa nieprawidłowo podczas zmniejszania rozmiaru okna: panel wewnątrz scrollpane nie zmniejsza się i pojawia się poziomy pasek przewijania. (Używam Windowsa nawiasem mówiąc).

when decreasing

Ponadto, niekiedy, jeżeli pionowa przewijania było widoczne, zaś wysokość panelu była duża, a następnie zwiększyć rozmiar okna wysokość szyba pozostaje nieracjonalnie duże i braki pojawiają się wokół wytwórni:

enter image description here

Dla mnie układ jest nieprawidłowy co drugi raz, gdy zmniejszam okno; Zwiększanie działa lepiej, ale jeśli idzie źle, jest to również nieprawidłowe co drugi raz. Próbowałem debugowania i drukowania wartości do konsoli; wydaje się, że view.getLayout().preferredLayoutSize(view) zależy nie tylko od view.setPreferredSize, ale także od bieżącego rozmiaru panelu i suwaka. Kod GridBagLayout jest zbyt skomplikowany, aby się zanurkować.

DIRTY HACK

Ponieważ każda inna zmiana rozmiaru daje poprawny wynik, to dlaczego nie zmienić jego rozmiar dwukrotnie? Powielanie rzeczy w obsłudze ScrollPane.componentResized nie powiodło się, prawdopodobnie dlatego, że rozmiar ScrollPane pozostaje taki sam. Sam ScrollPane musi być dwukrotnie zmieniany, z różnymi wartościami. Aby przetestować go w najprostszy sposób, podklasowałem JFrame: słucha ono componentResized i dwukrotnie zmienia jego okno potomne. Druga zmiana rozmiaru musi zostać odroczona poprzez SwingUtilities.invokeLater.

Wymień linie

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

przez

final MyFrame frame = new MyFrame(scroll); 

i dodać następujące klasy:

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

} 

To samo można osiągnąć przez instacji menedżera układu.

Oczywiście, takie podejście jest nieeleganckie i nieefektywne, ale jako obejście błędu JRE, a jeśli nic innego nie pomaga ... ;-)