Re: JTree within JScrollPane: Update visible rect on model change
events
Thanks for your reply, but I think I didn't explain my problem enough:
In your example the tree is scrolled to the newly added entry:
public void treeNodesInserted(TreeModelEvent event) {
tree.scrollPathToVisible(event.getTreePath());
}
but that behaviour interrupts the user. Instead it is necessary that
the same entries are shown --> the user doesn't even recognize any
changes...
If you try this code, you will see the flickering (scrollPathToVisible
is trickering a repaint...):
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeModel;
import java.awt.BorderLayout;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class JTreeRefreshTest {
static class NodeElement {
private String message;
public NodeElement( String message ) {
this.message = message;
}
@Override
public String toString() {
return message;
}
}
static class TestTreeModel extends DefaultTreeModel {
public TestTreeModel( TreeNode root ) {
super( root );
}
public void addNode( DefaultMutableTreeNode parent,
DefaultMutableTreeNode child ) {
parent.insert( child, 0 );
final int childIndex = parent.getIndex( child );
fireTreeNodesInserted( parent, parent.getPath(), new int[]
{childIndex}, new Object[]{child} );
}
}
static class TestPanel extends JPanel {
private DefaultMutableTreeNode rootNode;
private TestTreeModel treeModel;
public TestPanel() {
rootNode = new DefaultMutableTreeNode( new
NodeElement( "Root" ) );
for ( int idx = 0; idx < 100; idx++ ) {
rootNode.add( new DefaultMutableTreeNode( new
NodeElement( "Child " + idx ) ) );
}
treeModel = new TestTreeModel( rootNode );
final MyJTree tree = new MyJTree( treeModel );
final JScrollPane scroll = new JScrollPane( tree );
// scroll.getViewport().add( tree );
setLayout( new BorderLayout() );
add( scroll, BorderLayout.CENTER );
tree.addKeyListener( new KeyAdapter() {
@Override
public void keyReleased( KeyEvent event ) {
if ( event.getKeyCode() == KeyEvent.VK_1 ) {
final DefaultMutableTreeNode newNode = new
DefaultMutableTreeNode( new NodeElement( "New
Node" ) );
treeModel.addNode( rootNode, newNode );
}
}
} );
treeModel.addTreeModelListener( new TreeModelListener() {
public void treeNodesChanged( TreeModelEvent event ) {
}
public void treeNodesInserted( TreeModelEvent event ) {
Rectangle visibleRect = tree.getVisibleRect();
tree.oldDeltaY = tree.getSize().height - visibleRect.y -
visibleRect.height;
}
public void treeNodesRemoved( TreeModelEvent event ) {
}
public void treeStructureChanged( TreeModelEvent event ) {
}
} );
}
private static class MyJTree extends JTree {
public MyJTree( TreeModel model) {
super( model );
}
@Override
public void validate() {
super.validate();
fixVisibleRect();
}
@Override
public void invalidate() {
super.invalidate();
fixVisibleRect();
}
@Override
public void paint( Graphics g ) {
oldDeltaY = -1;
super.paint( g );
}
/**
* Fixes the visible rect
*/
private void fixVisibleRect() {
if ( oldDeltaY > -1 ) {
Rectangle visibleRect = getVisibleRect();
int actualDelta = getSize().height - visibleRect.y -
visibleRect.height;
if ( actualDelta != oldDeltaY ) {
int delta = actualDelta - oldDeltaY;
visibleRect.y += delta;
scrollRectToVisible( visibleRect );
oldDeltaY = -1;
}
}
}
int oldDeltaY = -1;
}
}
public static void main( String[] args ) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLayout( new BorderLayout() );
frame.add( new TestPanel(), BorderLayout.CENTER );
frame.pack();
frame.setVisible( true );
}
}