杰瑞科技汇

Java Swing JTable如何高效实现数据绑定与渲染?

Of course! Let's dive deep into JTable, one of the most powerful and commonly used components in Java Swing for displaying and editing tabular data.

Java Swing JTable如何高效实现数据绑定与渲染?-图1
(图片来源网络,侵删)

This guide will cover everything from the basics to more advanced topics, complete with code examples.


The Core Concept: JTable vs. TableModel

The most important thing to understand about JTable is the Model-View-Controller (MVC) design pattern it uses.

  • JTable (The View): This is the visual component you see on the screen. It's responsible for rendering the data and handling user interactions (like clicks, selections, and edits).
  • TableModel (The Model): This is the data itself. It's an interface that defines how the data is structured and accessed. The model has no knowledge of how the data is displayed; it only knows about the data.

This separation is powerful. You can change the data in the model, and the JTable will automatically update to reflect those changes. You can also have multiple views (e.g., a JTable and a JList) display the same underlying data model.

The most common implementation of TableModel is DefaultTableModel.

Java Swing JTable如何高效实现数据绑定与渲染?-图2
(图片来源网络,侵删)

Creating a Simple JTable

Here's the most basic way to create a JTable.

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
public class SimpleJTableExample {
    public static void main(String[] args) {
        // 1. Create the data (a 2D Object array)
        Object[][] data = {
            {"John Doe", 28, "Software Engineer"},
            {"Jane Smith", 34, "Project Manager"},
            {"Peter Jones", 45, "Data Analyst"}
        };
        // 2. Create the column headers (a 1D Object array)
        String[] columns = {"Name", "Age", "Occupation"};
        // 3. Create a TableModel and pass the data and columns to it
        DefaultTableModel model = new DefaultTableModel(data, columns);
        // 4. Create the JTable and set its model
        JTable table = new JTable(model);
        // 5. Create a JScrollPane to make the table scrollable
        JScrollPane scrollPane = new JScrollPane(table);
        // 6. Set up the main window
        JFrame frame = new JFrame("Simple JTable Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scrollPane); // Add the scroll pane, not the table directly
        frame.pack(); // Sizes the frame to fit its components
        frame.setLocationRelativeTo(null); // Center the frame
        frame.setVisible(true);
    }
}

Key Points:

  • Object[][] data: The data must be a 2D array of Objects. This allows for different data types (Strings, Numbers, etc.) in the same column.
  • String[] columns: The column headers are a simple array of strings.
  • DefaultTableModel: This class handles all the basic data storage for you.
  • JScrollPane: Always put your JTable inside a JScrollPane! If the table has more rows or columns than can fit in the view, the scroll pane will automatically provide scrollbars.

Modifying Data in the Table

You don't modify the JTable directly. You modify its TableModel.

// Assuming 'model' is our DefaultTableModel from the previous example
// Add a new row
model.addRow(new Object[]{"Alice Williams", 30, "UX Designer"});
// Add a new column
model.addColumn("Salary");
// Now you need to add data for the new column in existing rows
model.setValueAt(75000, 0, 3); // Set Alice's salary
model.setValueAt(92000, 1, 3); // Set John's salary
model.setValueAt(110000, 2, 3); // Set Jane's salary
model.setValueAt(85000, 3, 3); // Set Peter's salary
// Update a cell value
model.setValueAt("29", 0, 1); // Update John Doe's age
// Remove a row
model.removeRow(1); // Removes Jane Smith
// Get a cell value
String name = (String) model.getValueAt(0, 0); // Gets "John Doe"

Customizing the Table's Appearance and Behavior

You can customize the JTable using various methods.

Java Swing JTable如何高效实现数据绑定与渲染?-图3
(图片来源网络,侵删)

a) Making Cells Non-Editable

// To make all cells non-editable
table.setModel(new DefaultTableModel(data, columns) {
    @Override
    public boolean isCellEditable(int row, int column) {
        return false; // All cells are not editable
    }
});
// Or, to make specific columns non-editable
table.setModel(new DefaultTableModel(data, columns) {
    @Override
    public boolean isCellEditable(int row, int column) {
        // For example, make the "Age" column non-editable
        return column != 1; 
    }
});

b) Resizing Columns

// Auto-resize columns to fit the content
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
// Manually set the width of a column
table.getColumnModel().getColumn(0).setPreferredWidth(150); // Name column
table.getColumnModel().getColumn(1).setPreferredWidth(50);  // Age column

c) Selecting Rows and Columns

// Allow only single row selection
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Allow multiple interval selections
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// Set selection to be rows only (default)
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(false);

Using a Custom Data Model (AbstractTableModel)

For more complex applications, creating your own model by extending AbstractTableModel gives you full control. This is the standard practice for non-trivial applications.

Let's create a model that reads data from a list of custom objects.

Step 1: Create a simple data class (POJO)

public class Person {
    private String name;
    private int age;
    private String occupation;
    public Person(String name, int age, String occupation) {
        this.name = name;
        this.age = age;
        this.occupation = occupation;
    }
    // Getters
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getOccupation() { return occupation; }
}

