2011-08-21 35 views
28

JTable को Boolean.class के कॉलम के साथ देखते हुए default rendererJCheckBox है। user selection पर आधारित व्यक्तिगत कक्षों का चयन करना काफी आसान है, लेकिन यह सभी या किसी भी चेक बॉक्स का चयन करना सुविधाजनक हो सकता है। ये recentexamples तालिका शीर्षलेख में JCheckBox का उपयोग करके उल्लेख किया गया है, लेकिन कार्यान्वयन अजीब और अपरिहार्य था। अगर मुझे कॉलम को सॉर्ट करने की आवश्यकता नहीं है, तो मैं JTableHeader में एक अच्छी तरह से व्यवहार कैसे कर सकता हूं? मेरी sscce एक answer के रूप में है, लेकिन मैं एक जवाब है कि अच्छी तरह से व्यवहार समस्या के पहलू के पते को स्वीकार करने की कृपा होगी सुविधा के लिए, मैं जोड़ दिया है:मैं जेटीबल के जेटीबलहेडर में नियंत्रण कैसे रख सकता हूं?

परिशिष्ट।

+0

हम्म ... वास्तव में क्या सवाल है, विशेष रूप से, क्या आप अच्छी तरह से व्यवहार मतलब है है? हम सभी जानते हैं कि शीर्षलेख में "लाइव" घटकों के लिए कोई समर्थन नहीं है, सभी को स्वयं द्वारा किया जाना चाहिए :-) टॉगल बटन का उपयोग करने के लिए, यह नहीं पता कि उपयोगकर्ता समझते हैं कि यह क्या करता है और कब - गलती से क्लिक करना सेल कॉलम – kleopatra

+0

@ क्लोपेट्रा में सभी मौजूदा डेटा खो देगा: अच्छा बिंदु; मैं अनुमान लगाता हूं कि किसी को इस तरह के डिवाइस की आवश्यकता पर सवाल उठाना चाहिए। – trashgod

उत्तर

14

समस्या के दो हिस्सों रहे हैं (के रूप में मैं इसे देखना :-)

प्रयोज्य: की खोज करने यूआई-बातचीत/तत्वों भ्रामक उपयोगकर्ताओं की संभावना है। (मेरे लिए

  • स्तंभ शीर्ष लेख शीर्षक स्तंभ की सामग्री का वर्णन करने के लिए है, तो वह सामग्री वर्णन खो दिया है जब एक कार्रवाई विवरण
  • इसकी जगह में इसे तुरंत नहीं है, बेवकूफ: किसी विशेष क्रम में पृथ्वी पर उपयोगकर्ता :-) स्पष्ट करें कि हेडर सेल में टॉगल बटन का कार्य है। गलती से क्लिक करने से वह उस कॉलम में पिछले सभी सामग्री राज्य खो देंगे बातचीत विश्लेषण एक स्पष्ट हम करते हैं-की जरूरत/चाहते हैं-इसके साथ बाहर आता है

इसलिए भले ही, केवल इन-इसके अलावा करने के लिए

  • कार्रवाई सामग्री
  • एक विजेट का उपयोग करें जो स्पष्ट है (उदाहरण के लिए एक त्रि-राज्य चेकबॉक्स ऑल-डी-/चयनित, मिश्रित सामग्री)। इसके अलावा, मिश्रित सामग्री से डी/चयन दोनों संभव होना चाहिए। दूसरे विचार पर, शायद चेकबॉक्स शायद सबसे अच्छा विकल्प नहीं है,
  • गलती से संभावना को कम करें (केवल मेरे लिए :-) थोक स्थिति बदलें, (उदाहरण के लिए सक्रिय क्षेत्र के स्पष्ट दृश्य पृथक्करण द्वारा) - "सामान्य हेडर" क्षेत्र से - चेकबॉक्स आइकन)।

तकनीकी पहलू

  • TableHeader "लाइव" घटकों के लिए नहीं बनाया गया है। जो कुछ भी चाहता है उसे अपने आप नियंत्रित करना है
  • उदाहरण आसपास हैं (उदा।Jide ग्रिड को जोड़ने घटक) का समर्थन करता है
  • हैडर के साथ नगण्य है क्योंकि यह रेंडरर बदलने के लिए तुच्छ नहीं है और एक ही समय में एलएएफ उपस्थिति
