2011-02-10 31 views
8

Tôi đang cố tạo bảng có tiêu đề cột tùy chỉnh. Tôi muốn các tiêu đề cột bao gồm một nút mà người dùng có thể nhấp vào. Chức năng của nút sẽ là xóa cột khỏi bảng. Về cơ bản, tôi đang cố gắng để xây dựng một cái gì đó như this.JTable có nút “đóng” trong tiêu đề cột

Dưới đây là mã của tôi:

public class CustomColumnHeadersTable { 

    private static String[] columnNames = { 
     "Column 1", "Column 2", "Column 3" 
    }; 
    private static String[][] data = { 
     {"A", "B", "C"}, 
     {"D", "E", "F"}, 
     {"G", "H", "I"} 
    }; 

    public CustomColumnHeadersTable() { 
     DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames); 
     JTable table = new JTable(model); 
     JScrollPane scrollPane = new JScrollPane(table); 

     //set Header Renderer of each column to use the Custom renderer 
     Enumeration enumeration = table.getColumnModel().getColumns(); 
     while (enumeration.hasMoreElements()) { 
      TableColumn aColumn = (TableColumn) enumeration.nextElement(); 
      aColumn.setHeaderRenderer(new CustomColumnCellRenderer()); 
     } 

     JFrame frame = new JFrame(); 
     frame.getContentPane().add(scrollPane, BorderLayout.CENTER); 

     frame.setPreferredSize(new Dimension(300, 150)); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     CustomColumnHeadersTable ccht = new CustomColumnHeadersTable(); 
    } 
} 

class CustomColumnCellRenderer implements TableCellRenderer { 

    private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif"; 
     //using a URL for the icon, so I don't have to upload the icon with the question 
    private static Dimension buttonSize = new Dimension(16, 16); 
    private static Dimension buttonBoxSize = new Dimension(16, 16); 
    private static Border panelBorder = BorderFactory.createRaisedBevelBorder(); 

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
     JPanel panel = new JPanel(); 
     JLabel label = new JLabel(); 
     JButton button = new JButton(); 
     Box buttonBox = Box.createHorizontalBox(); 
     BorderLayout layout = new BorderLayout(); 

     label.setText(table.getColumnName(column)); 

     try { button.setIcon(new ImageIcon(new URL(iconURL))); } 
     catch (MalformedURLException ex) { 
      Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex); 
     } 

     //set size of the button and it's box 
     button.setMaximumSize(buttonSize); 
     button.setSize(buttonSize); 
     button.setPreferredSize(buttonSize); 
     buttonBox.setMaximumSize(buttonBoxSize); 
     buttonBox.setSize(buttonBoxSize); 
     buttonBox.setPreferredSize(buttonBoxSize); 

     button.addMouseListener(new CustomMouseListener()); //doesn't work... 

     buttonBox.add(button); 
     panel.add(label, BorderLayout.CENTER); 
     panel.add(buttonBox, BorderLayout.EAST); 

     panel.setBorder(panelBorder); 

     return panel; 
    } 
} 

class CustomMouseListener implements MouseListener 
{ 
    public void mouseClicked(MouseEvent e) { System.out.println("Mouse Clicked."); } 
    public void mousePressed(MouseEvent e) { System.out.println("Mouse Pressed."); } 
    public void mouseReleased(MouseEvent e) { System.out.println("Mouse Released."); } 
    public void mouseEntered(MouseEvent e) { System.out.println("Mouse Entered."); } 
    public void mouseExited(MouseEvent e) { System.out.println("Mouse Exited."); } 
} 

Theo mặc định, nếu tôi hiểu đúng, JTable sử dụng một JLabel để render các tiêu đề cột. Ý tưởng của tôi là sử dụng triển khai TableCellRenderer tùy chỉnh và xây dựng tiêu đề cột của riêng tôi từ một số thành phần, cụ thể là, một JPanel có chứa một JLabel và một JButton. Tôi xây dựng và trả về điều đó trong hàm getTableCellRendererComponent (...).