Step 2: Create the custom AbstractTableModel

import javax.swing.table.AbstractTableModel;
import java.util.List;
public class PersonTableModel extends AbstractTableModel {
    private final List<Person> persons;
    private final String[] columnNames = {"Name", "Age", "Occupation"};
    public PersonTableModel(List<Person> persons) {
        this.persons = persons;
    }
    // --- Required AbstractTableModel methods ---
    @Override
    public int getRowCount() {
        return persons.size();
    }
    @Override
    public int getColumnCount() {
        return columnNames.length;
    }
    @Override
    public String getColumnName(int column) {
        return columnNames[column];
    }
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Person person = persons.get(rowIndex);
        switch (columnIndex) {
            case 0: return person.getName();
            case 1: return person.getAge();
            case 2: return person.getOccupation();
            default: return null;
        }
    }
    // --- Optional but useful methods ---
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        // This helps the renderer choose the correct component (e.g., JLabel for String, JProgressBar for Integer)
        if (columnIndex == 1) {
            return Integer.class; // The 'Age' column contains Integers
        }
        return super.getColumnClass(columnIndex);
    }
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        // For example, allow editing only the 'Occupation' column
        return columnIndex == 2;
    }
    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex == 2) {
            Person person = persons.get(rowIndex);
            person.setOccupation((String) aValue);
            fireTableCellUpdated(rowIndex, columnIndex); // Notify listeners of the change
        }
    }
}

Step 3: Use the custom model in your JTable

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class CustomTableModelExample {
    public static void main(String[] args) {
        // 1. Create your data objects
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("John Doe", 28, "Software Engineer"));
        personList.add(new Person("Jane Smith", 34, "Project Manager"));
        personList.add(new Person("Peter Jones", 45, "Data Analyst"));
        // 2. Create your custom model
        PersonTableModel model = new PersonTableModel(personList);
        // 3. Create the JTable with the custom model
        JTable table = new JTable(model);
        // 4. Add to a scroll pane and a frame (same as before)
        JScrollPane scrollPane = new JScrollPane(table);
        JFrame frame = new JFrame("Custom Table Model Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(scrollPane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Handling User Events (Row Selection)

A very common task is to perform an action when a user selects a row. This is done using a ListSelectionListener.

// Assuming 'table' is your JTable and 'model' is your model
JListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.addListSelectionListener(new ListSelectionListener() {
    @Override
    public void valueChanged(ListSelectionEvent e) {
        // We only care about the final selection, not the intermediate ones
        if (!e.getValueIsAdjusting()) {
            int selectedRow = table.getSelectedRow();
            if (selectedRow != -1) {
                // Get the data from the selected row
                String name = (String) table.getValueAt(selectedRow, 0);
                int age = (int) table.getValueAt(selectedRow, 1);
                String occupation = (String) table.getValueAt(selectedRow, 2);
                System.out.println("Selected Row: " + selectedRow);
                System.out.println("Name: " + name);
                System.out.println("Age: " + age);
                System.out.println("Occupation: " + occupation);
                System.out.println("----------------------");
            }
        }
    }
});

Advanced: Custom Cell Renderers and Editors

By default, JTable uses JLabels to render cells and JTextFields to edit them. You can customize this to display any component you want.

Example: A JProgressBar in a Cell

Let's modify our PersonTableModel to include a "Performance" score from 0-100 and render it as a progress bar.

Update the Person class:

public class Person {
    // ... existing fields ...
    private int performance;
    public Person(String name, int age, String occupation, int performance) {
        // ... existing constructor code ...
        this.performance = performance;
    }
    // ... existing getters ...
    public int getPerformance() { return performance; }
}

Update the PersonTableModel:

public class PersonTableModel extends AbstractTableModel {
    // ... existing code ...
    private final String[] columnNames = {"Name", "Age", "Occupation", "Performance"};
    // ... getRowCount, getColumnCount, getColumnName, getValueAt ...
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Person person = persons.get(rowIndex);
        switch (columnIndex) {
            // ... other cases ...
            case 3: return person.getPerformance(); // Return the performance score
            default: return null;
        }
    }
    @Override
    public Class<?> getColumnClass(int columnIndex) {
        if (columnIndex == 1) return Integer.class; // Age
        if (columnIndex == 3) return Integer.class; // Performance
        return super.getColumnClass(columnIndex);
    }
}

Set the custom renderer in your main application:

// In your main method, after creating the table
// Set a custom renderer for the "Performance" column (column index 3)
table.setDefaultRenderer(Integer.class, new JProgressBar()); // This is a shortcut!
// Or, for more control, create a custom renderer class:
/*
table.getColumnModel().getColumn(3).setCellRenderer(new TableCellRenderer() {
    private JProgressBar progressBar = new JProgressBar();
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        if (value instanceof Integer) {
            progressBar.setValue((Integer) value);
        }
        return progressBar;
    }
});
*/

Now, when you run the application, the "Performance" column will display a JProgressBar filled according to the value.

分享:
扫描分享到社交APP
上一篇
下一篇