Resizing JPanels in JScrollPanes
First off, sorry for all these posts about JPanels, JScrollPanes, and resizing. I have been making different posts because the issues in each are not exactly directly related and I didn't want to take each post in a whole new direction a bunch of times.
Anywho, I have a rather interesting issue. So I made a small test program to play around with a functionality of setting JPanel's preferred size based on the painting inside of it. That JPanel was inside of a JScrollPane, which was the ContentPane for the JFrame on the test program. The test program worked as expected, by adding Scroll Bars on the JScrollPane when necessary.
But, when I implemented the same code in my large program (with the same input and all), it was not successful by any means. The Scroll Bars were not added and I am once again very frustrated. The key difference between the test program and the full sized program is that in the full sized program the JScrollPane is not the JFrame's ContentPane (as there are a bunch more elements in the JFrame). It would seem the JPanel does not want to reset its size when its JScrollPane is not the ContentPane. I have attempted revalidating in every location I could think of. I have attempted setting the preferred size of the JPanel is every location I could think of. And, based on advice I found on another messageboard, I attempted to override the JPanel's getPreferredSize() method to set it based on the values I want it to be. All failed to work and I am beginning to struggle with my sanity.
Does anyone have any advice for me that will actually work?
I can't really post any code since there is no way for me to narrow my problem down any smaller than my 200 line test program, where the issue does not occur. Plus, I cannot provide the image and text files the test program and the full program use so the context of the code I would provide wouldn't even be replicable.
EDIT: Ok, a much more interesting piece of the puzzle has been added and now I really am confused.
So my code create an Object that extends JPanel called TutorialPanel. TutorialPanel has a method called loadInput(int) that decides what will be painted to the JPanel, then calls the overwritten paintComponent() method. While painting, it determines what the last Y value was. I then overrode the getPreferredSize() method like this:
Code java:
public Dimension getPreferredSize()
{
Dimension pref = super.getPreferredSize();
pref.height = lastY;
return pref;
}
Now, the user decides what they want painted to the JPanel by using a JTree. I made a half-assed Change Listener for the JTree as I have not extensively researched how to find out what element has been selected. That Change Listener looks like this:
Code java:
tree.addTreeSelectionListener(new TreeSelectionListener(){
public void valueChanged(TreeSelectionEvent e)
{
TreePath path = e.getPath();
System.out.println(path.toString());
if(path.toString().equalsIgnoreCase("[Features, Event Table, Category Overview]"))
{
outputPane.loadInput(0);
outputPane.revalidate();
}
}
});
Now, what is interesting here is that when the user selected the path above, the JPanel paints, but not revalidates. BUT, if the user selects a different path (currently no implementation for other paths) and then reselects the path above, suddenly the TutorialPane revalidates and I have Scroll Bars.
Why doesn't it work the first time but it does the second? Has anything changed?
Re: Resizing JPanels in JScrollPanes
Ok, I have solved my problem, but I'm not sure if I am right about why it happened.
I think the paintComponent() method runs on a separate thread or something. Which would mean that despite the order of my code above, it was actually revalidating before it finished painting, thus before it knew how large the JPanel should be. It also explains why it worked the second time, since the size of the JPanel was already set from the previous paintComponent() call. After I put a revalidate call in my paintComponent() method, it works fine.
Any thoughts on this?
Re: Resizing JPanels in JScrollPanes
Quote:
After I put a revalidate call in my paintComponent() method, it works fine.
no no No No NO! That's one route to 100% CPU usage.
Never never never never change state (and revalidate() does result in a change of state) in a painting method override.
Adding a call to repaint() just after revalidate() may solve your problem. IF that doesn't do it, you need to post a SSCCE.
db
Re: Resizing JPanels in JScrollPanes
Quote:
Originally Posted by
Darryl.Burke
no no No No NO! That's one route to 100% CPU usage.
Never never never never change state (and revalidate() does result in a change of state) in a painting method override.
Adding a call to repaint() just after revalidate() may solve your problem.
Ok, well calling the repaint() method after revalidate() just breaks it again. That doesn't exactly surprise me, since i have already said I tried that and it didnt work.
Quote:
IF that doesn't do it, you need to post a SSCCE.
db
I already explained how I cannot really reduce this down:
Quote:
I can't really post any code since there is no way for me to narrow my problem down any smaller than my 200 line test program, where the issue does not occur. Plus, I cannot provide the image and text files the test program and the full program use so the context of the code I would provide wouldn't even be replicable.
The changing of the paint in the main program is controlled by the user's input. Based on that input, it does about 6 file streams and formats the painting. This is a runtime change from the user's input.
I have done the best I could to narrow this down and I think I did it (to about 130 lines), but I cannot be sure:
Code java:
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import java.awt.event.*;
import javax.swing.JPanel;
import java.text.AttributedString;
import java.text.AttributedCharacterIterator;
import java.awt.font.LineBreakMeasurer;
import java.util.Vector;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.io.File;
import javax.imageio.ImageIO;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.awt.font.FontRenderContext;
import java.awt.Dimension;
import java.awt.font.TextLayout;
public class TutorialPanel extends JPanel
{
int lastInput = -1;
String text0;
AttributedString attributedString;
AttributedCharacterIterator attributedCharacterIterator;
int start,end;
LineBreakMeasurer lineBreakMeasurer;
Vector<String> textBlocks = new Vector<String>();
Vector<BufferedImage> images = new Vector<BufferedImage>();
int lastY;
public TutorialPanel()
{
super();
}
public Dimension getPreferredSize()
{
Dimension pref = super.getPreferredSize();
pref.height = lastY;
return pref;
}
public void loadInput(int n)
{
lastInput = n;
repaint();
}
public void loadEventTableCategoryOverview(Graphics2D g2)
{
String text = "Java is a programming language originally developed by James Gosling at Sun Microsystems (which is now a subsidiary of Oracle Corporation) and released in 1995 as a core component of Sun Microsystems Java platform. The language derives much of its syntax from C and C++ but has a simpler object model and fewer low-level facilities. Java applications are typically compiled to bytecode (class file) that can run on any Java Virtual Machine (JVM) regardless of computer architecture. Java is a general-purpose, concurrent, class-based, object-oriented language that is specifically designed to have as few implementation dependencies as possible. It is intended to let application developers write once, run anywhere. Java is currently one of the most popular programming languages in use, and is widely used from application software to web applications.[9][10] The original and reference implementation Java compilers, virtual machines, and class libraries were developed by Sun from 1995. As of May 2007, in compliance with the specifications of the Java Community Process, Sun relicensed most of its Java technologies under the GNU General Public License. Others have also developed alternative implementations of these Sun technologies, such as the GNU Compiler for Java, GNU Classpath, and Dalvik";
float X = 10, Y = 0;
String[] text0Vals = new String[]{text};
for(int i=0;i<text0Vals.length;i++)
{
attributedString = new AttributedString(text0Vals[i]);
attributedCharacterIterator = attributedString.getIterator();
start = attributedCharacterIterator.getBeginIndex();
end = attributedCharacterIterator.getEndIndex();
lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator,new FontRenderContext(null, false, false));
Dimension size = getSize();
float width = (float) size.width-5;
lineBreakMeasurer.setPosition(start);
int count=0;
while(lineBreakMeasurer.getPosition() < end)
{
TextLayout textLayout = lineBreakMeasurer.nextLayout(width);
Y += textLayout.getAscent();
X = 5;
if(count>0)
X = 25;
textLayout.draw(g2, X, Y);
Y += textLayout.getDescent() + textLayout.getLeading();
count++;
}
}
X = 10;
Y+=10;
lastY = (int)Y;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
System.out.println(lastInput);
if(lastInput==-1)
{
lastY = 0;
return;
}
else if(lastInput==0)
{
loadEventTableCategoryOverview((Graphics2D)g);
}
}
static TutorialPanel panel;
public static void main(String[] args)
{
JFrame frame = new JFrame("Test Frame");
frame.setSize(500,450);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new TutorialPanel();
JScrollPane scrollPane = new JScrollPane(panel,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(400,100));
JPanel panel2 = new JPanel();
JButton button = new JButton("Press to Load");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
panel.loadInput(0);
}
});
panel2.add(button);
panel2.add(scrollPane);
frame.setContentPane(panel2);
frame.setVisible(true);
}
}
And before I get the usual lecture: Yes, there is a reason I am using paint instead of normal text components. This is a small part of a much larger thing and this is the best way of doing it, from the user's point of view.
Re: Resizing JPanels in JScrollPanes
Hmm, I see what you mean about CPU usage.
Any suggestions as to how to fix this?
EDIT:
In the loadInput method, if I change it to be the following:
Code java:
public void loadInput(int n)
{
lastInput = n;
repaint();
super.revalidate();
repaint();
super.revalidate();
}
It seems to be able to adjust the size of the Scrolling to the amount of painting being done, but it still doesn't work the first time something is selected but does the second time something is selected. I'm very confused about what is happening...