Trực quan, tính năng này hoạt động. Vấn đề là tôi không thể phát hiện nhấp chuột vào nút (hoặc, cho rằng vấn đề, trên bảng điều khiển nắm giữ nó). Đơn giản chỉ cần thêm một MouseListener vào nút không hoạt động. Sự kiện không bao giờ đạt đến nó.

Tôi đã tìm thấy một số thứ tương tự trên web nhưng chúng không đạt được chức năng tôi cần.

Đầu tiên, có một ví dụ về cách đặt một JCheckBox vào tiêu đề, ở đây:

http://java-swing-tips.blogspot.com/2009/02/jtableheader-checkbox.html 

Vấn đề ở đây là toàn bộ tiêu đề là hộp kiểm. Nhấp vào hộp kiểm hoặc trên nhãn được liên kết sẽ tạo ra cùng một hiệu ứng. Do đó, không thể sắp xếp cột. Tôi muốn làm cho nó để nhấp vào nhãn sắp xếp cột và nhấp vào nút đóng sẽ xóa cột khỏi bảng. Nói cách khác, tiêu đề cần phải có hai khu vực riêng biệt với trình xử lý sự kiện chuột riêng biệt.

tôi tìm thấy một ví dụ ở đây:

http://www.devx.com/getHelpOn/10MinuteSolution/20425/1954?pf=true 

này bao gồm việc đặt JButtons vào các tế bào của bảng, sau đó phát hiện cú click chuột trên bảng chính nó, tính toán cột và hàng nơi nhấp chuột xảy ra, và cử các sự kiện vào nút thích hợp.

Có một số vấn đề với điều này. Đầu tiên, các nút nằm trong các ô, không nằm trong các tiêu đề. Và thứ hai, đây chỉ là một thành phần, không phải một số thành phần bên trong một JPanel. Mặc dù tôi có ý tưởng gửi đi các sự kiện từ ví dụ này, tôi không thể làm cho nó hoạt động cho một thành phần phức hợp.

Tôi đã thử một cách tiếp cận khác. Tôi lý luận rằng nếu tôi có thể nhận được tọa độ của các nút đóng, sau đó biết tọa độ của nhấp chuột tôi có thể tính toán nút nào được nhấp và gửi sự kiện một cách thích hợp. Tôi chạy một vài thử nghiệm và phát hiện ra rằng các thành phần bên trong tiêu đề bảng không thực sự nằm trên màn hình.

Tôi đã thêm biến JButton tĩnh vào lớp chính (công khai) của tôi và tạo lớp thực hiện TableCellRenderer là lớp bên trong của lớp chính. Trong getTableCellRendererComponent (...), trước khi trở về, tôi gán JButton mà tôi vừa tạo cho biến tĩnh đó. Bằng cách này, tôi có thể có được một xử lý trên nó, vì vậy để nói chuyện. Sau đó, trong chính, tôi đã cố gắng getX(), getY(), getWidth(), getHeight() và getLocationOnScreen() sử dụng biến tĩnh đó. X, Y, Chiều rộng và Chiều cao đều trả về 0. GetLocationOnScreen() treo chương trình, nói rằng thành phần phải có mặt trên màn hình để chức năng này hoạt động.

Mã này trông giống như sau:

public class CustomColumnHeadersTable { 

     private static JButton static_button; 
     ///the rest as before.... 

"class CustomColumnCellRenderer thực hiện TableCellRenderer" trở thành một lớp bên trong của CustomColumnHeadersTable. Cho rằng, tôi phải từ bỏ các biến tĩnh trong CustomColumnCellRenderer, vì vậy tôi không bận tâm với các biểu tượng hoặc url hoặc bất cứ điều gì như thế. Thay vì một nút với một biểu tượng, tôi chỉ sử dụng một nút đơn giản mà nói "NÚT" ...

Tiếp theo, bên trong getTableCellRendererComponent (...), ngay trước khi tuyên bố trở lại, tôi đã làm

static_button = button; 

Và cuối cùng, bên trong main(), tôi đã cố gắng làm điều này:

System.out.println("X: " + static_button.getX()); 
    System.out.println("Y: " + static_button.getY()); 
    System.out.println("W: " + static_button.getWidth()); 
    System.out.println("H: " + static_button.getHeight()); 
    System.out.println("LOC: " + static_button.getLocation()); 
    System.out.println("LOC on screen: " + static_button.getLocationOnScreen()); 

Kết quả trông như thế này:

X: 0 
Y: 0 
W: 0 
H: 0 
LOC: java.awt.Point[x=0,y=0] 
Exception in thread "main" java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location 
at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1943) 
at java.awt.Component.getLocationOnScreen(Component.java:1917) 
... 