+1

+1 और संदर्भ में पूरा दृष्टिकोण डालने के लिए चेक। – trashgod

23

लेख How to Use Tables: Using Custom Renderers कॉलम हेडर पर माउस ईवेंट का पता लगाने के उदाहरण के रूप में TableSorter प्रदान करता है। इसी तरह के प्रभाव को प्राप्त करने के लिए नीचे दिए गए उदाहरण में SelectAllHeader extends JToggleButton और implements TableCellRenderer समान दृष्टिकोण का उपयोग करना। एक TableModelListener टॉगल बटन की स्थिति के लिए उपयोग किया जाता है जब सभी चेक बॉक्स एक समान स्थिति में होते हैं।

enter image description here

import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 
import javax.swing.event.TableModelEvent; 
import javax.swing.event.TableModelListener; 
import javax.swing.table.*; 

/** 
* @see http://stackoverflow.com/questions/7137786 
* @see http://stackoverflow.com/questions/7092219 
* @see http://stackoverflow.com/questions/7093213 
*/ 
public class SelectAllHeaderTest { 

    private static final int BOOLEAN_COL = 2; 
    private static final Object colNames[] = {"Column 1", "Column 2", ""}; 
    private DefaultTableModel model = new DefaultTableModel(null, colNames) { 

     @Override 
     public Class<?> getColumnClass(int columnIndex) { 
      if (columnIndex == BOOLEAN_COL) { 
       return Boolean.class; 
      } else { 
       return String.class; 
      } 
     } 
    }; 
    private JTable table = new JTable(model); 

    public void create() { 
     for (int x = 1; x < 6; x++) { 
      model.addRow(new Object[]{ 
        "Row " + x + ", Col 1", "Row " + x + ", Col 2", false 
       }); 
     } 
     table.setAutoCreateRowSorter(true); 
     table.setPreferredScrollableViewportSize(new Dimension(320, 160)); 
     TableColumn tc = table.getColumnModel().getColumn(BOOLEAN_COL); 
     tc.setHeaderRenderer(new SelectAllHeader(table, BOOLEAN_COL)); 
     JFrame f = new JFrame(); 
     f.add(new JScrollPane(table)); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new SelectAllHeaderTest().create(); 
      } 
     }); 
    } 
} 

/** 
* A TableCellRenderer that selects all or none of a Boolean column. 
* 
* @param targetColumn the Boolean column to manage 
*/ 
class SelectAllHeader extends JToggleButton implements TableCellRenderer { 

    private static final String ALL = "✓ Select all"; 
    private static final String NONE = "✓ Select none"; 
    private JTable table; 
    private TableModel tableModel; 
    private JTableHeader header; 
    private TableColumnModel tcm; 
    private int targetColumn; 
    private int viewColumn; 

    public SelectAllHeader(JTable table, int targetColumn) { 
     super(ALL); 
     this.table = table; 
     this.tableModel = table.getModel(); 
     if (tableModel.getColumnClass(targetColumn) != Boolean.class) { 
      throw new IllegalArgumentException("Boolean column required."); 
     } 
     this.targetColumn = targetColumn; 
     this.header = table.getTableHeader(); 
     this.tcm = table.getColumnModel(); 
     this.applyUI(); 
     this.addItemListener(new ItemHandler()); 
     header.addMouseListener(new MouseHandler()); 
     tableModel.addTableModelListener(new ModelHandler()); 
    } 

    @Override 
    public Component getTableCellRendererComponent(
     JTable table, Object value, boolean isSelected, 
     boolean hasFocus, int row, int column) { 
     return this; 
    } 

    private class ItemHandler implements ItemListener { 

     @Override 
     public void itemStateChanged(ItemEvent e) { 
      boolean state = e.getStateChange() == ItemEvent.SELECTED; 
      setText((state) ? NONE : ALL); 
      for (int r = 0; r < table.getRowCount(); r++) { 
       table.setValueAt(state, r, viewColumn); 
      } 
     } 
    } 

    @Override 
    public void updateUI() { 
     super.updateUI(); 
     applyUI(); 
    } 

