2012-03-09 3 views
4

В нашем приложении с JTabbedPane с неограниченными вкладками, когда ширина вкладок превышает ширину панели вкладок, вкладки начинают обертывание в несколько строк. Когда вы затем щелкаете по вкладке в одной из верхних строк, полная строка спускается вниз и на передний план. Для пользователей, которые посещают несколько вкладок, это очень запутывает, так как невозможно отслеживать порядок вкладок.Вкладки в фиксированных положениях в JTabbedPane или в одной строке

Как я могу либо - гвоздь язычков на фиксированные позиции, принося их содержимое на фронт (хотя бы оптический коррумпированной вкладки метафора, но я не забочусь), или - ограничить количество строк в одну (так что вкладки становятся очень узкими, а не обертыванием)?

+0

Как насчет переключения на другой тип управления? как список или дерево? – Randy

+0

'setTabLayoutPolicy()' может быть тем, что вы ищете –

+0

@Guillaume, setTabLayoutPolicy() имеет только два значения: прокрутка или обертка, оба не хороши здесь. – micro

ответ

3

Действительно быстрый и грязный (безусловно, нуждается в улучшений и изменений), но я предположил бы, что-то подобное, что может работать для вас (но не JTabbePane):

import java.awt.BorderLayout; 
import java.awt.Component; 
import java.awt.Container; 
import java.awt.Dimension; 
import java.awt.FlowLayout; 
import java.awt.Insets; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.ArrayList; 
import java.util.List; 

import javax.swing.JButton; 
import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.SwingUtilities; 

public class Test { 

    /** 
    * FlowLayout subclass that fully supports wrapping of components. 
    */ 
    public static class WrapLayout extends FlowLayout { 
     private Dimension preferredLayoutSize; 

     /** 
     * Constructs a new <code>WrapLayout</code> with a left alignment and a 
     * default 5-unit horizontal and vertical gap. 
     */ 
     public WrapLayout() { 
      super(); 
     } 

     /** 
     * Constructs a new <code>FlowLayout</code> with the specified alignment 
     * and a default 5-unit horizontal and vertical gap. The value of the 
     * alignment argument must be one of <code>WrapLayout</code>, 
     * <code>WrapLayout</code>, or <code>WrapLayout</code>. 
     * 
     * @param align 
     *   the alignment value 
     */ 
     public WrapLayout(int align) { 
      super(align); 
     } 

     /** 
     * Creates a new flow layout manager with the indicated alignment and 
     * the indicated horizontal and vertical gaps. 
     * <p> 
     * The value of the alignment argument must be one of 
     * <code>WrapLayout</code>, <code>WrapLayout</code>, or 
     * <code>WrapLayout</code>. 
     * 
     * @param align 
     *   the alignment value 
     * @param hgap 
     *   the horizontal gap between components 
     * @param vgap 
     *   the vertical gap between components 
     */ 
     public WrapLayout(int align, int hgap, int vgap) { 
      super(align, hgap, vgap); 
     } 

     /** 
     * Returns the preferred dimensions for this layout given the 
     * <i>visible</i> components in the specified target container. 
     * 
     * @param target 
     *   the component which needs to be laid out 
     * @return the preferred dimensions to lay out the subcomponents of the 
     *   specified container 
     */ 
     @Override 
     public Dimension preferredLayoutSize(Container target) { 
      return layoutSize(target, true); 
     } 

     /** 
     * Returns the minimum dimensions needed to layout the <i>visible</i> 
     * components contained in the specified target container. 
     * 
     * @param target 
     *   the component which needs to be laid out 
     * @return the minimum dimensions to lay out the subcomponents of the 
     *   specified container 
     */ 
     @Override 
     public Dimension minimumLayoutSize(Container target) { 
      Dimension minimum = layoutSize(target, false); 
      minimum.width -= getHgap() + 1; 
      return minimum; 
     } 

