2016-06-07 287 views
4

enter image description hereJTable中禁用列選擇

我試圖禁用JTable中所有,但1列的行選擇。 (示例截圖中的圖層列)。在其他列中,我想讓用戶能夠與之交互的紡紗器和複選框,而不會影響圖層列中的選擇。

我最初的嘗試是在發生任何選定的行時進行存儲,然後當列A以外的單元格被選中時恢復爲那組選定的行。它有點作品,但問題在於,當其他單元格被選中時,它會「閃爍」,然後再將其恢復。我怎樣才能防止「閃光燈」?

這裏是我設置來說明問題的例子:如果你想阻止你需要攔截的事件做出選擇之前,因爲選擇之後一直table.getSelectedColumn()只能閃光燈

public class TableTest { 

    static int[] selectedRows = new int[0]; 

    final static String[] columns = new String[] { "Layer", "Enabled", "Read Only", "Storage" }; 

    final static DefaultTableModel model = new DefaultTableModel(new Vector(), new Vector(Arrays.asList(columns))) { 

     @Override 
     public void setValueAt(Object obj, int row, int col) { 

      if (obj instanceof Boolean || obj instanceof Integer) { 
       Object localObject = super.getValueAt(row, col); 
       if (localObject instanceof Integer) { 

        Integer val = (Integer) localObject; 

        ((SpinnerCell) obj).getSpinner().setValue(val); 
       } else if (localObject instanceof Boolean) { 

        Boolean val = (Boolean) localObject; 

        ((CheckboxCell) obj).getCheckBox().setEnabled(val); 
       } 

      } else { 
       super.setValueAt(obj, row, col); 
      } 

     } 

     @Override 
     public boolean isCellEditable(int rowIndex, int colIndex) { 

      return colIndex != 0; 
     } 

    }; 

    public static void main(String[] a) { 

     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       JFrame frame = new JFrame(); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

       final JTable table = new JTable(model) { 

        @Override 
        public TableCellRenderer getCellRenderer(final int rowIndex, int colIndex) { 

         int reaRowlIndex = convertRowIndexToModel(rowIndex); 
         int realColumnIndex = convertColumnIndexToModel(colIndex); 

         Object o = model.getValueAt(reaRowlIndex, realColumnIndex); 

         if (o instanceof TableCellRenderer) { 
          return (TableCellRenderer) o; 
         } else { 
          return super.getCellRenderer(reaRowlIndex, realColumnIndex); 
         } 
        } 

        // 
        @Override 
        public TableCellEditor getCellEditor(final int rowIndex, int colIndex) { 

         int reaRowlIndex = convertRowIndexToModel(rowIndex); 
         int realColumnIndex = convertColumnIndexToModel(colIndex); 

         Object o = model.getValueAt(reaRowlIndex, realColumnIndex); 

         if (o instanceof TableCellEditor) { 
          return (TableCellEditor) o; 
         } else { 
          return super.getCellEditor(reaRowlIndex, realColumnIndex); 
         } 
        } 

       }; 

       table.getTableHeader().setReorderingAllowed(false); 

       table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { 

        @Override 
        public void valueChanged(ListSelectionEvent arg0) { 
         if (table.getSelectedColumn() == 0) { 
          selectedRows = table.getSelectedRows(); 

          System.out.println("Selected Rows before " + Arrays.toString(selectedRows)); 
         } 

        } 
       }); 

       final ListSelectionModel columnListSelectionModel = table.getColumnModel().getSelectionModel(); 
       columnListSelectionModel.addListSelectionListener(new ListSelectionListener() { 
        @Override 
        public void valueChanged(ListSelectionEvent e) { 

         if (table.getSelectedColumn() != 0) { 

          table.clearSelection(); 

          System.out.println("Selected Rows during " + Arrays.toString(table.getSelectedRows())); 

          for (int i = 0; i < selectedRows.length; i++) { 
           table.getSelectionModel().addSelectionInterval(selectedRows[i], selectedRows[i]); 
          } 

          System.out.println("Selected Rows after " + Arrays.toString(table.getSelectedRows())); 
         } 

        } 
       }); 