    private void applyUI() { 
     this.setFont(UIManager.getFont("TableHeader.font")); 
     this.setBorder(UIManager.getBorder("TableHeader.cellBorder")); 
     this.setBackground(UIManager.getColor("TableHeader.background")); 
     this.setForeground(UIManager.getColor("TableHeader.foreground")); 
    } 

    private class MouseHandler extends MouseAdapter { 

     @Override 
     public void mouseClicked(MouseEvent e) { 
      viewColumn = header.columnAtPoint(e.getPoint()); 
      int modelColumn = tcm.getColumn(viewColumn).getModelIndex(); 
      if (modelColumn == targetColumn) { 
       doClick(); 
      } 
     } 
    } 

    private class ModelHandler implements TableModelListener { 

     @Override 
     public void tableChanged(TableModelEvent e) { 
      if (needsToggle()) { 
       doClick(); 
       header.repaint(); 
      } 
     } 
    } 

    // Return true if this toggle needs to match the model. 
    private boolean needsToggle() { 
     boolean allTrue = true; 
     boolean allFalse = true; 
     for (int r = 0; r < tableModel.getRowCount(); r++) { 
      boolean b = (Boolean) tableModel.getValueAt(r, targetColumn); 
      allTrue &= b; 
      allFalse &= !b; 
     } 
     return allTrue && !isSelected() || allFalse && isSelected(); 
    } 
} 
+1

ए) मेटल बी में उड़ाता है) आकार बदलना चाहिए जब आकार बदलना क्षेत्र – kleopatra

+0

@ क्लेओपात्रा: यह ऐसी चीज है जिसे मैं जानना चाहता था; कृपया इसे उत्तर देने पर विचार करें। आगे के परीक्षण पर, 'धातु' पुनर्नवीनीकरण तीर को अस्वीकार कर रहा है; मुझे अपना खुद का बनाना चाहिए। – trashgod

+0

यदि आप कोई नई पंक्ति डालते हैं, तो ईवेंट को ट्रिगर करने योग्य ट्रायबलरोइन्स डालें, यह आइटम के लिए अपवाद फेंक देगा अस्थायी रेंज त्रुटि के साथ चेंज किया गया है, क्या कोई इसे पूरा करता है? तो यहां table.getRowCount() सही नहीं है, क्योंकि तालिका वहां प्रस्तुत नहीं है .... –

10

enter image description here

एक कस्टम TableCellRenderer का उपयोग प्रदान की रखने बदसूरत लग जाता है:

// column 1 
    col = table.getColumnModel().getColumn(1); 
    col.setHeaderRenderer(new EditableHeaderRenderer(new JButton("Button"))); 
    // column 2  
    col = table.getColumnModel().getColumn(2); 
    col.setHeaderRenderer(new EditableHeaderRenderer(new JToggleButton("Toggle"))); 
    // column 3 
    col = table.getColumnModel().getColumn(3); 
    col.setHeaderRenderer(new EditableHeaderRenderer(new JCheckBox("CheckBox"))); 



class EditableHeaderRenderer implements TableCellRenderer { 

    private JTable table = null; 
    private MouseEventReposter reporter = null; 
    private JComponent editor; 

    EditableHeaderRenderer(JComponent editor) { 
     this.editor = editor; 
     this.editor.setBorder(UIManager.getBorder("TableHeader.cellBorder")); 
    } 

    @Override 
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { 
     if (table != null && this.table != table) { 
      this.table = table; 
      final JTableHeader header = table.getTableHeader(); 
      if (header != null) { 
       this.editor.setForeground(header.getForeground()); 
       this.editor.setBackground(header.getBackground()); 
       this.editor.setFont(header.getFont()); 
       reporter = new MouseEventReposter(header, col, this.editor); 
       header.addMouseListener(reporter); 
      } 
     } 

     if (reporter != null) reporter.setColumn(col); 

     return this.editor; 
    } 

    static public class MouseEventReposter extends MouseAdapter { 

     private Component dispatchComponent; 
     private JTableHeader header; 
     private int column = -1; 
     private Component editor; 

     public MouseEventReposter(JTableHeader header, int column, Component editor) { 
      this.header = header; 
      this.column = column; 
      this.editor = editor; 
     } 

     public void setColumn(int column) { 
      this.column = column; 
     } 

