Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

JTree within JScrollPane: Update visible rect on model change events

76 views
Skip to first unread message

JSchneider

unread,
Aug 28, 2008, 5:28:33 AM8/28/08
to
Hi,

I have the following requirement:

I have a JTree placed within a JScrollPane. The model of the JTree is
now updated every other second. But the additional entries are added
at the top of the Model (index is nearly always 0).
This is a requirement and can't be changed...
(Think of a mail client that shows the youngest mails at the bottom
and loads the youngest mails first - filling the tree from the bottom
to the top).

Now the problem:
If the user has scrolled to the bottom (he wants to see the youngest
entries) the visible entries change every time an additional row is
added at the top (old mail has been added).

My approach:
I calculate the delta between the bottom of the visible rect and the
size of the tree when an entry has been added (within a
TreeModelListener).
Then I have overridden "validate" and "invalidate" and call
"scrollRectToVisible" with an updated rect, calculated based on the
delta.

That works well - just with one exception. ScrollRectToVisble causes a
repaint with the old visible rect. Therefore the tree is
"jumping" (paints one time with the "wrong" visible rect).
When the scroll mode of the JScrollPane is changed to
"JViewport.BACKINGSTORE_SCROLL_MODE" everything works fine.
Unfortunately this is not an option due to performance issues....

I hope the problem is understandable - any hints or suggestions?
Thanks in advance...

tomaszewski.p

unread,
Apr 27, 2011, 11:48:44 AM4/27/11
to
To: comp.lang.java.gui


"JSchneider" <joha...@familieschneider.info> wrote in message
news:0a56d7bd-4911-480e...@c58g2000hsc.googlegroups.com...


> Hi,
>
> I have the following requirement:
>
> I have a JTree placed within a JScrollPane. The model of the JTree is
> now updated every other second. But the additional entries are added
> at the top of the Model (index is nearly always 0).
> This is a requirement and can't be changed...

[...]


> Now the problem:
> If the user has scrolled to the bottom (he wants to see the youngest
> entries) the visible entries change every time an additional row is
> added at the top (old mail has been added).

[...]


> ScrollRectToVisble causes a
> repaint with the old visible rect. Therefore the tree is
> "jumping" (paints one time with the "wrong" visible rect).

[...]

Try this:

import java.util.Enumeration;
import java.util.List;
import java.util.LinkedList;

import java.awt.BorderLayout;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;

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() {
super();
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 JTree tree = new JTree(treeModel);
final JScrollPane scroll = new JScrollPane();
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() {
@Override
public void treeNodesChanged(TreeModelEvent event) {
}

@Override
public void treeNodesInserted(TreeModelEvent event) {
tree.scrollPathToVisible(event.getTreePath());
}

@Override
public void treeNodesRemoved(TreeModelEvent event) {
}

@Override
public void treeStructureChanged(TreeModelEvent event) {
}
});
}
}

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);
}

}

Przemek

---
* Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

JSchneider

unread,
Apr 27, 2011, 11:48:44 AM4/27/11
to
To: comp.lang.java.gui
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 );
}

}

---

John B. Matthews

unread,
Aug 29, 2008, 12:41:12 PM8/29/08
to
In article
<69535338-c5a3-4946...@25g2000hsx.googlegroups.com>,
JSchneider <joha...@familieschneider.info> wrote:

> 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...):

I am unable to reproduce this using Java 1.5.0_13 on Mac OS 10.4.11. I
scroll to the bottom and press "1". I see the scroll bar shrink with
each addition, but I see no flickering.

Could this be an implementation/platform issue? I believe my platform
double-buffers by default.

[...]

--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews

tomaszewski.p

unread,
Aug 31, 2008, 12:45:51 AM8/31/08
to

Uzytkownik "JSchneider" <joha...@familieschneider.info> napisal w
wiadomosci
news:69535338-c5a3-4946...@25g2000hsx.googlegroups.com...


> 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...):
>

[...]

Hi,

Everything looks fine in Your solution. The only thing with scrollbar is
resizing and I assume that is correct behaviour (JRE 1.6.0_7, Vista, Nvidia
GF 8400).

Przemek


0 new messages