Change JTree row height resizing behavior when rendering

后端 未结 1 1517
执笔经年
执笔经年 2021-01-05 09:21

I want to use a custom TreeCellRenderer containing three text fields only when a node is SELECTED, while use the default renderer when the node is NOT SELECTED. The problem

1条回答
  •  时光说笑
    2021-01-05 10:22

    Some facts:

    • BasicTreeUI keeps a cache of node sizes
    • there's no public api to force it to revalidate that cache
    • node size requirements are assumed to depend exclusively on data, not on visual state, that is change of selection state will not trigger any internal update
    • an aside: setting size inside the renderer/editor has no effect: whatever you do, the ui will change it as it deems appropriate

    On the whole, there is no way to implement your requirement without going dirty. Basically, you have to listen to selection changes - because the renderer has different size requirement in selected vs. not selected - and then do your best to invalidate the ui's internal cache. Basically two options:

    • use reflection to access protected methods of the ui
    • fake model events that would lead to an internal re-calculation of the cache

    Below is a snippet for the first (couldn't make the second work on a quick test, but faintly remember that I did it ...)

    protected TreeSelectionListener createReflectiveSelectionListener() {
        TreeSelectionListener l = new TreeSelectionListener() {
    
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                invalidateLayoutCache();
            }
    
            protected void invalidateLayoutCache() {
                BasicTreeUI ui = (BasicTreeUI) tree.getUI();
                try {
                    Method method = BasicTreeUI.class.getDeclaredMethod("configureLayoutCache");
                    method.setAccessible(true);
                    method.invoke(ui);
                } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
                   e1.printStackTrace();
                }
            }
        };
        return l;
    }
    

    Just found the second - similar dirtyness-level as the first - option:

    protected TreeSelectionListener createFakeDataEventSelectionListener() {
        TreeSelectionListener l = new TreeSelectionListener() {
    
            @Override
            public void valueChanged(final TreeSelectionEvent e) {
                fireDataChanged(e.getOldLeadSelectionPath());
                fireDataChanged(e.getNewLeadSelectionPath());
            }
    
            private void fireDataChanged(TreePath lead) {
                if (lead == null) return;
                DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
                TreeNode last = (TreeNode) lead.getLastPathComponent();
                model.nodeChanged(last);
            }
        };
        return l;
    }
    

    0 讨论(0)
提交回复
热议问题