2011-09-05 30 views
12

我已經讀/嘗試了這些職位,但沒有幫助:多行文本和動態高度的Swing JList的

我需要的是一個ListCellRenderer,它返回一個面板,左側有一個圖標,右側有一個動態長度的文本(如在任何論壇中:左側是用戶頭像,右側是t他發佈文字)。文本是不是我知道,所以我不能設置一個固定的細胞高度。此外,文本長度不同於列表單元格到列表單元格。因此,根據文本的長度,每個列表單元格都需要自己的高度。其實是一個非常常見的佈局......但不適合Swing。單元格高度根據文本長度不會擴展。

我已經在JList幾乎讀過關於動態單元格高度和多行文本的任何文章,但找不到解決方案。所以我決定給一個小小的SSCCE。請給我一個關於如何實現我所描述的內容的提示,或者如果您認爲它很簡單,請修復我的代碼。

感謝

以下是部份SSCCE:

public class MultiLineList extends JFrame 
{ 

    private static final long serialVersionUID = 1L; 

    public static void main(final String[] args) 
    { 
     new MultiLineList(); 
    } 

    private MultiLineList() 
    { 
     setTitle("MultiLineList"); 
     setSize(800, 450); 
     setResizable(true); 
     setVisible(true); 
     setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     this.getContentPane().setLayout(new BorderLayout()); 

     final DefaultListModel model = new DefaultListModel(); 
     model.addElement("This is a short text"); 
     model.addElement("This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. "); 
     model.addElement("This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. "); 

     final JList list = new JList(model); 
     list.setCellRenderer(new MyCellRenderer()); 

     this.add(list); 

     this.getContentPane().invalidate(); 
     this.getContentPane().validate(); 

    } 

    public class MyCellRenderer extends DefaultListCellRenderer 
    { 
     @Override 
     public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus) 
     { 

      final String text = (String) value; 

      //create panel 
      final JPanel p = new JPanel(); 
      p.setLayout(new BorderLayout()); 

      //icon 
      final JPanel IconPanel = new JPanel(new BorderLayout()); 
      final JLabel l = new JLabel("icon"); //<-- this will be an icon instead of a text 
      IconPanel.add(l, BorderLayout.NORTH); 
      p.add(IconPanel, BorderLayout.WEST); 

      //text 
      final JTextArea ta = new JTextArea(); 
      ta.setText(text); 
      ta.setLineWrap(true); 
      ta.setWrapStyleWord(true); 
      p.add(ta, BorderLayout.CENTER); 

      return p; 

     } 
    } 

} 
+0

與手頭問題無關:a)neve如果new不滿足is-a要求,則不要使用_extend_類 – kleopatra

+1

請注意,SSCCE應包含導入。並不是所有人都可以使用automagic IDE來搞清楚它們! –

+0

我會記住 – haferblues

回答

15

編輯1:哎呀 - 看到@安德魯的截圖,實現如預期,這不工作,文本實際上比用這個顯示的時間要長(忽略了內部註釋「PENDING:不適用於JScrollPane中的JList」; - )如果我不能很快開始工作,會挖掘一下並刪除這個答案。

編輯2:明白了 - 如下所示的渲染器實現是可以的,罪魁禍首是JList偶爾少於優化大小的緩存。有兩個部分

  • BasicListUI不會考慮到調整的名單可能需要清除內部尺寸(實際上行高)高速緩存,應用程序代碼必須迫使它這樣做,f.i.在一個ComponentListener中
  • list的Scrollable實現的tracksViewportWidth包含了一種阻礙它的邏輯(導致該區域循環展開直到它是一行),子類返回true。

    final JList list = new JList(model) { 
    
         /** 
         * @inherited <p> 
         */ 
         @Override 
         public boolean getScrollableTracksViewportWidth() { 
          return true; 
         } 
    
    
        }; 
        list.setCellRenderer(new MyCellRenderer()); 
    
        ComponentListener l = new ComponentAdapter() { 
    
         @Override 
         public void componentResized(ComponentEvent e) { 
          // next line possible if list is of type JXList 
          // list.invalidateCellSizeCache(); 
          // for core: force cache invalidation by temporarily setting fixed height 
          list.setFixedCellHeight(10); 
          list.setFixedCellHeight(-1); 
         } 
    
        }; 
    
        list.addComponentListener(l); 
        add(new JScrollPane(list)); 
    

    先回答

    文本區是在大小有點棘手(它使用的JTextArea渲染組件渲染器實現):使用下面的渲染