     private void setDispatchComponent(MouseEvent e) { 
      int col = header.getTable().columnAtPoint(e.getPoint()); 
      if (col != column || col == -1) return; 

      Point p = e.getPoint(); 
      Point p2 = SwingUtilities.convertPoint(header, p, editor); 
      dispatchComponent = SwingUtilities.getDeepestComponentAt(editor, p2.x, p2.y); 
     } 

     private boolean repostEvent(MouseEvent e) { 
      if (dispatchComponent == null) { 
       return false; 
      } 
      MouseEvent e2 = SwingUtilities.convertMouseEvent(header, e, dispatchComponent); 
      dispatchComponent.dispatchEvent(e2); 
      return true; 
     } 

     @Override 
     public void mousePressed(MouseEvent e) { 
      if (header.getResizingColumn() == null) { 
       Point p = e.getPoint(); 

       int col = header.getTable().columnAtPoint(p); 
       if (col != column || col == -1) return; 

       int index = header.getColumnModel().getColumnIndexAtX(p.x); 
       if (index == -1) return; 

       editor.setBounds(header.getHeaderRect(index)); 
       header.add(editor); 
       editor.validate(); 
       setDispatchComponent(e); 
       repostEvent(e); 
      } 
     } 

     @Override 
     public void mouseReleased(MouseEvent e) { 
      repostEvent(e); 
      dispatchComponent = null; 
      header.remove(editor); 
     } 
    } 
} 

कृपया ध्यान दें कि popupmenu (जैसे JComboBox या JMenu) के साथ घटक अच्छी तरह से काम नहीं करते हैं। देखें: JComboBox fails to expand in JTable TableHeader)। लेकिन आप TableHeader में एक MenuButton उपयोग कर सकते हैं:

enter image description here

class MenuButtonTableHeaderRenderer extends JPanel implements TableCellRenderer { 

    private int  column = -1; 
    private JTable table = null; 
    private MenuButton b; 

    MenuButtonTableHeaderRenderer(String name, JPopupMenu menu) { 
     super(new BorderLayout()); 
     b = new MenuButton(ResourceManager.ARROW_BOTTOM, menu); 
     b.setBorder(BorderFactory.createEmptyBorder(1,1,1,1)); 
     JLabel l = new JLabel(name); 
     l.setFont(l.getFont().deriveFont(Font.PLAIN)); 
     l.setBorder(BorderFactory.createEmptyBorder(1,5,1,1)); 
     add(b, BorderLayout.WEST); 
     add(l, BorderLayout.CENTER); 
     setBorder(UIManager.getBorder("TableHeader.cellBorder")); 
    } 

    @Override 
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { 

     if (table != null && this.table != table) { 
      this.table = table; 
      final JTableHeader header = table.getTableHeader(); 
      if (header != null) { 
       setForeground(header.getForeground()); 
       setBackground(header.getBackground()); 
       setFont(header.getFont()); 

       header.addMouseListener(new MouseAdapter() { 

        @Override 
        public void mouseClicked(MouseEvent e) { 
         int col = header.getTable().columnAtPoint(e.getPoint()); 
         if (col != column || col == -1) return; 

         int index = header.getColumnModel().getColumnIndexAtX(e.getPoint().x); 
         if (index == -1) return; 

         setBounds(header.getHeaderRect(index)); 
         header.add(MenuButtonTableHeaderRenderer.this); 
         validate(); 

         b.doClick(); 

         header.remove(MenuButtonTableHeaderRenderer.this); 

         header.repaint(); 
        } 
       }); 
      } 
     } 
     column = col; 
     return this; 
    } 
} 
+0

मैं JButton और JToggleButton के लिए कार्रवाइयों को कहां सुन सकता हूं? दूसरे के रूप में, माउस जारी किए जाने के बाद आप संपादक को क्यों हटाते हैं? इससे क्लिक ईवेंट के बाद संपादक गायब हो जाता है। –

+0

यह "संसाधन प्रबंधक .ARROW_BOTTOM" त्रुटि देने वाली चीज़, मैं संसाधन प्रबंधक श्रेणी कैसे प्राप्त कर सकता हूं? किस जार @luca – JAVA

+0

में यह एक कस्टम क्लास है जिसे मैंने आइकनों के लिए उपयोग किया था। आप इसके बजाय अपना आइकन पास कर सकते हैं – luca