       model.addRow(new Object[] { "Bird", new CheckboxCell(new JCheckBox()), 
         new CheckboxCell(new JCheckBox()), new SpinnerCell(new JSpinner()) }); 

       model.addRow(new Object[] { "Cat", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()), 
         new SpinnerCell(new JSpinner()) }); 

       model.addRow(new Object[] { "Dog", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()), 
         new SpinnerCell(new JSpinner()) }); 

       model.addRow(new Object[] { "Fish", new CheckboxCell(new JCheckBox()), 
         new CheckboxCell(new JCheckBox()), new SpinnerCell(new JSpinner()) }); 

       model.addRow(new Object[] { "Pig", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()), 
         new SpinnerCell(new JSpinner()) }); 

       frame.add(new JScrollPane(table)); 

       frame.setSize(300, 200); 
       frame.setVisible(true); 

      } 

     }); 

    } 

    static class CheckboxCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { 

     private static final long serialVersionUID = 1L; 
     private JCheckBox checkBox; 

     public CheckboxCell(JCheckBox inputCheckBox) { 
      checkBox = inputCheckBox; 
     } 

     @Override 
     public Object getCellEditorValue() { 
      return checkBox.isSelected(); 
     } 

     @Override 
     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, 
       int column) { 

      return checkBox; 
     } 

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

      return checkBox; 
     } 

     public JCheckBox getCheckBox() { 
      return checkBox; 
     } 

     @Override 
     public boolean isCellEditable(EventObject evt) { 
      return true; 
     } 

     public String toString() { 
      return checkBox.isSelected() + ""; 
     } 

    } 

    static class SpinnerCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer { 

     private static final long serialVersionUID = 1L; 
     private JSpinner editSpinner, renderSpinner; 

     public SpinnerCell() { 
      editSpinner = new JSpinner(); 
      JTextField tf = ((JSpinner.DefaultEditor) editSpinner.getEditor()).getTextField(); 
      tf.setForeground(Color.black); 
      renderSpinner = new JSpinner(); 
      JTextField tf2 = ((JSpinner.DefaultEditor) renderSpinner.getEditor()).getTextField(); 
      tf2.setForeground(Color.black); 
     } 

     public SpinnerCell(JSpinner showSpinner) { 
      editSpinner = showSpinner; 
      JTextField tf = ((JSpinner.DefaultEditor) editSpinner.getEditor()).getTextField(); 
      tf.setForeground(Color.black); 
      renderSpinner = showSpinner; 
      JTextField tf2 = ((JSpinner.DefaultEditor) renderSpinner.getEditor()).getTextField(); 
      tf2.setForeground(Color.black); 

     } 

     @Override 
     public Object getCellEditorValue() { 
      return editSpinner.getValue(); 
     } 

     @Override 
     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, 
       int column) { 

      return editSpinner; 
     } 

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

     public String toString() { 
      return editSpinner.getValue().toString(); 
     } 

     public JSpinner getSpinner() { 
      return editSpinner; 
     } 

     @Override 
     public boolean isCellEditable(EventObject evt) { 
      return true; 
     } 
    } 

} 
+3

爲什麼?我沒有看到這一點。您仍然想要突出顯示所有選定的行,但只允許用戶通過單擊第一列來選擇一行?該UI根本不直觀,我當然不會使用它。 – camickr

+0

我的真實實現在除第一列之外的所有內容中都有jcheckboxes和jspinners。我試圖防止點擊jcheckboxes和jspinners選擇行。我只希望第一列是可選的,同時仍然有jcheckboxes和jspinners的工作。 – systemoutprintln

+0

我已經更新了我想要完成的更直接版本的示例。 – systemoutprintln

回答

3

這裏是我的簡單的例子:

  • 覆蓋JTable#changeSelection(...)
  • table.setCellSelectionEnabled(true);
  • 覆蓋ListSelectionModel#isSelectedIndex(...)
import java.awt.*; 
import javax.swing.*; 
import javax.swing.event.*; 
import javax.swing.table.*; 