Nói cách khác, nút có tất cả các kích thước của 0, và theo Java nó không thực sự nằm trên màn hình (mặc dù tôi có thể nhìn thấy nó ...). Gọi getLocationOnScreen() làm hỏng chương trình.

Vì vậy, vui lòng trợ giúp nếu có thể. Có lẽ ai đó biết cách làm điều này. Có lẽ bạn chỉ có thể đề xuất một số cách tiếp cận khác để thử. Hoặc, có thể bạn biết điều đó là không thể ...

Cảm ơn sự giúp đỡ của bạn.

Trả lời

2

Có, tôi đã ngạc nhiên khi lần đầu tiên tôi cố gắng làm điều gì đó tương tự, như đặt JButton s vào một JList - nó không hoàn toàn phù hợp với những lý do bạn đã nêu. Cách chính xác để làm những gì tôi muốn sử dụng danh sách giống như LayoutManager để đặt số JButton trong danh sách.

Trong trường hợp của bạn, tôi sẽ cố gắng sử dụng kết hợp JLabel s và MouseListeners, mà tôi tin rằng sẽ hoạt động chính xác. Sử dụng JLabel làm tiêu đề của bạn sẽ cho phép bạn cung cấp cả hình ảnh và văn bản và bạn có thể đăng ký MouseListener trên JLabel. Được cấp, bạn sẽ không nhận được các hiệu ứng hình ảnh giống như khi bạn nhấp vào một số JButton (tức là nút bị giảm), nhưng nó sẽ cho phép có cùng chức năng.

Một điều cần lưu ý khác là sử dụng static JButton s (hoặc bất kỳ JComponent) nào. Vấn đề với việc này là tọa độ của JButton được lưu trữ trong chính bản thân số JButton, có nghĩa là bạn không thể sử dụng cùng một số JButton ở những nơi khác nhau. Nói cách khác, nếu bạn thử thêm cùng một số static JButton vào danh sách nhiều lần, bạn sẽ không thấy nhiều JButton s, bạn sẽ chỉ thấy JButton ở vị trí mà bạn đã thêm lần cuối. Cách thích hợp để đạt được hiệu quả mong muốn của bạn là thay vào đó giữ tham chiếu tĩnh đến nội dung của JButton - cụ thể là hình ảnh và văn bản - thay vì chính bản thân số JButton. Sau đó, chỉ cần tạo một hình ảnh và văn bản mới JButton và đặt nó ở bất cứ đâu bạn muốn. Nói chung, tốt nhất là tránh xa các tham chiếu tĩnh thành các thành phần của Swing vì nó không thể có nhiều phiên bản của cùng một thành phần.

Đu đôi khi không đu đủ. Hy vọng rằng tôi đã cung cấp một số thông tin hữu ích - tiếp tục chiến đấu với cuộc chiến tốt!

+0

Cảm ơn bạn! Tôi đã thử một cái gì đó tương tự - quyết định chỉ tính toán nếu các nhấp chuột rơi vào nút. Vì vậy, tôi cần giữ nút ở một nơi duy nhất. Nói, đó là 16x16, và tôi cần phải luôn luôn giữ nó 8 pixel bên trái của cạnh cột. Mặc dù tôi đã có thể làm điều đó bằng cách sử dụng Hộp và keo, tôi đã không thể làm cho nó để chơi tốt với JLabel. Nói, cột của tôi trông như thế này: | Tên cột [x] |. Nếu cột được kích thước lại, tôi muốn nó trông giống như | Colum ... [x] |. IOW, hình elip sẽ xuất hiện tự động. Thay vào đó, nút chỉ vẽ trên nhãn. Bất kỳ ý tưởng? – Aleksey

