“突出显示”JTable中的特定行

时间:2012-08-08 19:44:11

标签: java swing jtable highlight jtabbedpane

每当一个单元格的内容与来自用户的输入匹配时,我想突出显示JTable中的特定行。以下代码是我迄今为止所做的工作:

JTable table = new JTable(model) {
    public Component prepareRenderer(
            TableCellRenderer renderer, int row,
            int column) {
        Component c = super.prepareRenderer(renderer,
                row, column);
        if (!isRowSelected(row) ) {
            c.setBackground((hashMapcontainer
                    .containsKey(row)) ? Color.GREEN
                    : getBackground());
        }
        return c;
    }
    @Override
    public boolean isCellEditable(int row, int column) {
        return false;
    }
};

注意:hashMapcontainer是一个hashmap,它是源文件中的全局范围。

现在,这在某种程度上有效,我将此JTable添加到JTabbedPane内的JFrame。 JTable在程序的整个运行时间内动态创建。但是,prepareRenderer方法会导致所有创建的JTable中的所有特定单元格都被突出显示。

如何让所有JTable中的单元格保留自己特定的突出显示单元格,而不是让所有JTable中的单元格中都有相同的突出显示单元?

提前致谢!

3 个答案:

答案 0 :(得分:3)

渲染器是“橡皮图章”。这基本上意味着他们将先前的设置带到下一个单元格。

您需要做的是提供“默认”行为

if (!isRowSelected(row) ) {
    c.setBackground((hashMapcontainer
        .containsKey(row)) ? Color.GREEN
        : getBackground());
} else {

    // Define the default background color
    // Don't forget to take into the selection state

}

虽然我个人认为prepareRenderer在这种情况下可能是一个公平的解决方案,但您真的应该探索提供基线渲染器的可能性。这是很多工作要做到正确,但具有可移植的优势(如果您更改表实现)以及允许其他 人们有机会定义给定单元格的突出显示规则,你基本上已经过去了并且被覆盖了,恕我直言。

我还建议您查看JXTable内置的highlighting

答案 1 :(得分:3)

通常,在基本Swing类上重写方法是个坏主意。建议的方法是创建一个Jcomponent来实现TableCellRenderer并将其应用于setDefaultRenderer()的表。请注意,默认情况下,JTable为Object,Number和Boolean类型提供其中的3个。通常,渲染器看起来像这样:

public class MyRenderer extends JLable, implements TableCellRenderer{
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, 
         boolean isSelected, boolean hasFocus, int row, int column) {
    // Set up default state here 
    c.setBackground(Color.white);
    c.setForeground(Color.black);
    // ...
    if (!isRowSelected(row) ) {
        c.setBackground((hashMapcontainer
                .containsKey(row)) ? Color.GREEN
                : getBackground());
    }
    return c;
}

这为您提供了一个可重用的组件,而不是每个创建它的地方都需要扩展JTable。至于在所有表中选择相同的单元格,这是由于isRowSelectedhashMapContainer访问全局状态而不是每个实例状态。所有JComponent都有getClientPropertyputClientProperty。这些允许您将自己的状态对象附加到JTable。然后,您的isRowSelected变为isRowSelected(table, row),只需调用:

MyObject myObj = (MyObject)table.getClientProperty("MySelectionProperty");
myObj.isRowSelected(row);

同样明智的是,hashMapContainer也可以从表中检索到:

MyHashContainer myHash = (MyHash)table.getClientProperty("MyHashContainer");

<强>更新

对于动态生成的表,这几乎是相同的。表创建将如下所示:

JTable t = new JTable();
// other typical table setup, t.setModel(...); etc
t.setDefaultRenderer(String.class, myRenderer);
t.putClientProperty("MySelectionProperty", new MyObject());
t.putClientProperty("MyHashContainer", new MyHashContainer());

值得注意的是,只要渲染器没有状态,就不需要为每个表创建一个实例。我通常会创建一个并将其用于我的所有表格。

以上是对上面渲染器的更新,它不使用全局状态,而是在表格中查找属性:

public class MyRenderer extends JLable, implements TableCellRenderer{
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, 
        boolean isSelected, boolean hasFocus, int row, int column) {

        // Pull hashMapContainer from the per-table client properties
        MyHashContainer hashMapcontainer = (MyHashContainer)table.getClientProperty("MyHashContainer");

        // Set defaults as above            

        if (!isRowSelected(table, row) ) {
            // Same as above
        }
        return c;
    }
    // Private method to check for row selection
    private boolean isRowSelected(JTable t, int row) {
        int[] selectedRows = table.getSelectedRows();
        for (int i = 0; i < selectedRows.length; i++) {
            if (selectedRows[i] == row) {
                return true;
            }
         }
         return false;
    }
}

答案 2 :(得分:0)

我希望我的 JTable 使用高亮颜色显示更新的行,以便轻松查看正在更新的行。这就是我完成它的方式。该解决方案是作为传统 TableCellRenderer 的包装器实现的。像这样使用它:

RowHighlighter highlighter = new RowHighlighter(table);
table.setDefaultRenderer(Date.class, highlighter.wrap(new DefaultTableCellRenderer()));

STEPS 和 PERIOD 允许配置(我猜应该是一个参数)高光如何淡入背景。在本例中,它将在 2 秒内通过 50 步淡出到单元格的背景颜色。这也保留了真实渲染器的背景颜色,包括当前是否选择了行。

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellRenderer;

class RowHighlighter implements TableModelListener, ActionListener {
    
    private static int STEPS = 50;
    private static int PERIOD = 2000;
    
    private Map<Integer, Integer> rowHighlights = new HashMap<>();
    private JTable table;
    
    public RowHighlighter(JTable table) {
        this.table = table;
        table.getModel().addTableModelListener(this);
        Timer timer = new Timer(PERIOD/STEPS, this);
        timer.start();
    }
    
    @Override
    public void tableChanged(TableModelEvent e) {
        int first = e.getFirstRow();
        int last = e.getLastRow();
        for (int i=first; i<=last; i++) {
            rowHighlights.put(i, STEPS);
        }
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if (rowHighlights.size() == 0)
            return;
        Iterator<Map.Entry<Integer, Integer>> it = rowHighlights.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<Integer, Integer> entry = it.next();
            int v = entry.getValue();
            if (v > 1) {
                entry.setValue(v - 1);
            } else {
                it.remove();
            }
        }
        table.repaint();
    }
    
    public TableCellRenderer wrap(TableCellRenderer delegate) {
        return new HighlightingTableCellRenderer(rowHighlights, delegate);
    }
    
    private static class HighlightingTableCellRenderer implements TableCellRenderer {

        private Map<Integer, Integer> rowMap;
        private TableCellRenderer delegate;
        
        public HighlightingTableCellRenderer(Map<Integer, Integer> rowMap, TableCellRenderer delegate) {
            this.rowMap = rowMap;
            this.delegate = delegate;
        }
        
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            Component c = delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            Integer v = rowMap.get(row);
            if (v != null) {
                Color background = c.getBackground();
                Color highlight = getColor(v, Color.PINK, background);
                c.setBackground(highlight);
            }
            return c;
        }
        
        private Color getColor(int n, Color from, Color to) {
            float p = ((float)n)/STEPS;
            
            return new Color((int)(from.getRed() * p + to.getRed() * (1-p)),
                    (int)(from.getGreen() * p + to.getGreen() * (1-p)),
                    (int)(from.getBlue() * p + to.getBlue() * (1-p)));
        }
        
    }

}