public class TableTest2 { 
    public JComponent makeUI() { 
    String[] columnNames = {"Layer", "Enabled", "Read Only"}; 
    Object[][] data = { 
     {"Bird", true, false}, {"Cat", true, false}, 
     {"Dog", true, false}, {"Fish", true, false}, {"Pig", true, false} 
    }; 
    TableModel model = new DefaultTableModel(data, columnNames) { 
     @Override public Class<?> getColumnClass(int column) { 
     return getValueAt(0, column).getClass(); 
     } 
     @Override public boolean isCellEditable(int row, int col) { 
     return col != 0; 
     } 
    }; 
    JTable table = new JTable(model) { 
     @Override public void changeSelection(
      int rowIndex, int columnIndex, boolean toggle, boolean extend) { 
     if (convertColumnIndexToModel(columnIndex) != 0) { 
      return; 
     } 
     super.changeSelection(rowIndex, columnIndex, toggle, extend); 
     } 
    }; 
    table.setAutoCreateRowSorter(true); 
    table.setCellSelectionEnabled(true); 
    table.getColumnModel().setSelectionModel(new DefaultListSelectionModel() { 
     @Override public boolean isSelectedIndex(int index) { 
     return table.convertColumnIndexToModel(index) == 0; 
     } 
    }); 
    return new JScrollPane(table); 
    } 
    public static void main(String... args) { 
    EventQueue.invokeLater(() -> { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     f.getContentPane().add(new TableTest2().makeUI()); 
     f.setSize(320, 240); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    }); 
    } 
} 
+0

謝謝。這是完美的。一旦啓用,我會獎勵賞金。 – systemoutprintln

3

做出來的,你會一直看到選擇閃光。

您可以使用按鍵偵聽器來檢查「右箭頭」,「數字鍵盤右鍵」和「結束」鍵按下,鼠標單擊偵聽器以檢查用戶正在點擊的位置,然後將事件更改爲選擇列只有一個。

例如看到這個答案展示瞭如何以檢查用戶點擊做出選擇之前鼠標:https://stackoverflow.com/a/7351053/1270000

然後你只需要添加一些代碼來選擇在A列中正確的細胞,而不是和唐忘記使用事件「e.consume()」以防止原始事件由於錯誤的用戶選擇而完成。

3

還沒有嘗試過,但您可能可以使用自定義ListSelectionModel。

下面是一個例子,可以讓你切換行的開/關選擇:

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

public class ToggleListSelectionModel extends DefaultListSelectionModel 
{ 
    @Override 
    public void setSelectionInterval(int index0, int index1) 
    { 
     // Select multiple lines 

     if (index0 != index1) 
     { 
      super.addSelectionInterval(index0, index1); 
      return; 
     } 

     // Toggle selection of a single line 

     if (super.isSelectedIndex(index0)) 
     { 
      super.removeSelectionInterval(index0, index0); 
     } 
     else 
     { 
      super.addSelectionInterval(index0, index0); 
     } 
    } 

    private static void createAndShowGUI() 
    { 
     String[] numbers = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; 
     final JList<String> list = new JList<String>(numbers); 
     list.setVisibleRowCount(numbers.length); 
     list.setSelectionModel(new ToggleListSelectionModel()); 
//  list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 

     JButton clear = new JButton("Clear"); 
     clear.addActionListener(new ActionListener() 
     { 
      public void actionPerformed(ActionEvent e) 
      { 
       list.clearSelection(); 
      } 
     }); 

     JFrame frame = new JFrame("SSCCE"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(new JScrollPane(list), BorderLayout.CENTER); 
     frame.add(clear, BorderLayout.PAGE_END); 
     frame.setLocationByPlatform(true); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) 
    { 
     EventQueue.invokeLater(new Runnable() 
     { 
      public void run() 
      { 
       createAndShowGUI(); 
      } 
     }); 
    } 
} 

在你的情況我想你需要對錶的訪問,所以你可以使用getSelectedColumn()方法。

你會從上面的例子中刪除的邏輯和喜歡的東西替代它:

if (table.getSelectedColumn() == 0) 
    super.setSelectionInterval(index0, index1);