1

I have a JTree inside a JScrollPane, and when I use the scrollbar the tree gets all blurred up, as you can see in the image below.

It gets back to normal if I do something to make it repaint, like minimize and restore the window, or click in the tree to make a node expand or collapse (however the blurring doesn't go away if I drag the window off the screen and back, or drag another window in front of it).

The JTree has a custom TreeModel and cell renderer. The recent change was for the TreeModel; the cell renderer has been there for a long time and was working fine. The cell renderer is a subclass of DefaultTreeCellRenderer, with only the getTreeCellRendererComponent method overridden (to display custom icons).

I used to populate DefaultMutableTreeNodes from a data structure which contained the data to be displayed, but that was giving performance problems when the number of nodes was large (like over 10,000). Since the data I had was already in a tree structure, I realized it would be fairly simple to create a custom TreeModel around it without using any DefaultMutableTreeNodes. That made the JTree populate more quickly, but now I'm left with this blurred scrolling problem.

Blurred JTree

The code below isn't from the application, but it compiles as is and will demonstrate the problem. Removing the tree.setBackground line stops the blurry behavior.

package stackoverflow;

import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.*;
import java.awt.Color;


public class NumberTreeModel implements TreeModel {

   public static final int ROOT_NUMBER = 100;

   public Object getChild(Object parent, int index) {
      return index;
   }

   public int getChildCount(Object node) {
      return isLeaf(node) ? 0 : ROOT_NUMBER;
   }

   @Override
   public int getIndexOfChild(Object parent, Object child) {
      int parentValue = ((Integer) parent).intValue();
      int childValue = ((Integer) child).intValue();
      return parentValue == ROOT_NUMBER ? childValue : -1 ;
   }

   public Object getRoot() {
      return ROOT_NUMBER;
   }

   public boolean isLeaf(Object node) {
      return ((Integer) node).intValue() < ROOT_NUMBER;
   }

   public void addTreeModelListener(TreeModelListener listener) { }
   public void removeTreeModelListener(TreeModelListener listener) { }
   public void valueForPathChanged(TreePath path, Object obj) { }

   public static void display() {

      JFrame frame = new JFrame("Number JTree");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      NumberTree tree = new NumberTree();
      tree.setModel(new NumberTreeModel());
      tree.setBackground(new Color(0,0,0,0));
      JScrollPane scroll = new JScrollPane(tree);
      frame.add(scroll);
      scroll.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE);
      tree.expandRow(0);
      frame.pack();
      frame.setSize(300, 400);
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static class NumberTree extends JTree {
      static final long serialVersionUID = 1;

      @Override
      public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf,
               int row, boolean hasFocus) {
         if (value instanceof Integer) {
            int n = ((Integer) value).intValue();
            return n + "=========".substring(0, n % 10);
         } else {
            System.out.println("value class=" + value.getClass());
            return value.toString();
         }
      }
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            display();
         }
      });
   }
}
Gigatron
  • 1,995
  • 6
  • 20
  • 27
  • Please edit your question to include an [sscce](http://sscce.org/) that exhibits the problem you describe. – trashgod Mar 29 '13 at 03:33
  • 3
    Something some where isn't honoring the transparency requirements of Swing. Ie. Trying to set the background color as a transparent or semi transparent value – MadProgrammer Mar 29 '13 at 03:39
  • After removing the CustomCellRenderer and the overriding of JTree.convertValueToText it still gives the same problem. – Gigatron Mar 29 '13 at 03:56
  • An SSCCE will take a lot of time. I'd have to rewrite the TreeModel, as it is currently tied to a very large class that houses a data structure with the hierarchical data, and this tree is just a small piece of the application and would have to be separated out into its own window and so on. I'll eventually do it if I have to, but hopefully somebody can give me a hint that will lead to a solution without having to rewrite the TreeModel and carve this piece out of the application. – Gigatron Mar 29 '13 at 04:03
  • `new JTree()` has a convenient, built-in model. – trashgod Mar 29 '13 at 04:04
  • I was using the built-in model before, and it was working fine (except for the performance, which is why I replaced it). So it's something to do with my custom TreeModel, which apparently isn't doing something the built-in model and its `DefaultMutableTreeNode`s were doing. – Gigatron Mar 29 '13 at 04:10
  • For reference, "`DefaultTreeCellRenderer` is not opaque."—[API](http://docs.oracle.com/javase/7/docs/api/javax/swing/tree/DefaultTreeCellRenderer.html) – trashgod Mar 29 '13 at 04:20
  • A TreeModel has nothing to do with the actual painting. It just contains the data that gets painted. So I don't see how the model is the cause of the problem. – camickr Mar 29 '13 at 04:27
  • 1
    _An SSCCE will take a lot of time_ ahhh ... in other words: **your** time is too valuable and **our** time is just fine to invest on **your** problem even if posted incompletely? -1 and voting to close. – kleopatra Mar 29 '13 at 10:53
  • 1
    I said I'd write the SSCCE if I have to, and it looks like I'll have to so I'm going to write it today. But it will take a lot of time. I don't expect anybody to spend hours trying to solve this for me, I was only hoping that somebody else who has had this problem before would give me a hint of what is going wrong so I can solve it on my own. – Gigatron Mar 29 '13 at 12:25
  • @camickr, I agree that the TreeModel shouldn't affect the scrolling, but this is what happened when I started using a custom TreeModel, and reverting to a version of the code with the built-in model and DefaultMutableTreeNodes doesn't have this problem. There might be stuff going on inside those built-in model classes, perhaps the interaction with TreeModelListeners, which makes the repainting work better and prevents the blurring. I'll have to take some time later to dive into the source code of those classes. – Gigatron Mar 29 '13 at 14:05
  • This is a real question. Don't close it. – Faustas Nov 20 '13 at 15:53

2 Answers2

2

I suspect you have overridden paintComponent() but neglected super.paintComponent(g), as shown here.

Less likely, you might experiment with setScrollMode() in the scroll pane's viewport.

Editing your question to include an sscce might clarify the problem.

Addendum: For reference, here's an example that does not exhibit the rendering artifact seen in the question.

enter image description here

import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;

/**
 * @see https://stackoverflow.com/a/15696825/230513
 */
public class Sscce {

    private void display() {
        JFrame f = new JFrame("Sscce");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JTree tree = new JTree();
        for (int i = 0; i < tree.getRowCount(); i++) {
            tree.expandRow(i);
        }
        f.add(new JScrollPane(tree));
        f.pack();
        f.setSize(200, 200);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Sscce().display();
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I didn't override `paintComponent` anywhere. I overrode `JTree.convertValueToText` so it would display an appropriate string for each node instead of the default functionality of toString(), and I overrode the `getTreeCellRendererComponent` as mentioned in the question. – Gigatron Mar 29 '13 at 03:31
  • Changing the viewport scroll mode to SIMPLE_SCROLL_MODE stopped the blurring from happening (the other 2 modes still had the blur). But when the tree is large, the GUI freezes for a long time when the tree is being populated. I think there is a need for performance improvement in the `getIndexOfChild` of my custom TreeModel, so I'll work on that and see what happens. – Gigatron Mar 29 '13 at 14:02
  • Although the SIMPLE_SCROLL_MODE solved the scrolling blur problem, now it has another problem where it sometimes doesn't refresh properly when expanding and collapsing nodes. But I think I'm onto a solution. More details this afternoon, including an SSCCE. – Gigatron Mar 29 '13 at 17:03
  • If you make your `TreeCellRenderer` opaque and paint the background, you might be able to use either of the more efficient modes. – trashgod Mar 29 '13 at 17:12
  • It looks like my problem will be solved with a transparent cell renderer, but using null colors instead of transparent colors as in http://stackoverflow.com/questions/14563433/jtree-set-background-of-node-to-non-opaque – Gigatron Mar 29 '13 at 18:50
0

Solution:

  • Don't use setColor to change the background of the JTree with a non-opaque color.

  • For visual transparency of the nodes, implement a custom TreeCellRenderer to return null for getBackgroundNonSelectionColor and getBackground, as described in JTree set background of node to non-opaque

    • setting the viewport mode of the JScrollPane to JViewport.SIMPLE_SCROLL_MODE helps the scroll blur problem, but not necessarily repaint problems with expanding and collapsing ndoes.
Community
  • 1
  • 1
Gigatron
  • 1,995
  • 6
  • 20
  • 27
  • just for clarification: a) setting a semi-transparent background to _any_ component requires setting its opacity property to false, otherwise you'll experience painting artefacts. Technically, it's the same error that @trashgod suspected (overriding paintComponent without calling super) b) DefaultTreeCellRenderer has a rather [weird implementation regarding its background](http://stackoverflow.com/q/13735987/203657) – kleopatra Mar 30 '13 at 09:54