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

Posted

技术标签:

【中文标题】“突出显示” JTable 中的特定行【英文标题】:"Highlighting" specific rows in a JTable 【发布时间】:2012-08-06 00:07:03 【问题描述】:

每当单元格的内容与用户的输入匹配时,我想突出显示 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 中。 JTables 是在整个程序运行时动态创建的。但是,prepareRenderer 方法会导致突出显示所有创建的 JTable 中的所有特定单元格。

如何在所有 JTable 中保留单元格以保留它们自己的特定突出显示单元格,而不是让所有 JTable 中的每个单元格都具有完全相同的突出显示单元格?

提前致谢!

【问题讨论】:

听起来像臭名昭著的 DTCR 颜色记忆:***.com/questions/9607670/… 【参考方案1】:

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

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

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

【讨论】:

如果我希望继续使用此代码,您提供的代码 sn-p 是否会在更改 JTabbedPane 的新 JTables 的同时保留以前 JTables 的突出显示行? 好吧,考虑到每个表都是它自己的实例,并且你只在未选择行时更改回“默认”背景,我会说这是一个很好的机会 好的,我认为您可能遇到的另一个问题是确定该行是否实际上属于给定的表。基本上,据我所知,全球地图在说,在所有表格中,突出显示相同的行。这是您想要实现的功能吗? 是的,这就是我现在遇到的问题。它突出显示 LAST JTable 突出显示的行的相同行。我只希望最后一个表进行更改,而之前的 JTables 保持不变。 我会为每个表分离您的选择模型,以便他们获得应该突出显示的自己的列表 rws 或者(我不喜欢这个想法),在您的选择模型中放置一个参考 hich要突出显示特定行的表格【参考方案2】:

一般来说,覆盖基本 Swing 类的方法是个坏主意。推荐的方法是创建一个实现TableCellRendererJcomponent 并将其应用于带有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 访问全局状态而不是每个实例状态。所有JComponents 都有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;
    

【讨论】:

如果我动态添加 JTable 和行会怎样?【参考方案3】:

我希望我的 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)));
        
        
    


【讨论】:

以上是关于“突出显示” JTable 中的特定行的主要内容,如果未能解决你的问题,请参考以下文章

如何在JTable中的特定行中设置颜色

突出显示 JTextArea 中的一个特定行/行

比较两个或多个 JTable 和“突出显示”差异

如果另一列中的相应行包含特定值,我想使用条件格式突出显示一列中的单元格

如何修改此 SQL 查询中的 XML 代码以突出显示表的特定行?

使用 Python pyqt4 获取 QTableWidget 中特定行和列的单元格突出显示