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.
Related
I have some components which I need to use setBounds() on, hence the reason why I'm using the setLayout(null).
But some of my components are out the window(below the Y-axis). I was wondering if there is a way to add a scrollbar to navigate down the window so as to see all the remaining components. A screenshot of my window is below.
Output of my window image:
That GUI would be simple to produce using layouts. Put the component displaying the list (which looks well suited to being a JTable, given the two pieces of data per row / line) into a JScrollPane. Put the scroll pane into the CENTER of a BorderLayout. Put the red label into the PAGE_START of the border layout. Then .. oh wait, the job is done!
This is what it might look like (using a JTextArea instead of a table).
can u please post a copy of this code.
Try implementing it based on the instructions above. If there is a problem, post a minimal reproducible example of your attempt.
Since you are refering to the items in the scrolling area as components, and not as texts in a JTextArea, please have a look at the below.
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Mainframe {
private JFrame f;
Box box;
JScrollPane scrollPane;
Random rand = new Random();
public static void main(String[] args) {
new Mainframe().go();
}
private void go() {
box = new Box(BoxLayout.Y_AXIS);
JLabel label = new JLabel("Possible Paths and Total Distances");
label.setForeground(Color.RED);
for (int i = 0; i < 200; i++) {
box.add(Box.createRigidArea(new Dimension(0, 2)));// creates space between the components
box.add(new JLabel(i + " : " + rand.nextInt(10000)));
}
scrollPane = new JScrollPane(box);
Dimension dim = new Dimension(box.getComponent(0).getPreferredSize());
scrollPane.getVerticalScrollBar().setUnitIncrement(dim.height * 2); // adjusts scrolling speed
//scrollPane.getViewport().setBackground(Color.WHITE);
f = new JFrame();
f.getContentPane().add(label, BorderLayout.NORTH);
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(640, 480);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
I have been searching for a while now, but couldnt find a solution so I have decided to ask here.
I am using Java Swing for my gui implementation of calculator. I have custom made layout(which works correctly 100%). I have added all buttons and all buttons are positioned correctly, always. Last component I have inserted is "Inv" and it is checkbox which I cant find a way to center it inside its area. I have tried putting it in panel,in panel with borderlayout.center, setting the horizontal and vertical text alignment, but nothing works.
invert = new JCheckBox("Inv");
invert.setBackground(Color.decode("#8DA336"));
invert.addActionListener(new CommandListener(this,"invert"));
container.add(invert, new RCPosition(5, 7));
This RCPosition is nothing more than object which says in which row and column this component is (nothing wrong with that).
Checkbox is by default left-aligned. Try make it center-aligned:
invert = new JCheckBox("Inv");
invert.setHorizontalAlignment(SwingConstants.CENTER);
// styling and add to container
If it don't help, then you should publish your layout manager.
You could try putting it in a JPanel with BoxLayout, then add horizontal glue on the left and right.
final JFrame frame = new JFrame();
final JPanel jp = new JPanel();
jp.setLayout(new BoxLayout(jp, BoxLayout.X_AXIS));
jp.add(Box.createHorizontalGlue());
final JCheckBox jcb = new JCheckBox("inv");
jp.add(jcb);
jp.add(Box.createHorizontalGlue());
frame.getContentPane().add(jp);
frame.pack();
frame.setLocationRelativeTo(null);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
This is just one way to do it, setHorizontalAlignment should work as well.
So I've been looking at other solutions to this problem, but it seems that none of them help at all. Can someone help me?
Code:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Container cont = frame.getContentPane();
JPanel buttonpanel = new JPanel();
JButton[] button = new JButton[12];
JTextArea score1, score2;
score1 = new JTextArea(100, 200);
score2 = new JTextArea(100, 200);
frame.setSize(800, 600);
frame.setVisible(true);
score1.setLayout(null);
score1.setBackground(Color.BLUE);
score2.setLayout(null);
score2.setBackground(Color.RED);
score1.setLocation(0, 100);
score2.setLocation(700, 100);
frame.add(score1);
for (int i = 0; i < button.length / 2; i++) {
button[i].setBounds(100 * (i + 1), 100, 100, 100);
button[i].setBackground(Color.GRAY);
buttonpanel.add(button[i]);
}
frame.add(buttonpanel);
frame.add(score2);
// frame.add(panel);
}
It just gives me window completely blue.
You're seeing all blue because you're adding score1, an all blue JTextArea to the JFrame's contentPane, a container that uses a BorderLayout, and this makes the JTextArea fill the contentPane, leaving nothing but blue. Nothing else is added because the NullPointerException that is caused by your using a JButton array that is filled with nulls. Once you fix this bug (which you never told us about), you'll see nothing but red because of the same issue.
The best solution, which you've undoubtedly read about is to use layout managers to the best advantage to create your GUI. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Other problems:
You're calling setVisible(true) on your JFrame before adding anything to it which is backwards. You want to make this call after everything has been added, so that it will show in the JFrame when it is displayed.
You're setting the layout of a JTextArea which doesn't make sense since you'll never want to use these as containers for other components.
Also understand that this isn't doing what you think it's doing: JTextArea(100, 200);. You're not creating a JTextArea that is 100 by 200 pixels but rather is 100 rows by 200 columns which is one amazingly huge JTextArea. You'll want to read the documentation, the API when using unfamiliar components.
You state, "So I've been looking at other solutions to this problem, but it seems that none of them help at all." Likely the "other solutions" have recommended that you use layout managers, and rather than state that they don't "help at all", you should instead strive to learn how to use these tools since those other solutions are correct.
And yeah, you're trying to use JButtons in a JButton array that have not yet been created. Understand that an array of reference type, such as an array of JButton is initially filled with nothing but null references, similar to an empty carton of eggs. Just like you can't use any eggs from the carton until you put eggs in there, you can't use an JButtons in your array before you've placed them in there. In the for loop where you iterate through the array, create each JButton item on the top line: button[i] = new JButton("Something");
You can find links to the Swing tutorials and to other Swing resources here: Swing Info
For example, you tell me which is easier to debug, your code, or this code:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.*;
public class Foo2 extends JPanel {
private static final String[] BUTTON_TEXTS = { "A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "l" };
private static final int GAP = 3;
private JTextArea textArea1 = new JTextArea(20, 30);
private JTextArea textArea2 = new JTextArea(20, 30);
public Foo2() {
JPanel textAreaGrid = new JPanel(new GridLayout(1, 0, GAP, GAP)); // gridlayout 1 row
textAreaGrid.add(new JScrollPane(textArea1));
textAreaGrid.add(new JScrollPane(textArea2));
JPanel buttonPanel = new JPanel(new GridLayout(2, 0, GAP, GAP)); // 2 rows
for (String btnText : BUTTON_TEXTS) {
buttonPanel.add(new JButton(btnText));
}
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP)); // main GUI uses border layout
add(textAreaGrid, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
}
private static void createAndShowGui() {
Foo2 mainPanel = new Foo2();
JFrame frame = new JFrame("Foo2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which displays as:
I'm trying to add a JScrollPane to my JList but for whatever reason it isn't working. I've read several tutorials both on here and other sites and it seems i'm following the directions correctly. I've tried it using both a DefaultListModel and no DefaultListModel seeing if it would make a difference. I've also tried resizing the widget itself and that doesn't work, either.
Here is my code. itemNames is an array of Strings[] which contain various souvenir names that i'm adding to the JList. i'm using BorderLayout() and the panel i'm attempting to add the JScrollPane to is utilizing a GridBagLayout():
souvenirList = new JList(itemNames);
souvenirList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
souvenirList.setLayoutOrientation(JList.VERTICAL);
souvenirList.setVisibleRowCount(-1);
scrollPane = new JScrollPane(souvenirList);
gbc3.gridx = 1;
gbc3.gridy = 1;
centerPanel.add(scrollPane, gbc3);
c.add(centerPanel, BorderLayout.CENTER);
The box shows up with the names in it, but no JScrollPane :( I was hoping someone could help me out, maybe point out a simple mistake I may be making. Thanks to all in advance. Please don't post links to tutorials on the topic cus believe me, i've read them thoroughly.
EDIT: In this small runnable example I built, using mostly code from my previous snippet, the bar shows up fine! Why could this be? Could it have something to do w the JPanel i'm adding it to? Or maybe the GridBagLayout? I'm so confused here....
import javax.swing.*;
import java.awt.*;
public class JScroll extends JFrame
{
JList souvenirList;
JScrollPane scrollPane;
Container c = getContentPane();
private String[] itemNames = {"mug","cap","tee shirt","sweat shirt","pennant","mini stick",
"bobblehead","paper bag","foam paw","thunderstix"};
public JScroll()
{
setLayout(new FlowLayout());
souvenirList = new JList(itemNames);
souvenirList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
souvenirList.setLayoutOrientation(JList.VERTICAL);
//souvenirList.setVisibleRowCount(-1);
scrollPane = new JScrollPane(souvenirList);
c.add(scrollPane);
}
public static void main(String[] args)
{
JScroll frame = new JScroll();
frame.setSize(400,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
The issue was centered around the fill() method within GridBagLayout(). I didn't notice it was set to VERTICAL, meaning the JList was going to request more vertical space. Changing to HORIZONTAL fixed the issue.
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.
*/
}
}