     /** 
     * Returns the minimum or preferred dimension needed to layout the 
     * target container. 
     * 
     * @param target 
     *   target to get layout size for 
     * @param preferred 
     *   should preferred size be calculated 
     * @return the dimension to layout the target container 
     */ 
     private Dimension layoutSize(Container target, boolean preferred) { 
      synchronized (target.getTreeLock()) { 
       // Each row must fit with the width allocated to the containter. 
       // When the container width = 0, the preferred width of the 
       // container 
       // has not yet been calculated so lets ask for the maximum. 

       int targetWidth = target.getSize().width; 

       if (targetWidth == 0) { 
        targetWidth = Integer.MAX_VALUE; 
       } 

       int hgap = getHgap(); 
       int vgap = getVgap(); 
       Insets insets = target.getInsets(); 
       int horizontalInsetsAndGap = insets.left + insets.right + hgap * 2; 
       int maxWidth = targetWidth - horizontalInsetsAndGap; 

       // Fit components into the allowed width 

       Dimension dim = new Dimension(0, 0); 
       int rowWidth = 0; 
       int rowHeight = 0; 

       int nmembers = target.getComponentCount(); 

       for (int i = 0; i < nmembers; i++) { 
        Component m = target.getComponent(i); 

        if (m.isVisible()) { 
         Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); 

         // Can't add the component to current row. Start a new 
         // row. 

         if (rowWidth + d.width > maxWidth) { 
          addRow(dim, rowWidth, rowHeight); 
          rowWidth = 0; 
          rowHeight = 0; 
         } 

         // Add a horizontal gap for all components after the 
         // first 

         if (rowWidth != 0) { 
          rowWidth += hgap; 
         } 

         rowWidth += d.width; 
         rowHeight = Math.max(rowHeight, d.height); 
        } 
       } 

       addRow(dim, rowWidth, rowHeight); 

       dim.width += horizontalInsetsAndGap; 
       dim.height += insets.top + insets.bottom + vgap * 2; 

       // When using a scroll pane or the DecoratedLookAndFeel we need 
       // to 
       // make sure the preferred size is less than the size of the 
       // target containter so shrinking the container size works 
       // correctly. Removing the horizontal gap is an easy way to do 
       // this. 

       Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); 

       if (scrollPane != null) { 
        dim.width -= hgap + 1; 
       } 

       return dim; 
      } 
     } 

     /* 
     * A new row has been completed. Use the dimensions of this row 
     * to update the preferred size for the container. 
     * 
     * @param dim update the width and height when appropriate 
     * @param rowWidth the width of the row to add 
     * @param rowHeight the height of the row to add 
     */ 
     private void addRow(Dimension dim, int rowWidth, int rowHeight) { 
      dim.width = Math.max(dim.width, rowWidth); 

      if (dim.height > 0) { 
       dim.height += getVgap(); 
      } 

      dim.height += rowHeight; 
     } 
    } 

    public static class MyTabbedPane extends JPanel { 
     private JPanel buttonPanel; 
     private JPanel currentview; 

     private Tab currentTab; 

     private class Tab { 
      String name; 
      JComponent component; 
     } 

     private List<Tab> tabs = new ArrayList<Tab>(); 

     public MyTabbedPane() { 
      super(new BorderLayout()); 
      buttonPanel = new JPanel(new WrapLayout()); 
      currentview = new JPanel(); 
      add(buttonPanel, BorderLayout.NORTH); 
      add(currentview); 
     } 

     public void addTab(String name, JComponent tabView, int index) { 
      if (index < 0 || index > tabs.size()) { 
       throw new IllegalArgumentException("Index out of bounds"); 
      } 
      final Tab tab = new Tab(); 
      tab.component = tabView; 
      tab.name = name; 
      tabs.add(index, tab); 
      JButton b = new JButton(name); 
      b.addActionListener(new ActionListener() { 

       @Override 
       public void actionPerformed(ActionEvent e) { 
        setCurrentTab(tab); 
       } 

      }); 
      buttonPanel.add(b, index); 
      buttonPanel.validate(); 
     } 

     public void removeTab(int i) { 
      Tab tab = tabs.remove(i); 

      if (tab == currentTab) { 
       if (tabs.size() > 0) { 
        if (i < tabs.size()) { 
         setCurrentTab(tabs.get(i)); 
        } else { 
         setCurrentTab(tabs.get(i - 1)); 
        } 
       } else { 
        setCurrentTab(null); 
       } 
      } 
      buttonPanel.remove(index); 
     } 

     void setCurrentTab(final Tab tab) { 
      if (currentTab == tab) { 
       return; 
      } 
      if (currentTab != null) { 
       currentview.remove(currentTab.component); 
      } 
      if (tab != null) { 
       currentview.add(tab.component); 
      } 
      currentTab = tab; 
      currentview.validate(); 
     } 
    } 

    public static void main(String[] args) { 
     JFrame frame = new JFrame(); 
     MyTabbedPane tabbedPane = new MyTabbedPane(); 
     for (int i = 0; i < 100; i++) { 
      tabbedPane.addTab("Button " + (i + 1), new JLabel("Dummy Label " + (i + 1)), i); 
     } 
     frame.add(tabbedPane); 
     frame.pack(); 
     frame.setSize(new Dimension(1000, 800)); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 

} 

В WrapLayout был взят из другого поста на SO.

+0

+1 Я видел здесь кучу JButtons в JScrollPane – mKorbel

+0

Хороший ответ, предположительно, нет простого способа изменить JTabbedPane. Благодаря! – micro

+0

Если вы действительно хотите, вы можете расширить JTabbedPane и переопределить каждый метод в JTabbedPane (их не так много) и сопоставить их с приведенными выше. –