2012-11-12 23 views
6

包含HTML文本的JLabel會使用可用空間自動換行。如果將JLabel添加到一個JSrollPane,他必須將preferredSize設置爲一個合適的值,否則它不會換行。所有這些都可以使用LayoutManager在JPanel內部的其他組件上正常工作。帶有包裝HTML文本作爲JScrollPane客戶端的JLabel

原因我想要一個可調整大小的應用程序窗口我擴展了JScrollPane以跟蹤調整大小事件並動態更改同步到視口寬度的大小。基本上它可以工作,但有時佈局管理器計算首選高度是錯誤的(值太大或太小)。例如,通過第一行切割的紅色邊框的可見性表示計算高度是錯誤的。

framegrabbing

我無法重現與單個包裝的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很好的問題 – mKorbel

+5

'//添加一些文字here'呃,不,謝謝,我會留下那*你*做的。事實上,爲了更快提供更好的幫助,請發佈[SSCCE](http://sscce.org/)。 –

回答

1

顯然,它要麼在GridBagLayout的一個bug,或者你正在使用的佈局引擎由開發商完全意想不到的方式。幾個帶有HTML的多行標籤,設置首選大小並立即通過後門詢問首選大小?啊!

我注意到,有時佈局工作不正確,當減少窗口大小:滾動面板內的面板不會減少和水平滾動條出現。 (我正在使用Windows)。

when decreasing

此外,有時,如果垂直滾動條是可見的,並且面板的高度很大,然後我增加窗口大小,面板高度保持不合理的大和差距周圍出現標籤:

enter image description here

對我來說,當我減少窗口的時候,佈局是錯誤的;增加效果會更好,但如果出現問題,那麼每隔一段時間都會出現錯誤。我嘗試調試和打印值到控制檯;看起來view.getLayout().preferredLayoutSize(view)不僅取決於view.setPreferredSize,還取決於面板和滾動條的當前大小。 GridBagLayout的代碼太複雜,無法深入。

骯髒的黑客

由於所有其他的調整大小產生正確的結果,爲什麼不調整它的兩倍?在ScrollPane.componentResized處理程序中複製事件不成功,可能是因爲ScrollPane的大小保持不變。 ScrollPane本身需要調整兩次,使用不同的值。爲了以最簡單的方式測試它,我分類了JFrame:它會監聽componentResized並將其子窗口調整兩次。第二次調整大小必須通過SwingUtilities.invokeLater推遲。

通過

final MyFrame frame = new MyFrame(scroll); 

更換線

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

並添加以下類:

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

} 

同樣可以通過繼承佈局管理器來實現。

顯然,這種方法是不雅和低效的,但作爲一個JRE錯誤解決方法,如果沒有別的幫助... ;-)