默认情况下,我使用的是表行条带的外观和感觉。当我输入一个JXTreeTable时,我注意到由于某种原因,它没有自动得到行条。
所以我用荧光笔解决了这个问题,但是看起来我又出现了一个小故障:

似乎JXTreeTable只是在重新绘制文本的边界,而不是整个单元格。我一直试图在调试器中找到原因,但是每次我在程序之间切换时,整个窗口都会重新绘制,所以几乎不可能捕捉到这类事情。
JTable和JTree都表现得很正常。这种外观和感觉描绘了JTree的整个行(比如Quaqua和Synth),所以这可能也与它有关。也许JXTreeTable有某种假设,即外观和感觉不会画出树的行?如果是的话,有没有办法解决这个问题?不仅仅是这种外观和感觉才是问题所在。
守则:
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableModel;
import org.trypticon.haqua.HaquaLookAndFeel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Arrays;
public class TreeTableDemo2 implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TreeTableDemo2());
}
@Override
public void run() {
try {
UIManager.setLookAndFeel(new HaquaLookAndFeel());
} catch (Exception e) {
throw new RuntimeException(e);
}
JFrame frame = new JFrame("Tree Table Demo");
frame.setLayout(new BorderLayout());
frame.add(createPanel(), BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public JPanel createPanel() {
JPanel panel = new JPanel(new BorderLayout());
TreeTableModel treeTableModel = new DummyTreeTableModel();
JXTreeTable treeTable = new FixedTreeTable(treeTableModel);
JScrollPane treeTableScroll = new JScrollPane(treeTable);
panel.add(treeTableScroll, BorderLayout.CENTER);
return panel;
}
private static class FixedTreeTable extends JXTreeTable {
private static final Highlighter oddRowHighlighter = new AbstractHighlighter() {
@Override
protected Component doHighlight(Component component, ComponentAdapter componentAdapter) {
if (componentAdapter.row % 2 != 0 &&
!componentAdapter.isSelected()) {
component.setBackground(UIManager.getColor("Table.alternateRowColor"));
}
return component;
}
};
public FixedTreeTable(TreeTableModel treeModel) {
super(treeModel);
// This hack makes it paint correctly after releasing the mouse, which is not quite good enough.
// getSelectionModel().addListSelectionListener(new ListSelectionListener() {
// @Override
// public void valueChanged(ListSelectionEvent e) {
// Rectangle repaintRange = getCellRect(e.getFirstIndex(), 0, true);
// repaintRange.add(getCellRect(e.getLastIndex(), 0, true));
// repaint(repaintRange);
// }
// });
}
@Override
public void updateUI() {
removeHighlighter(oddRowHighlighter);
super.updateUI();
// JTable does this striping automatically but JXTable's default renderer
// seems to ignore it, so JXTreeTable inherits this broken behaviour.
if (UIManager.get("Table.alternateRowColor") != null) {
addHighlighter(oddRowHighlighter);
}
}
}
private static class DummyTreeTableNode extends DefaultMutableTreeTableNode {
private final Object[] values;
private DummyTreeTableNode(String name) {
super(name);
values = new Object[5];
values[0] = name;
}
private DummyTreeTableNode(Object... values) {
super(values[0]);
this.values = values;
}
@Override
public Object getValueAt(int column) {
return values[column];
}
}
private static class DummyTreeTableModel extends DefaultTreeTableModel {
private static DefaultMutableTreeTableNode rootNode = new DefaultMutableTreeTableNode();
static {
DefaultMutableTreeTableNode blue = new DefaultMutableTreeTableNode("Blue");
blue.add(new DummyTreeTableNode("Orionis C", 33000, 30000.0, 18.0, 5.90));
rootNode.add(blue);
DefaultMutableTreeTableNode bluish = new DefaultMutableTreeTableNode("Bluish");
bluish.add(new DummyTreeTableNode("Becrux", 30000, 16000.0, 16.0, 5.70));
bluish.add(new DummyTreeTableNode("Spica", 22000, 8300.0, 10.5, 5.10));
bluish.add(new DummyTreeTableNode("Achernar", 15000, 750.0, 5.40, 3.70));
bluish.add(new DummyTreeTableNode("Rigel", 12500, 130.0, 3.50, 2.70));
rootNode.add(bluish);
DefaultMutableTreeTableNode blueWhite = new DefaultMutableTreeTableNode("Blue-White");
blueWhite.add(new DummyTreeTableNode("Sirius A", 9500, 63.0, 2.60, 2.30));
blueWhite.add(new DummyTreeTableNode("Fomalhaut", 9000, 40.0, 2.20, 2.00));
blueWhite.add(new DummyTreeTableNode("Altair", 8700, 24.0, 1.90, 1.80));
rootNode.add(blueWhite);
DefaultMutableTreeTableNode white = new DefaultMutableTreeTableNode("White");
white.add(new DummyTreeTableNode("Polaris A", 7400, 9.0, 1.60, 1.50));
white.add(new DummyTreeTableNode("Eta Scorpii", 7100, 6.3, 1.50, 1.30));
white.add(new DummyTreeTableNode("Procyon A", 6400, 4.0, 1.35, 1.20));
rootNode.add(white);
DefaultMutableTreeTableNode yellowWhite = new DefaultMutableTreeTableNode("Yellow-White");
yellowWhite.add(new DummyTreeTableNode("Alpha Centauri A", 5900, 1.45, 1.08, 1.05));
yellowWhite.add(new DummyTreeTableNode("The Sun", 5800, 100.0, 1.00, 1.00));
yellowWhite.add(new DummyTreeTableNode("Mu Cassiopeiae", 5600, 0.70, 0.95, 0.91));
yellowWhite.add(new DummyTreeTableNode("Tau Ceti", 5300, 0.44, 0.85, 0.87));
rootNode.add(yellowWhite);
DefaultMutableTreeTableNode orange = new DefaultMutableTreeTableNode("Orange");
orange.add(new DummyTreeTableNode("Pollux", 5100, 0.36, 0.83, 0.83));
orange.add(new DummyTreeTableNode("Epsilon Eridani", 4830, 0.28, 0.78, 0.79));
orange.add(new DummyTreeTableNode("Alpha Centauri B", 4370, 0.18, 0.68, 0.74));
rootNode.add(orange);
DefaultMutableTreeTableNode red = new DefaultMutableTreeTableNode("Red");
red.add(new DummyTreeTableNode("Lalande 21185", 3400, 0.03, 0.33, 0.36));
red.add(new DummyTreeTableNode("Ross 128", 3200, 0.0005, 0.20, 0.21));
red.add(new DummyTreeTableNode("Wolf 359", 3000, 0.0002, 0.10, 0.12));
rootNode.add(red);
}
private static final Object[] columnNames = {
"Star", "Temperature (K)", "Luminosity", "Mass", "Radius"
};
public DummyTreeTableModel() {
super(rootNode, Arrays.asList(columnNames));
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return String.class;
} else {
return Double.class;
}
}
@Override
public boolean isCellEditable(Object node, int column) {
return false;
}
}
}进一步调查第1轮:
最后,我通过在JXTreeTable中查看paint方法,成功地捕获了调试器中的条件。我看到的是,它有一种叫做ClippedTreeCellRenderer的东西,它有一些看起来与可疑行为相对应的字段:
iconRect = {java.awt.Rectangle@2946}"java.awt.Rectangle[x=20,y=92,width=16,height=16]"
textRect = {java.awt.Rectangle@2947}"java.awt.Rectangle[x=20,y=-17,width=62,height=15]"
itemRect = {java.awt.Rectangle@2948}"java.awt.Rectangle[x=20,y=36,width=103,height=18]"我还没有确认它确实使用这个值来绘制矩形,但是textRect正好是它重新绘制的小窗口的大小。所以现在的问题是,JXTreeTable到底是从哪里提取这些值的,为什么要使用它们呢?
我的直觉告诉我,JXTreeTable的渲染器在某种程度上使用树状细胞渲染器来直接渲染单元格,而不仅仅是让树自己绘制。绘制行背景和展开/折叠图标的逻辑是在树中,而不是在单元格中,所以如果它这样做,那么它没有一贯地绘制树就有意义了。
进一步调查第2轮:
我想我走错路了。看起来整行都正在绘制,但是tree.isPathSelected(路径)返回新选定的蓝色行的false,并为刚刚被选中的行返回true。
我可以通过DefaultTreeSelectionModel中的断点来确认,只有在释放鼠标之后才更新树选择,这就是为什么它最终返回到正确呈现的原因。
我将深入研究JXTreeTable,看看它是如何保持这些同步的。
发布于 2014-05-24 04:31:19
我找到窃听器了。它看起来像是JXTreeTable.java中一些善意的代码,它试图减少更新:
/**
* Class responsible for calling updateSelectedPathsFromSelectedRows
* when the selection of the list changse.
*/
class ListSelectionHandler implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
updateSelectedPathsFromSelectedRows();
}
}
}如果删除该if检查,一切都会正常工作。我想我只需要对SwingX做一些本地更改,因为这个项目基本上已经死了。:(
https://stackoverflow.com/questions/23832023
复制相似问题