JScrollPane triggering weird AdjustmentEvent when adding components - java

I'm using a JScrollPane with a BoxLayout (a Box, although I've tried a GridLayout too) that shows a list of paginated elements, each page has 5 elements and each element has 5 visual components. The data to fulfill the elements is retrieved from a web service, on load an every time the page changes, removing the current elements in the list and adding the new ones (I know this could be optimized but that's what I have for now).
The weird thing here is that, both on load and when changing page, the vertical scroll bar is moved to some position in the middle, not top, not bottom and not exact center. I've tried to move it up programatically after adding the components, but the scroll is put again in that position after the data presentation method finishes. Then, I can use the scroll bar normally, until the next page change.
Actually, I've attached an AdjustmentListener to the JScrollBar and I can count 25 times (5*5, duh!) that an AdjustmentEvent event that moves the scrollbar to that position is triggered - and yes, if I add less components the event is triggered less times.
I have tried calling revalidate() and repaint() on the window before moving the scroll bar but nothing seem to work, it keeps getting moved to that particular position once per added component.
Any ideas? I'm sorry for not posting some piece of code, but it is kind of a mess and it's difficult to extract the essential parts that could be causing the problem.
Thanks a lot.
Update
Responding to #kleopatra, I'll try to make up some code similar to what I'm doing, although maybe I'm skipping something that somehow causes the strange behavior.
public class MyUI extends JPanel {
List<Data> data;
JPanel dataItemsPanel;
JScrollPane scrollPane;
// ...
/**
* Method to build the UI.
*/
void createUI() {
this.setLayout(new BorderLayout());
// ...
JPanel left = new JPanel();
JPanel right = new JPanel(new BorderLayout());
// ...
dataItemsPanel = new JPanel(new GridLayout(0, 1));
// this is the scroll pane that behaves weird
scrollPane = new JScrollPane(dataItemsPanel);
//...
right.add(scrollPane, BorderLayout.CENTER);
JSplitPane splitPane = new JSplitPane(JSPlitPane.HORIZONTAL_SPLIT,
left, right);
splitPane.setResizeWeight(0);
splitPane.setOneTouchExpandable(true);
splitPane.setContinuousLayout(true);
this.add(splitPane, BorderLayout.CENTER);
// ...
loadPage(0);
}
/**
* Method to load a data page. Called when the program starts (to load
* the first page) and whenever the data page is changed.
*/
void loadPage(int page) {
data = retrievePageDataFromWebService(page);
// remove previous data
dataItemsPanel.removeAll();
// add new data
for (Data d : data) {
// build complex data item representation
JPanel description = new JPanel(new FlowLayout(FlowLayout.LEFT));
description.add(new JEditorPane(/*...*/));
JPanel options = new JPanel(new FlowLayout(FlowLayout.LEFT));
options.add(new JButton(/*...*/));
options.add(new JButton(/*...*/));
JPanel leftDataPanel = new JPanel(new BorderLayout());
leftDataPanel.add(new JEditorPane(/*...*/));
JPanel rightDataPanel = new JPanel(new BorderLayout());
rightDataPanel.add(new JEditorPane(/*...*/));
rightDataPanel.add(options, BorderLayout.CENTER);
JPanel data = new JPanel(new GridLayout(1, 2));
data.add(leftDataPanel);
data.add(rightDataPanel);
JPanel dataItemContainer = new JPanel(new BorderLayout());
dataItemContainer.add(description, BorderLayout.NORTH);
dataItemContainer.add(data, BorderLayout.CENTER);
// finally add it to the data panel
dataItemsPanel.add(dataItemContainer);
}
/*
After this method finishes, an AdjustmentEvent is called once per
added component (25 times). Trying to set the scroll bar position
at this point has no effect, as the events are triggered after the
method. I have tried to call revalidate() and repaint() here and
then move the scroll bar but the result is the same.
*/
}
}

Related

JScrollPane drawing components under itself

I have a JScrollPane inside of a JTabbedPane tab. Into that JScrollPane I place a JPanel whose background is red and whose size is explicitly set.
This is what I get:
Note that in the second image, where the container frame has been resized, the component is being drawn under the scrollbar elements.
What's going on here? I've tried just about every combination of layout managers for all the components involved (as well as components besides JPanel - ultimately I want several JEditorPane here), and I cannot get anything to draw except under a big, blank, rectangle (is it a background? which?)
Code:
In main JFrame subclass:
// Called once on startup/layout.
public void refreshGUIState() {
for (int i = 0; i < client.getFrameStackLength(); i++) {
InferenceFrame frame = client.getFrame(i);
JScrollPane pane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
JPanel wtf = new JPanel();
pane.setBackground(Color.YELLOW);
wtf.setBackground(Color.red);
wtf.setSize(100,100);
wtf.setPreferredSize(wtf.getSize());
pane.add(wtf);
workspace.addTab(Integer.toString(i), pane);
}
}
public GuiClient() throws CliException, ParticleSourceException {
super("L4 Interactive Prover");
setSize(800,600);
mainArea = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
mainArea.setDividerSize(2);
mainArea.setBackground(Color.DARK_GRAY);
getContentPane().add(mainArea);
/* ... menu bar init ... */
toolspace = new JPanel();
workspace = new JTabbedPane();
mainArea.add(toolspace);
mainArea.add(workspace);
this.validate();
refreshGUIState();
}
Edit: under the suspicion that JScrollPane was somehow not creating a viewport when its first child was added, as expected, I also tried the three-argument constructor. Same behavior.
Re-edit: Except I erred and left in the add() call. Whoops. Looks like that's it.
Instead of
pane.add(wtf);
you need
pane.setViewportView(wtf);
You can also do it when initialising the JScrollPane:
JScrollPane pane = new JScrollPane(wtf);
Or, in your case:
JScollPane pane = new JScrollPane(wtf, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

BoxLayout Displaying Incorrectly

I'm trying to create a pretty simple application that has a JSplitPane (which is divided into a JTabbedPane and a JPanel) above a status bar panel. I want to use a simple layout (i.e. BoxLayout, FlowLayout, or BorderLayout), but I've tried and they all give me the same error. I've simplified the code as much as possible to show the error.
The error is that there should only be 2 regions in the main box layout (the frame): a top (with the JSplitPane, which has the black border) and a bottom (with the JPanel status bar). However, when I add the status bar, a third region is created in the upper left that contains nothing. Any ideas on how to get rid of it?
public static void main(String[] args) {
JFrame frame = new JFrame("Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
// Create left side of the application
JTabbedPane tabby = new JTabbedPane(JTabbedPane.LEFT);
// Create right side of the application
JPanel rightPanel = new JPanel(new BorderLayout());
// Create the status bar at the bottom
JPanel statusBar = new JPanel(new BorderLayout());
JPanel statusBarPanel = new JPanel();
statusBarLabel = new JLabel("Status Bar");
statusBarPanel.add(statusBarLabel);
parent.add(statusBarPanel);
JSplitPane mainPain = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tabby, rightPanel);
frame.add(mainPain);
frame.add(statusBar);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
I'm trying to create a pretty simple application that has a JSplitPane (which is divided into a JTabbedPane and a JPanel) above a status bar panel.
Normally you would just use the default BorderLayout of the frame and then do:
frame.add(splitPane, BorderLayout.CENTER);
frame.add(statusBar, BorderLayout.PAGE_END);
A status bar is typically one or more labels that display information so they are display in a fixed size at the bottom.
The other panel will then contain the main components of the application. These components will then get any extra space available to the frame as it is resized.
but I've tried and they all give me the same error
parent.add(statusBarPanel);
The variable "parent" doesn't exist. Get rid of it. Add the status bar to the frame as shown above.
not sure if thats it, but it seems, like you add 2 Panels when adding the Statusbar.
You got a statusBarPanel which is added to "parent", and statusBar, which is added to the frame itself. Maybe thats your 3rd Panel.

Resizing a JScrollPane based on window size

After much searching, I hope this will bring me an answer.
Ok, I have a JFrame, which has a a JPanel across the top, and one across the bottom. It also has one on the side, which contains a JScrollPane. The top and bottom panels should remain a consistent size with the window resizing, but the side panel should change vertically. Unfortunately, no scrollbar shows up at all when the JScrollPane has too many items. Rather, the entire window is enlarged, pushing the bottom panel and all excess within the JScrollPane off-screen.
I have been using MigLayout, but if I need to use another layout for the side panel I can. Here is my most recent failed iteration of code.
This is where I add the JScrollPane:
public MenuPanel(){
this.setLayout(new BorderLayout());
innerPanel = new InnerPanel();
jsp = new JScrollPane(innerPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(jsp, BorderLayout.CENTER);
}
This is inside the main window:
private void addSideSelectionPane() {
side = new SelectionPanel();
this.add(side, "wmax 200, growy");
}
And here is the code where I create the main window:
public InsWindow(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState( this.getExtendedState()| java.awt.Frame.MAXIMIZED_BOTH );
this.setLayout(new MigLayout("debug, nogrid, fill", "[grow, fill]", "[pref!]10[grow, fill]"));
this.addTestLabel();
this.addSideSelectionPane();
this.addMainWindow();
this.addBottomPanel();
this.setVisible(true);
}

How to use the JScrollPane in Java

How can I get the scroller around my JList component in the code given below? It doesn't seem to work properly :(
public class JButtonO extends JFrame{
String[] values = {"henry", "Michael","Uche","John","Ullan","Nelly",
"Ime","Lekan","Austine","jussi","Ossi","Imam","Empo","Austine","Becky",
"Scholar","Ruth", "Anny"};
public JButtonO()
{
super("the button");
this.setSize(400,200);
JPanel panel = new JPanel();
JLabel label = new JLabel("Output Items:");
label.setAlignmentX(1);
label.setAlignmentY(1);
JList conList = new JList(values);
conList.setVisibleRowCount(3);
JScrollPane scroller = new JScrollPane(conList);
panel.add(label);
panel.add(scroller);
panel.add(conList);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.setVisible(true);
}
Adding the JScrollPane scroller that includes the JList conList to the JPanel panel is enough.
The mistake is that you are adding the JList a second time.
JScrollPane scroller = new JScrollPane(conList);
panel.add(label);
panel.add(scroller);
panel.add(conList); // <---THIS LINE SHOULD BE DELETED...
Look, I may not answering what you need, because I don´t remember to much of swing layout. I don´t work with it a long time ago...
But removing setting a layout (I remember) on your JPanel it works with this code:
public JButtonO() {
super("the button");
this.setSize(400, 200);
// Create a panel with a borderlayout
JPanel jpanel = new JPanel(new BorderLayout());
JLabel label = new JLabel("Output Items:");
label.setAlignmentX(1);
label.setAlignmentY(1);
// Add Label to top of layout
jpanel.add(label, BorderLayout.NORTH);
JList conList = new JList(values);
conList.setVisibleRowCount(3);
JScrollPane scroller = new JScrollPane(conList);
//AddScroll to center
jpanel.add(scroller);
//Add Panel to JFrame
this.add(jpanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
I think the problems is the default layoutmaneger of JPanel. Because of how it works your scroll was not "srink" enough to create scrolls...
Hope it helps, even without too much explanation...
ACTUALLY: After I post the answer I saw your mistake. Now I can explain what is wrong. You already added your JList inside your JScrollPane here:
JScrollPane scroller = new JScrollPane(conList);
But after that you put it inside the JPanel:
panel.add(conList);
this changes where yout JList will be displayed, and let the JScroll empty again. Without components it will be displayed with size 0x0 and will not be draw (even being there).
Now I think I helped =D
The JScrollPane has settings called the scrollbar policies which say when the scrollbars are to be displayed. You can set them using JScrolPane(Component,int,int) constructor, or by calling setVerticalScrollBarPolicy() and setHorizontalScrollBarPolicy(). The default policies are "as needed", meaning the scrollbar is only displayed if the component is too large to display whole. So if your list fits inside the window, the scrolbars will not be visible, but will become visible when you e.g. make the window smaller using the mouse. You can change one or both policies to "always" using corresponding constants in order to make the scrollbar(s) always visible if that's what you need.

JTable selects wrong cell on click

I am working inside of a quite complex eclipse based application, and having a problem with a JTable based custom component inside of a JSplitPane. The part of the application that I actually have access to is a panel, within a tab, within a panel, within the actual application, so there are a lot of things that can go wrong.
The specific problem that I'm having right now is that the table component is selecting the wrong cell when I click on it. If I select a cell in row 0, column 0, the cell that actually gets selected is at row 2, column 0, which is about 20 pixels below the actual click. This only happens if the table is in a JSplitPane though: if I just add the table itself to a panel, cell selection is correct.
What it seems like to me is that because the table is in a JSplitPane, the boundaries of the table (or maybe the viewport of the scroll pane containing the table?) are off by about 20 pixels somewhere. Another problem that I had which can back this theory up, is that scrolling the table caused repaints above the table: so for example, as I scrolled down, instead of the table scrolling, it actually moved upwards (painting over the components above the table) about 20 pixels before scrolling. I was able to workaround this problem by adding
jscrollpane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
to the scrollpane that contained the table.
Because of all the custom components involved, I can't actually get a small app that shows the problem, but I have the next best thing, which is an app that shows the layout that I have (of course, it doesn't actually have the same problems). Any ideas on what might be causing the problem?
//Test class showing layout of table/splitpane
import javax.swing.*;
import java.awt.*;
public class SplitTest
{
private static JFrame frame;
private static JPanel buildTable()
{
JPanel tblPanel = new JPanel();
tblPanel.setLayout(new BorderLayout());
String[] cols = new String[]{"one", "two", "three", "four", "five", "six", "seven"};
Object[][] data = new Object[30][7];
for(int x = 0;x < data.length;x++)
for(int y = 0;y < data[x].length;y++)
data[x][y] = x + ", " + y;
JTable tbl = new JTable(data, cols);
JScrollPane scrollPane = new JScrollPane(tbl);
tblPanel.add(scrollPane, BorderLayout.CENTER);
return tblPanel;
}
private static JPanel buildTab()
{
JPanel pnl = new JPanel();
pnl.setLayout(new BorderLayout());
JPanel menuPnl = new JPanel();
menuPnl.setLayout(new FlowLayout(FlowLayout.LEFT));
menuPnl.add(new JLabel("label"));
menuPnl.add(new JComboBox(new String[]{"one", "two"}));
menuPnl.add(new JButton("Button"));
pnl.add(menuPnl, BorderLayout.NORTH);
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(buildTable());
JPanel bottomPnl = new JPanel();
bottomPnl.setPreferredSize(new Dimension(800, 200));
bottomPnl.setBackground(Color.RED);
splitPane.setRightComponent(bottomPnl);
splitPane.setDividerLocation(.5);
pnl.add(splitPane, BorderLayout.CENTER);
return pnl;
}
private static JTabbedPane buildGUI()
{
JTabbedPane topLevelTabbedFrame = new JTabbedPane();
topLevelTabbedFrame.addTab("Tab 1", buildTab());
topLevelTabbedFrame.addTab("Tab 2", new JPanel());
topLevelTabbedFrame.addTab("Tab 3", new JPanel());
return topLevelTabbedFrame;
}
private static void createAndShowGUI()
{
frame = new JFrame("Split Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(buildGUI(), BorderLayout.CENTER);
// frame.setSize(new Dimension(800, 600));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
Because of all the custom components involved, I can't actually get a small app that shows the problem, but I have the next best thing, which is an app that shows the layout that I have (of course, it doesn't actually have the same problems).
I was about to tell you the posted code workd just fine, and the I read this.
Anyway, it seems the problem lies in all the custom components you added to the mix. For JTable and JSplitPane work fine alone.
What I would do is to remove components one by one until it works ( probably I will work when the code is similar to the one posted and there is nothing else there )
Or you can go the opposite way which is easier. Start with your sample code and then add more and more components until it fail.
You can take this opportunity to refactor and clean your code and move unneeded components. And even ( why not ) add test cases in the process.
Good luck.
Have you tries running it on a different box to check if its hardware related.
May be related to this bug
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4763448
As it turns out, the problem was with the order that the components were initialized and added to the split pane. So the eventual fix was to delay adding the table to the split pane until after the split pane was actually added to the panel, rather than adding the table to the split pane before adding the split pane to the panel. Making that small change fixed the issue.

Categories