代碼,它需要得到初始化爲合理的東西:

public class MyCellRenderer implements ListCellRenderer { 

    private JPanel p; 
    private JPanel iconPanel; 
    private JLabel l; 
    private JTextArea ta; 

    public MyCellRenderer() { 
     p = new JPanel(); 
     p.setLayout(new BorderLayout()); 

     // icon 
     iconPanel = new JPanel(new BorderLayout()); 
     l = new JLabel("icon"); // <-- this will be an icon instead of a 
           // text 
     iconPanel.add(l, BorderLayout.NORTH); 
     p.add(iconPanel, BorderLayout.WEST); 

     // text 
     ta = new JTextArea(); 
     ta.setLineWrap(true); 
     ta.setWrapStyleWord(true); 
     p.add(ta, BorderLayout.CENTER); 
    } 

    @Override 
    public Component getListCellRendererComponent(final JList list, 
      final Object value, final int index, final boolean isSelected, 
      final boolean hasFocus) { 

     ta.setText((String) value); 
     int width = list.getWidth(); 
     // this is just to lure the ta's internal sizing mechanism into action 
     if (width > 0) 
      ta.setSize(width, Short.MAX_VALUE); 
     return p; 

    } 
} 
+0

這實際上是完美的。我只是通過int width = list.getWidth()來替換int width = list.getWidth() - l.getIcon()。getIconWidth();否則,計算textarea的高度,就好像列表的總寬度可用。非常感謝。今天我也學到了很多東西。 – haferblues

+0

@haferblues - 好的調整到其他寬度,謝謝:-) – kleopatra

+0

@kleopatra - 不錯的修復!我正在把我的頭髮撕掉! –

11

Multi-Line List

import java.awt.*; 
import javax.swing.*; 

public class MultiLineList 
{ 
    private static final long serialVersionUID = 1L; 

    public static void main(final String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       new MultiLineList(); 
      } 
     }); 
    } 

    private MultiLineList() 
    { 
     JFrame f = new JFrame("MultiLineList"); 
     f.setResizable(true); 
     f.setVisible(true); 
     f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     f.getContentPane().setLayout(new BorderLayout()); 

     final DefaultListModel model = new DefaultListModel(); 
     model.addElement("This is a short text"); 
     model.addElement("This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. This is a long text. "); 
     model.addElement("This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. This is an even longer text. "); 

     final JList list = new JList(model); 
     list.setCellRenderer(new MyCellRenderer()); 

     f.add(list); 

     f.pack(); 
    } 

    public class MyCellRenderer extends DefaultListCellRenderer 
    { 
     final JPanel p = new JPanel(new BorderLayout()); 
     final JPanel IconPanel = new JPanel(new BorderLayout()); 
     final JLabel l = new JLabel("icon"); //<-- this will be an icon instead of a text 
     final JLabel lt = new JLabel(); 
     String pre = "<html><body style='width: 200px;'>"; 

     MyCellRenderer() { 
      //icon 
      IconPanel.add(l, BorderLayout.NORTH); 
      p.add(IconPanel, BorderLayout.WEST); 

      p.add(lt, BorderLayout.CENTER); 
      //text 
     } 

     @Override 
     public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus) 
     { 
      final String text = (String) value; 
      lt.setText(pre + text); 

      return p; 
     } 
    } 
} 
+4

darn ..再次打了一個_with_截圖:-) – kleopatra

+0

你忙於給出關於不在渲染器中創建組件的好建議。 ;) –

+0

http://stackoverflow.com/questions/6901153/expand-jlist-row-height-depending-on-content/6902589#6902589,但都+1 – mKorbel