我需要制作一個 JTable,其中每個“單元格”都有各種單獨的資料要顯示。對于每個專案,我需要以占總額的百分比和美元價值、目標價值、實際價值、預計價值以及三者之間的差異來顯示。附加的影像是在 Excel 中完成的,但顯示了我試圖在 JTable 中生成的內容。

為了使事情變得更有趣,除了目標行和實際值行之外,用戶在運行時可以選擇所有其他行。因為他們可以通過在我的程式中選擇選項來選擇要顯示的行。我需要能夠在逐個子單元格的基礎上自定義單元格底紋、字體和文本格式,并且我還需要在整個表格中改變邊框、行高和列寬。
我還沒有開始撰寫代碼,因為我正在考慮不同的方法。我正在尋找其他人關于采取哪種方法的反饋,或者我是否應該考慮其他選擇。
選項 1 - 只需將單個 JTable 與自定義資料模型一起使用,但要盡可能接近默認的單元格渲染器和單元格編輯器,并使用邊框和每個單元格的格式來模仿預期的外觀。資料模型將處理 JTable 提供的行、列以及每個單元格實際對應的專案/值的轉換。
選項 2 - 使用 JTable 作為 JTable 中每個單元格的單元格編輯器(表中的表)。
選項 3 - 使用各種標簽和文本欄位撰寫我自己的自定義單元格渲染器。
選項 4 - 是否有任何預先撰寫的組件可以提供我正在尋找的那種我應該考慮的靈活性?
對采取最佳方法的想法表示贊賞。
謝謝。
uj5u.com熱心網友回復:
我選擇選項 2A - 謝謝,吉爾伯特。下面的代碼和附加的影像是概念驗證。格式化目前還很初級,但作為一個演示專案它可以作業。
package tableTest;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.text.DecimalFormat;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
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.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
/** Listener interface between data and TableDataModel to keep things in sync. */
interface DataNotifier {
void update();
}
class Grid {
JFrame frame;
/** Data array in Row,Col array order */
MyData[][] myData;
Double totalValue = 1234567.89d; // just a hack for demonstration purposes
public Double getTotalValue() {
return totalValue;
}
/** Constructor */
public Grid() {
MyData[][] myData1 = {
{new MyData(0.060, 0.052, 0.058, "Red", "Square", this), new MyData(0.040, 0.038, 0.040, "Red", "Circle", this), new MyData(0.045, 0.045, 0.045, "Red", "Triangle", this), new MyData(0.080, 0.084, 0.081, "Red", "Octagon", this)},
{new MyData(0.160, 0.142, 0.172, "Blue", "Square", this), new MyData(0.120, 0.135, 0.123, "Blue", "Circle", this), new MyData(0.000, 0.000, 0.000, "Blue", "Triangle", this), new MyData(0.060, 0.050, 0.052, "Blue", "Octagon", this)},
{new MyData(0.080, 0.072, 0.079, "Green", "Square", this), new MyData(0.010, 0.023, 0.011, "Green", "Circle", this), new MyData(0.090, 0.120, 0.099, "Green", "Triangle", this), new MyData(0.110, 0.105, 0.110, "Green", "Octagon", this)},
{new MyData(0.000, 0.000, 0.000, "Pink", "Square", this), new MyData(0.080, 0.065, 0.078, "Pink", "Circle", this), new MyData(0.030, 0.010, 0.028, "Pink", "Triangle", this), new MyData(0.035, 0.060, 0.024, "Pink", "Octagon", this)},
};
myData = myData1;
initialize();
}
/** Initialize console */
private void initialize() {
final int ROW_HEIGHT = 24;
frame = new JFrame("Grid Window");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(800, 800);
frame.setLayout(new BorderLayout());
frame.setLocationRelativeTo(null);
Font defaultFont = javax.swing.UIManager.getDefaults().getFont("Label.font");
// Add a control panel - allows us to initiate a change in the data.
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS));
JButton action1 = new JButton("Increase Total Value");
action1.addActionListener(this::action1ButtonPressed);
controlPanel.add(action1);
frame.add(controlPanel, BorderLayout.NORTH);
JPanel tablePanel = new JPanel();
tablePanel.setLayout(new GridLayout(4, 4));
for (int r = 0; r < 4; r ) {
for (int c = 0; c < 4; c ) {
// cellPanel represents one of our "cells" in tabelPanel - it encloses the table, header and super-header
JPanel cellPanel = new JPanel();
cellPanel.setLayout(new BorderLayout());
cellPanel.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));
// Create the super-header that is the title for each cell's data
JLabel cellLabel = new JLabel(myData[r][c].getColor() " " myData[r][c].getShape());
cellLabel.setFont(defaultFont.deriveFont(Font.BOLD));
cellLabel.setHorizontalAlignment(SwingConstants.CENTER);
cellLabel.setPreferredSize(new Dimension(102, ROW_HEIGHT));
cellLabel.setMinimumSize(new Dimension(102, ROW_HEIGHT));
// Add to cellPanel
cellPanel.add(cellLabel, BorderLayout.NORTH);
// Create the table
MyTableModel cellDataModel = new MyTableModel(myData[r][c], this);
myData[r][c].setOurDataModel(cellDataModel); // Gives myData a link to the TableModel for data change notifications
MyCellRenderer cellRender = new MyCellRenderer();
JTable cellTable = new JTable(cellDataModel);
cellTable.setDefaultRenderer(Object.class, cellRender);
cellTable.getTableHeader().setMinimumSize(new Dimension(102, ROW_HEIGHT));
cellTable.setRowHeight(ROW_HEIGHT);
cellTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
cellTable.setMinimumSize(new Dimension(102, ROW_HEIGHT));
TableColumnModel tcm = cellTable.getColumnModel();
tcm.getColumn(0).setPreferredWidth(120);
tcm.getColumn(1).setPreferredWidth(80);
tcm.getColumn(2).setPreferredWidth(80);
tcm.getColumn(0).setMinWidth(120);
tcm.getColumn(1).setMinWidth(80);
tcm.getColumn(2).setMinWidth(80);
cellTable.setFillsViewportHeight(true);
// Since we're not putting the table into a scroll pane, we need to explicitly add the header
// or it won't show. We'll use another panel for this.
JPanel dataPanel = new JPanel();
dataPanel.setLayout(new BorderLayout());
// Add the header
dataPanel.add(cellTable.getTableHeader(), BorderLayout.NORTH);
// Add the table
dataPanel.add(cellTable, BorderLayout.CENTER);
// Add dataPanel to the cell panel
cellPanel.add(dataPanel, BorderLayout.CENTER);
// Add cellPanel to the tablePlanel
tablePanel.add(cellPanel);
}
}
// We now have a panel with our home-made grid - add it to a scroll pane
JScrollPane spTable = new JScrollPane(tablePanel);
spTable.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
spTable.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(spTable, BorderLayout.CENTER);
}
public void show() {
frame.setVisible(true);
}
/** Pressing the button just increases TotalValue by 50% - just for demo reasons. */
private void action1ButtonPressed(ActionEvent ae) {
totalValue *= 1.5;
System.out.println("New value = " Double.toString(totalValue));
updateData();
}
/** Initiate a data update
* <p>
* This tells each data object that the data has changed, which in turn tells each
* TableModel which then tells the corresponding table.
*/
private void updateData() {
for (int r = 0; r < 4; r ) {
for (int c = 0; c < 4; c ) {
myData[r][c].update();
}
}
}
/** Custom cell renderer */
private class MyCellRenderer extends DefaultTableCellRenderer {
DecimalFormat currencyFormatter = new DecimalFormat("$#,###.00");
DecimalFormat percentFormatter = new DecimalFormat("0.0%");
protected MyCellRenderer() {
super();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
// First, adjust the value
String newValue;
if (column == 0) {
newValue = (String) value;
} else if (column == 1) {
newValue = currencyFormatter.format(value);
} else {
newValue = percentFormatter.format(value);
}
// Call super
JComponent c = (JComponent) super.getTableCellRendererComponent(table, newValue, isSelected, hasFocus, row, column);
JLabel cl = (JLabel) c;
// Adjust formatting
switch (column) {
case 0:
// row header
cl.setBackground(Color.LIGHT_GRAY);
cl.setHorizontalAlignment(SwingConstants.LEFT);
break;
case 1:
// value column
cl.setBackground(Color.white);
cl.setHorizontalAlignment(SwingConstants.RIGHT);
break;
case 2:
cl.setBackground(Color.white);
cl.setHorizontalAlignment(SwingConstants.CENTER);
break;
default:
// Something we don't expect
throw new IllegalArgumentException("column unexpected.");
}
return cl;
}
}
private class MyTableModel extends DefaultTableModel implements DataNotifier {
/** Reference to the source data */
MyData thisData;
/** Reference to the console app - implemented for future use */
Grid parent;
private static final String[] rowNames = {"Target", "Actual", "Actual - Target", "Projected", "Projected - Target", "Projected - Actual"};
private static final String[] columnNames = {"", "Value", "%"};
/** Constructor */
protected MyTableModel(MyData dataItem, Grid parent) {
super();
thisData = dataItem;
this.parent = parent;
}
/** TableModel's end of the listenr interface */
public void update() {
fireTableDataChanged();
}
@Override
public int getRowCount() {
return rowNames.length;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public Class<?> getColumnClass(int col) {
return String.class;
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
@Override
public Object getValueAt(int row, int column) {
Double ret;
Double mult;
// Evaluate the column
switch (column) {
case 0:
// row header
return rowNames[row];
case 1:
// value column
mult = parent.getTotalValue();
break;
case 2:
mult = 1d;
break;
default:
// Something we don't expect
throw new IllegalArgumentException("column greater than 2");
}
// Evaluate the row
switch (row) {
case 0:
// Target
ret = thisData.getTargetPct() * mult;
break;
case 1:
// Actual
ret = thisData.getActualPct() * mult;
break;
case 2:
// Actual - Target
ret = (thisData.getActualPct() - thisData.getTargetPct()) * mult;
break;
case 3:
// Projected
ret = thisData.getPlanPct() * mult;
break;
case 4:
// Projected - Target
ret = (thisData.getPlanPct() - thisData.getTargetPct()) * mult;
break;
case 5:
// Projected - Actual
ret = (thisData.getPlanPct() - thisData.getActualPct()) * mult;
break;
default:
throw new IllegalArgumentException("row greater than our size");
}
return ret;
}
@Override
public void setValueAt(Object aValue, int row, int column) {
super.setValueAt(aValue, row, column);
}
}
/** Custom data object for our demo */
private class MyData {
Double pTarget;
Double pActual;
Double pPlan;
String color;
String shape;
Grid parent;
/** Data's end of the listener interface */
DataNotifier ourDataModel;
protected String getColor() {
return color;
}
protected void setColor(String color) {
this.color = color;
update();
}
protected String getShape() {
return shape;
}
protected void setShape(String shape) {
this.shape = shape;
update();
}
protected Double getTargetPct() {
return pTarget;
}
protected void setTargetPct(Double pTarget) {
this.pTarget = pTarget;
update();
}
protected Double getActualPct() {
return pActual;
}
protected void setActualPct(Double pActual) {
this.pActual = pActual;
update();
}
protected Double getPlanPct() {
return pPlan;
}
protected void setPlanPct(Double pPlan) {
this.pPlan = pPlan;
update();
}
protected DataNotifier getOurDataModel() {
return ourDataModel;
}
protected void setOurDataModel(DataNotifier ourDataModel) {
this.ourDataModel = ourDataModel;
}
protected MyData(Double pTarget, Double pActual, Double pPlan, String color, String shape, Grid parent) {
super();
this.pTarget = pTarget;
this.pActual = pActual;
this.pPlan = pPlan;
this.color = color;
this.shape = shape;
this.parent = parent;
}
/** Fire off an udpate to the listener */
protected void update() {
if (ourDataModel != null) ourDataModel.update();
}
}
} // END OF GRID
public class TableTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
Grid runwindow = new Grid();
runwindow.show();
}
});
}
}
和一張圖片
截屏
謝謝。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/456053.html