1

Bạn có thể sử dụng JXTable (từ bảng xoay) có tùy chọn đó để 'tắt' một cột cụ thể khỏi bảng của bạn.

Andrei Ionut Apopei

0

tôi có những giải pháp:

public class CustomColumnHeadersTable { 

    public static Vector<JButton> buttons = new Vector<JButton>(); 

    private static String[] columnNames = { 
      "Column 1", "Column 2", "Column 3" 
    }; 
    private static String[][] data = { 
      {"A", "B", "C"}, 
      {"D", "E", "F"}, 
      {"G", "H", "I"} 
    }; 

    class ColumnHeaderListener extends MouseAdapter { 
     public void mouseClicked(MouseEvent evt) { 
      JTable table = ((JTableHeader) evt.getSource()).getTable(); 
      TableColumnModel colModel = table.getColumnModel(); 

      int index = colModel.getColumnIndexAtX(evt.getX()); 
      if (index == -1) { 
       return; 
      } 
      Rectangle headerRect = table.getTableHeader().getHeaderRect(index); 

      if(1== index){ 
       if(headerRect.contains(evt.getX() , evt.getY())){ 
        int xx = evt.getX() - headerRect.x ; 

        for (int i = 0; i < buttons.size(); i++) { 
         JButton button = buttons.get(i); 
         Rectangle re = button.getBounds(); 
         if(re.contains(xx, evt.getY())){ 
          System.out.println("Bingle"); 
         } 

        } 
       } 
      } 
     } 
    } 

    public CustomColumnHeadersTable() { 
     DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames); 
     JTable table = new JTable(model); 
     JScrollPane scrollPane = new JScrollPane(table); 

     JTableHeader header = table.getTableHeader(); 
     header.addMouseListener(new ColumnHeaderListener()); 

     //set Header Renderer of each column to use the Custom renderer 
     Enumeration enumeration = table.getColumnModel().getColumns(); 
     while (enumeration.hasMoreElements()) { 
      TableColumn aColumn = (TableColumn) enumeration.nextElement(); 
      aColumn.setHeaderRenderer(new CustomColumnCellRenderer(buttons)); 
     } 

     JFrame frame = new JFrame(); 
     frame.getContentPane().add(scrollPane, BorderLayout.CENTER); 

     frame.setPreferredSize(new Dimension(300, 150)); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     CustomColumnHeadersTable ccht = new CustomColumnHeadersTable(); 
    } 
} 

class CustomColumnCellRenderer implements TableCellRenderer { 

    private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif"; 
    //using a URL for the icon, so I don't have to upload the icon with the question 
    private static Dimension buttonSize = new Dimension(16, 16); 
    private static Dimension buttonBoxSize = new Dimension(16, 16); 
    private static Border panelBorder = BorderFactory.createRaisedBevelBorder(); 

    Vector<JButton> buttons = null; 

    JPanel panel = new JPanel(); 
    JLabel label = new JLabel(); 
    JButton button = new JButton(); 

    CustomColumnCellRenderer(Vector<JButton> buttons) { 
     this.buttons = buttons; 

     try { button.setIcon(new ImageIcon(new URL(iconURL))); } 
     catch (MalformedURLException ex) { 
      Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex); 
     } 

     //set size of the button and it's box 
     button.setMaximumSize(buttonSize); 
     button.setSize(buttonSize); 
     button.setPreferredSize(buttonSize); 

     BorderLayout layout = new BorderLayout(); 
     panel.setLayout(layout); 
     panel.add(label, BorderLayout.CENTER); 
     panel.add(button, BorderLayout.EAST); 
     panel.setBorder(panelBorder); 

     buttons.add(button); 
    } 

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

     label.setText(table.getColumnName(column)); 
     return panel; 
    } 
}