I have a JPanel inside of a JScrollPane inside of a GridBagLayout
that is not scrolling properly. What should happen is that every time the
xxx button is pressed, a new line is added inside of the scrolling pane.
What actually happens is that if xxx is pressed, say 10 times, only the
first seven lines show up and the rest can't be scrolled to. Can anyone
suggest changes to the source code below that will make scrolling behave
properly? I have spent hours on this with no success, trying strategies all
over the Web.
Notes:
The text is split in paintComponent because drawString does
not handle end-of-line characters.
The JPanel inside of JScrollPane inside of GridBagLayout configuration is a necessary part of a much larger
piece of software with the same problem, so I have kept it here.
Thanks.
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class question {
public static void main(String[] args) {
new DrawingGUI();
}
private static class ScrollingPane extends JPanel {
private static final long serialVersionUID = 1L;
String text;
public ScrollingPane() {
setPreferredSize(new Dimension(300, 100));
text = "";
}
public void SetText(String text_x) {text = text + System.lineSeparator() + text_x;}
public void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fm = g.getFontMetrics();
int y = -fm.getHeight();
for (String text : text.split("\n"))
g.drawString(text, 0, y += fm.getHeight());
}
}
static class DrawingGUI extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
JPanel jp;
JScrollPane js;
ScrollingPane sp;
int LineNum;
DrawingGUI() {
LineNum = 0;
JFrame frame = new JFrame("xxx");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
addComponentsToPane(frame.getContentPane());
frame.setSize(800,800);
frame.setVisible(true);
}
public void addComponentsToPane(Container pane) {
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton button = new JButton("xxx");
button.setActionCommand("add_text");
button.addActionListener(this);
c.gridx = 0;
c.gridy = 0;
pane.add(button, c);
jp = new JPanel();
c.gridx = 0;
c.gridy = 1;
c.ipadx = 600;
c.ipady = 450;
pane.add(jp, c);
sp = new ScrollingPane();
js = new JScrollPane(sp);
js.getViewport().setPreferredSize(new Dimension(300,100));
c.gridx = 0;
c.gridy = 2;
c.ipady = 50;
pane.add(js, c);
}
public void actionPerformed(ActionEvent e) {
if ("add_text".equals(e.getActionCommand())) {
LineNum++;
sp.SetText("LineNum = " + LineNum);
sp.revalidate();
sp.repaint();
}
}
}
}
You're short circuiting the ability of your ScrollingPane JPanel from correctly sizing itself with this line:
setPreferredSize(new Dimension(300, 100));
This will fix the size of the ScrollingPane JPanel. I see that you have several possible solutions:
Difficult: override getPreferredSize() for the ScrollingPane JPanel, and calculate the appropriate preferred size based on the size of the text it holds and draws using FontMetrics.
Easier: Don't add text as you're doing, but rather have ScrollingPane use a GridLayout(0, 1) (one column, variable number of rows), and add JLabels to the ScrollingPane when new text is needed to be added. Then call revalidate() and repaint() on it.
Easier still: Don't use a ScrollingPane JPanel but rather a JTextArea, that looks like a JPanel and that can't be edited. Add that to the JScrollPane, and again, do not restrict its size
Easiest: Just use a JList as that's the functionality you are using here.
For instance, either of these would work and would look similar:
private static class ScrollingPane2 extends JPanel {
private static final long serialVersionUID = 1L;
private JTextArea textArea = new JTextArea(6, 20);
public ScrollingPane2() {
setLayout(new BorderLayout());
add(textArea, BorderLayout.CENTER);
textArea.setEditable(false);
textArea.setFocusable(false);
textArea.setBackground(null);
}
public void SetText(String text_x) {
textArea.append(text_x + "\n");
}
}
private static class ScrollingPane3 extends JPanel {
private static final long serialVersionUID = 1L;
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> jList = new JList<>(listModel);
public ScrollingPane3() {
setLayout(new BorderLayout());
add(jList, BorderLayout.CENTER);
jList.setBackground(null);
}
public void SetText(String text_x) {
listModel.addElement(text_x);
}
}
Related
Here, My Jscrollpane isn't working. I have added the scrollpane and list both on main Jpanel. The Jlist is working, But it seems, JScrollpane Not working in this code.
my main class extends Jframe, so from main class i just pass the main panel, and working on the same panel.
and here i'm using absolute layout.
public class ExternalsLinks extends JPanel {
///Links List
private JList mainList;
///List Custom Border
Border lineBorder = BorderFactory.createLineBorder(Color.BLACK, 5);
///Main Jpanel
private JPanel mainPanel;
///Scroll Pane for Lists
private JScrollPane mainListScrollPane;
/// Lebels
private JLabel lblExternalLinks;
///Buttons
private Button btnBackCredits = new Button("Back");
///Button Properties
private final int BUTTON_X = 220; private final int BUTTON_Y = 450;
private final int BUTTON_X_LENGTH = 350; private final int BUTTON_Y_LENGTH = 50;
///List Options
private String [] listMenu = {"Searching", "Sorting"};
///Constructors
public ExternalsLinks(JPanel mainPanel) {
this.mainPanel = mainPanel;
this.mainPanel.setLayout(null);
mainPanel.setBackground(Color.WHITE);
initialize_All();
}
/// Initialize method to initialize all components to JFrame
private void initialize_All() {
///Main Panel Components
lblExternalLinks = new JLabel("\"External Links\"");
lblExternalLinks.setBounds(290, 0, 400, 100);
lblExternalLinks.setFont(new Font("courier", Font.BOLD, 30));
mainPanel.add(lblExternalLinks);
mainList = new JList(listMenu);
mainList.setBackground(Color.CYAN);
mainList.setForeground(Color.BLACK);
mainList.setFont(new Font("Calibiri", Font.BOLD, 25));
mainList.setVisibleRowCount(2);
mainList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mainList.setBorder(lineBorder);
mainList.setBounds(50, 80, 300, 350);
mainListScrollPane = new JScrollPane();
mainPanel.add(mainListScrollPane);
mainPanel.add(mainList);
///Buttons
btnBackCredits.setBounds(BUTTON_X, BUTTON_Y, BUTTON_X_LENGTH, BUTTON_Y_LENGTH);
mainPanel.add(btnBackCredits);
btnBackCredits.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
mainList.setVisible(false);
mainPanel.removeAll();
new MainMenu(mainPanel);
}
});
}
}
I have added the scrollpane and list both on main Jpanel.
That's a problem -- don't do that. You can only add a component to one container, and the JScrollPane counts as a container. So add your JList to the JScrollPane's viewport, and then only add the JScrollPane to the GUI.
i'm using absolute layout
And that's another serious problem. Don't use null layouts and setBounds(...). For instance calling setBounds on your list will prevent the scrollpane from being able to work since the list can't expand inside the JScrollPane as it should. Learn and use the layout managers.
Also, you never add the JList to the JScrollPane's viewport!
You need JScrollPane scrollPane = new JScrollPane(myList); or something similar, with whatever your variable names are. Please go through the tutorials as this is all fully explained there.
JScrollPane tutorial link
Swing Info link to tutorials and Swing resources
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import javax.swing.*;
#SuppressWarnings("serial")
public class ExternalsLinks2 extends JPanel {
// constants
private static final String[] LIST_DATA = {"Searching", "Sorting"};
private static final Font LIST_FONT = new Font("Calibiri", Font.BOLD, 25);
private static final Font LABEL_FONT = new Font("courier", Font.BOLD, 30);
private static final int LIST_VISIBLE_ROW_COUNT = 10;
private static final String TITLE_TEXT = "External Links";
private static final Color LIST_BG = Color.CYAN;
// JList field created with constant array data
private JList<String> jList = new JList<>(LIST_DATA);
public ExternalsLinks2() {
jList.setFont(LIST_FONT);
jList.setPrototypeCellValue("ABCDEFGHIJKLMNOP ABCDE");
jList.setVisibleRowCount(LIST_VISIBLE_ROW_COUNT);
jList.setBackground(LIST_BG);
JLabel titleLabel = new JLabel(TITLE_TEXT, SwingConstants.CENTER);
titleLabel.setFont(LABEL_FONT);
JButton backButton = new JButton("Back");
// add ActionListener or AbstractAction here
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
setLayout(new BorderLayout(5, 5)); // use BorderLayout
add(titleLabel, BorderLayout.PAGE_START); // JLabel at top
add(new JScrollPane(jList), BorderLayout.CENTER); // JList inside of JScrollPane in center
add(backButton, BorderLayout.PAGE_END); // JButton at bottom
}
private static void createAndShowGui() {
JFrame frame = new JFrame("External Links");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ExternalsLinks2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Which displays as:
On a project, I have a JFrame A starting a JDialog B, which itself starts a JDialog C (all using buttons). However, when following one of these procedures :
I click on A to start B ; or
I click on A to start B, then on B to start C, then click on C's cancel button,
what is displayed in B is not the same (the second procedure gives a weird ugly thing).
I don't get why that could be, since in both ways my updateAll method is called.
I tried to recreate this with a little program so that it's easier to see what's going on. Solving this may or may not solve it on my actual project, but it will surely help.
Since I don't know where it could come from, here's the full code of my (test) program. Brace yourselves.
The 'A' Frame
public class MyFrame extends JFrame {
private static final long serialVersionUID = 7073064926636937881L;
public MyFrame() {
this.setSize(200, 300);
JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new MyDialog1().setVisible(true);
}
});
this.getContentPane().add(button);
}
public static void main(String args[]) {
new MyFrame().setVisible(true);
}
}
The 'B' Dialog
public class MyDialog1 extends JDialog {
private static final long serialVersionUID = 9181006217120036637L;
private JScrollPane scrollPane;
public String text = "aaaaaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaa\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\naaaaaaaa";
public MyDialog1() {
this.setVisible(false);
this.setSize(800, 600);
this.initComponent();
this.updateAll();
}
private void initComponent() {
this.getContentPane().setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
this.scrollPane = new JScrollPane();
c.gridx = 0;
c.gridy = 0;
this.getContentPane().add(this.scrollPane, c);
c.gridx = 0;
c.gridy = 1;
JButton b = new JButton("Supposedly edit stuff");
final MyDialog1 caller = this;
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new MyDialog2(caller).setVisible(true);
}
});
this.getContentPane().add(b, c);
c.gridx = 1;
c.gridy = 1;
b = new JButton("Leave");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
}
});
this.getContentPane().add(b, c);
}
public void updateAll() {
JPanel mainPanel = new JPanel();
for (int i = 0 ; i < 5 ; i++) {
JPanel subPanel = new JPanel();
JTextArea t = new JTextArea(this.text);
t.setSize(60, 30);
t.setVisible(true);// Useful ? What about setSize ?
subPanel.add(t);
mainPanel.add(subPanel);
}
this.scrollPane.setSize(150, 150); // FIXME When in initComponent, doesn't do anything, and when in updateAll, behavior is inconsistent
this.scrollPane.setViewportView(mainPanel); // Replacing previous JPanel
}
}
The 'C' Dialog
public class MyDialog2 extends JDialog {
private static final long serialVersionUID = 5676648412234106581L;
private MyDialog1 caller;
public MyDialog2(MyDialog1 c) {
this.setSize(100, 150);
this.caller = c;
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
caller.text += "\nbbbbbbbbbbbbb\nbbbbbbbbbbbbbbbbbb\nbbbbbbbbbbb\nbbbbbbbbbbbbbb\ncccccccccccccccccccccccccccccccccccccccccccc\ncccccccccc";
caller.updateAll();
}
});
this.getContentPane().add(cancelButton);
}
}
Thanks for your help.
I suggest to use
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH; // make the component fill its display area entirely
c.ipady = 150; //height
c.anchor = GridBagConstraints.FIRST_LINE_START; // component start from the left top corner
this.getContentPane().add(this.scrollPane, c);
for definition of JScrollPane constraints.
In additition, add validate() and repaint() after modifying elements
this.scrollPane.setViewportView(mainPanel); // Replacing previous JPanel
this.validate();
this.repaint();
Easy answer which ignores good practice:
Replace this:
this.scrollPane.setSize(150, 150);
with this:
this.scrollPane.setMinimumSize(new Dimension(150, 150));
Calling setSize on a component which is in a Container with a layout usually does nothing; at best, it will set the size until the next time the container is validated, since the size will be overwritten by the layout manager. However, setting a minimumSize (or preferredSize or maximumSize) sets a persistent property that is respected by (most) layout managers.
Why does setMinimumSize make a difference? Because you have not set any weightx or weighty properties on any of your GridBagConstraints, so there is not enough room for the GridBagLayout to display your JScrollPane at its preferred size. When GridBagLayout determines there isn't enough room to display everything at the size it requires, the layout "punts" and forces everything to revert to its minimum size.
The first time you display the B dialog, you are seeing the JScrollPane at its minimum size (that is, just large enough to display the scrollbars and viewport border). After canceling the C dialog, the setSize(150, 150) takes effect, but it's likely that any subsequent changes to any descendant of the GridBagLayout would cause that (150, 150) to be overwritten with the JScrollPane's minimum size again.
More complex answer which is good practice:
Remove all calls to setSize in every class. Usually you should not set explicit sizes at all, but if you must do so, use setPreferredSize.
Components which are made to grow and shrink, like JScrollPanes, should be assigned positive weightx and weighty values in their corresponding GridBagConstraints, along with fill being set to GridBagConstraints.BOTH as pcej suggested.
I have a class called 'Panel' that extends JPanel and it is inside another class called 'Main'. The constructor instantiates JFrame, and all the GUI components, and sets it all up, such as size.
The class 'Panel' which extends JPanel has a method public void paintComponent(Graphics g){} and inside it I added a few JButtons and used g.drawString's.
Then in the 'Main' class, I added the 'Panel' to the JFrame.
My question is, I am trying to implement an actionListener to a button added inside the 'Panel' class. The actionListener function would add more buttons and use g.drawString's as well. Now where would I place the ActionListener in order to do so? How can I use g.drawString for a particular panel and the g.drawString line is inside another class, which is the ActionListener class? I would need to use Graphics g of paintComponent inside the actionPerformed.
Thank you!
EDIT -
CODE EXAMPLE:
public class Main{
private JFrame jf;
private JTextField jtf1;
private JTextField jtf2;
private Panel p;
private JComboBox jcb1;
private JComboBox jcb2;
private JButton button;
private Object options[];
//ActionListener Variables
private int string1 = 150;
private int string2 = 150;
private int yJtf1 = 150;
private int yJtf2 = 160;
private int cb1 = 140;
private int cb2 = 165;
private int count = 0;
public Main(){
jf= new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(700, 700);
p = new Panel();
jtf1 = new JTextField("", 20);
jtf2= new JTextField("", 20);
Object options[] = {""};
jcb1 = new JComboBox(tools);
jcb2 = new JComboBox(tools);
button = new JButton("+");
jf.add(p);
jf.setVisible(true);`
}
public class Panel extends JPanel{
public Panel(){
this.setLayout(null);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
/*button.addActionListener(new ActionListener(){ //Would this work or should the ActionListener be a class as shown below?
public void actionPerformed(ActionEvent e){
if(count < 3){ //Won't be allowed to add anymore after 3 times
string1 += 50;
string2 += 50;
jtf1 += 50;
jtf2 += 50;
cb1 += 50;
cb2 += 45;
//Would like to add the following components to the 'Panel' (which is a JPanel) whenever the JButton 'button' already added to 'Panel' is clicked.
p.add(jtf1); //Would doing p.add really add to the panel when the ActionListener is called?
jtf1.setBounds(60, yJtf1, 50, 40);
p.add(jtf2);
jtf2.setBounds(60, yJtf2, 50, 40);
add(jcb1);
jcb1.setBounds(250, cb1, 50, 40);
add(left2);
jcb2.setBounds(250, cb2, 50, 40);
Font font = new Font("TimesRoman", Font.BOLD, 18);
g.setFont(font); //Getting error on 'g' regardless
g.drawString("Hi", 15, string1); //This is the main problem, how would I be able to add this strings to the 'Panel' (which is a JPanel)
g.drawString("There", 330, string1);
}
count++;
}
});*/
add(jtf1);
jtf1.setBounds(100, 30, 120, 30);
add(jtf2);
ljtf2.setBounds(100, 60, 120, 30);
add(button);
plusButton.setBounds(200,150, 50, 50);
//button.addActionListener(new ButtonClicked()); if doing ActionListener via class like below
add(jcb1);
jcb1.setBounds(300, 350, 100, 50);
add(ljcb2);
jcb2.setBounds(300, 350, 100, 25);
Font font = new Font("Arial", Font.BOLD, 12);
g.setFont(font);
g.drawString("Item:", 40, 45);
g.drawString("Cost:", 40, 75);
}
}
public static void main(String [] args){
new Main();
}
class ButtonClicked implements ActionListener{ //Action Listener: The follow is what I am trying to implement
public void actionPerformed(ActionEvent ae){
if(count < 3){ //Won't be allowed to add anymore after 3 times
string1 += 50;
string2 += 50;
jtf1 += 50;
jtf2 += 50;
cb1 += 50;
cb2 += 45;
//Would like to add the following components to the 'Panel' (which is a JPanel) whenever the JButton 'button' already added to 'Panel' is clicked.
p.add(jtf1); //Would doing p.add really add to the panel when the ActionListener is called?
jtf1.setBounds(60, yJtf1, 50, 40);
p.add(jtf2);
jtf2.setBounds(60, yJtf2, 50, 40);
mp.add(jcb1);
jcb1.setBounds(250, cb1, 50, 40);
mp.add(left2);
jcb2.setBounds(250, cb2, 50, 40);
Font font = new Font("TimesRoman", Font.BOLD, 18);
g.setFont(font);
g.drawString("Hi", 15, string1); //This is the main problem, how would I be able to add this strings to the 'Panel' (which is a JPanel)
g.drawString("There", 330, string1);
}
count++;
}
}
}
I would need to use Graphics g of paintComponent inside the actionPerformed."`
No, you wouldn't since Swing graphics is passive not active. The Graphics object would reside in the JPanel's paintComponent method and would be passed to it from the JVM when it and only it calls the paintComponent method. The actionPerformed method would then change the value of a String variable or perhaps an ArrayList<String>, call repaint(), and then the JPanel's paintComponent method would use the changed String to draw the appropriate text.
If you need more specific help, consider telling us a few more details and posting a minimal example program.
Edit
On review of your code I have several suggestions:
Please try to fix your code a bit so that it is either compilable or as close to compilable as possible.
Never add components or do anything but painting inside of your paintComopnent(...) method. You don't have full control over when or even if that method will be called, and placing something in inside of this method will result in unwanted side effects, like combo boxes that simply won't work.
If you ever wanted text shown in your program, you can always add a JLabel.
Your program uses null layout and setBounds(...) something that results in a rigid GUI that may look good on one system but will usually look poor on any other system or screen resolution. Also programs created this way are very hard to debug, maintain and upgrade. Instead use the layout managers as this is what they excel at: at creating complex flexible GUI's that can be enhanced and changed easily.
Edit
I'm guessing what you really want to do, and that possibly is to add more components to allow the user to enter more data into the GUI. You also want to add text perhaps to guide the user in what the purpose of the components may be. If this is so, then the best solution is not to add Strings to the GUI in paintComponent but rather to add Strings/Components in an organized fashion with the Strings being displayed in JLabels, and the components and the labels all held in a JPanel or JPanels, and added to the GUI using layout managers.
For example, if you want the user to add data in two JTextfields and have two JComboBoxes, and then allow the user to add 3 more of these guys if need be, a GUI could look something like this:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Main2 extends JPanel {
private List<DataPanel> dataPanelList = new ArrayList<>();
private JPanel dataPanelHolder = new JPanel();
public Main2() {
DataPanel dataPanel = new DataPanel();
dataPanelList.add(dataPanel);
setLayout(new BorderLayout());
dataPanelHolder.setLayout(new BoxLayout(dataPanelHolder, BoxLayout.PAGE_AXIS));
dataPanelHolder.add(dataPanel);
JPanel innerBorderLayoutPanel = new JPanel(new BorderLayout());
innerBorderLayoutPanel.add(dataPanelHolder, BorderLayout.PAGE_START);
JScrollPane scrollPane = new JScrollPane(innerBorderLayoutPanel);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
int w = dataPanel.getPreferredSize().width;
int h = dataPanel.getPreferredSize().height * 4;
Dimension viewPortSize = new Dimension(w, h);
scrollPane.getViewport().setPreferredSize(viewPortSize);
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
buttonPanel.add(new JButton(new AddDatatAction("Add")));
buttonPanel.add(new JButton(new ExitAction("Exit", KeyEvent.VK_X)));
add(scrollPane, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
}
private class AddDatatAction extends AbstractAction {
private int maxCount = 4;
public AddDatatAction(String name) {
super(name);
int mnemonic = (int)name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (dataPanelList.size() < maxCount) {
DataPanel dataPanel = new DataPanel();
dataPanelList.add(dataPanel);
dataPanelHolder.add(dataPanel);
dataPanelHolder.revalidate();
dataPanelHolder.repaint();
}
}
}
private class ExitAction extends AbstractAction {
public ExitAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Window win = SwingUtilities.getWindowAncestor(Main2.this);
win.dispose();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Main2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class DataPanel extends JPanel {
private static final String[] TOOLS = {"Tool 1", "Tool 2", "Tool 3", "Tool 4"};
private static final String[] FIELD_LABELS = {"Item", "Cost"};
private static final String[] COMBO_LABELS = {"Foo", "Bar"};
private JTextField[] fields = new JTextField[FIELD_LABELS.length];
private List<JComboBox<String>> comboList = new ArrayList<>();
public DataPanel() {
setBorder(BorderFactory.createTitledBorder("Data"));
setLayout(new GridBagLayout());
for (int i = 0; i < FIELD_LABELS.length; i++) {
add(new JLabel(FIELD_LABELS[i]), createGbc(0, i));
fields[i] = new JTextField(10);
add(fields[i], createGbc(1, i));
JComboBox<String> combo = new JComboBox<>(TOOLS);
comboList.add(combo);
add(combo, createGbc(2, i));
add(new JLabel(COMBO_LABELS[i]), createGbc(3, i));
}
}
public static GridBagConstraints createGbc(int x, int y) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
int ins = 4;
gbc.insets = new Insets(ins, ins, ins, ins);
return gbc;
}
}
And could look like:
one datapanel added
four added:
The SSCCE below is a class that extends JPanel. The JPanel is the basic outline of a calendar (I've stripped it way down for simplicity's sake), and it consists of JButton components, a JLabel, and a JTable. When I add this frame to a window (i.e. JDialog), it appears as normal. However, when I add another component with it, it disappears. Why is this happening, and how can I make this not happen?
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
public class CalendarPanel extends JPanel {
private static JDialog dialog = new JDialog();
public static void main(String[] args) {
setDialogProperties();
addComponentsToDialog();
dialog.setVisible(true);
}
private static void setDialogProperties() {
dialog.setModal(true);
dialog.setResizable(false);
dialog.setSize(new Dimension(330, 400));
dialog.setDefaultCloseOperation(2);
dialog.setLocationRelativeTo(null);
}
private static void addComponentsToDialog() {
CalendarPanel calendar = new CalendarPanel();
JPanel panel = new JPanel();
panel.add(calendar);
dialog.add(panel);
//dialog.add(new JLabel("Hello"));
}
private static final long serialVersionUID = 1L;
private JLabel lblMonth;
private JButton btnPrev, btnNext;
private JTable tblCalendar;
private DefaultTableModel mtblCalendar;
private JScrollPane stblCalendar;
private static GridBagLayout gridBag = new GridBagLayout();
private GridBagConstraints constraints = new GridBagConstraints();
public CalendarPanel() {
super(gridBag);
createControls();
addControlsToPanel();
addHeaders();
setTableProperties();
}
private void createControls() {
lblMonth = new JLabel("January");
btnPrev = new JButton("<");
btnNext = new JButton(">");
mtblCalendar = new DefaultTableModel() {
public boolean isCellEditable(int rowIndex, int mColIndex) {
return false;
}
};
tblCalendar = new JTable(mtblCalendar);
stblCalendar = new JScrollPane(tblCalendar);
stblCalendar.setPreferredSize(new Dimension(300, 247));
}
private void addControlsToPanel() {
GridBagLayout topGridBag = new GridBagLayout();
JPanel topPanel = new JPanel(topGridBag);
JPanel labelPanel = new JPanel();
labelPanel.add(lblMonth);
labelPanel.setPreferredSize(new Dimension(180, 20));
constraints.gridx = 1;
topGridBag.setConstraints(labelPanel, constraints);
constraints.gridx = 2;
topGridBag.setConstraints(btnNext, constraints);
topPanel.add(btnPrev);
topPanel.add(labelPanel);
topPanel.add(btnNext);
gridBag.setConstraints(topPanel, constraints);
constraints.gridy = 1;
gridBag.setConstraints(stblCalendar, constraints);
this.add(topPanel);
this.add(stblCalendar);
}
private void addHeaders() {
String[] headers = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
for (int i = 0; i < 7; i++) {
mtblCalendar.addColumn(headers[i]);
}
}
private void setTableProperties() {
tblCalendar.getTableHeader().setReorderingAllowed(false);
tblCalendar.setRowHeight(38);
mtblCalendar.setColumnCount(7);
mtblCalendar.setRowCount(6);
}
}
JDialogs and all top-level windows use BorderLayout by default. When you add a component to it (actually its contentPane) without specifying the position, you add it to the BorderLayout.CENTER position by default. You are covering up the previously added component whenever you add a new one. You will want to learn about the layouts available for your use and then use them to their best advantage.
e.g.,
dialog.add(panel);
dialog.add(new JLabel("Hello", SwingConstants.CENTER), BorderLayout.SOUTH);
}
Next you'll want to avoid setting the sizes of anything and to be sure to pack() your top level windows that allow this.
I'm trying to put multiple JLists in a JPanel and the JPanel in a JScrollPane. The problem is that when I add the JScrollPane to the equation there is a space between the JList and outer border, how do I get rid of this border so it's fills horizontally?
Here is a small sample code that demonstrates this problem with a JLabel instead of JList.
/edit: It's seems to be windows thing, it runs fine on Mac OS.
import java.awt.*;
import javax.swing.*;
public class Test extends JFrame
{
public static void main(String[] args)
{
Test test = new Test();
}
public Test()
{
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setContentPane(contentPane);
this.setMinimumSize(new Dimension(400, 300));
JScrollPane sp = new JScrollPane(createLeftPane());
sp.setBorder(BorderFactory.createLineBorder(Color.yellow));
this.add(sp, BorderLayout.WEST);
LookAndFeel.installBorder(sp, "BorderFactory.createEmptyBorder()");
StackTraceElement[] st = Thread.currentThread().getStackTrace();
for(int i = 0; i < st.length; i++) {
System.out.println(st[i]);
}
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private JPanel createLeftPane()
{
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
JLabel label = new JLabel("TEST LABEL");
label.setBorder(BorderFactory.createLineBorder(Color.blue));
panel.add(label, c);
panel.setBorder(BorderFactory.createLineBorder(Color.red));
return panel;
}
}
Here's a screenshot (first one is without the scrollpane)
Stack Trace:
java.lang.Thread.getStackTrace(Thread.java:1479)
test.Test.<init>(Test.java:27)
test.Test.main(Test.java:10)
/edit: After some trial and error I found out that when I add a c.weightx = 0.5 to the contrains it does fill Horizontally, but when the scrollPane becomes larger than it's content it makes itself wider, which is weird. See the screenshots below:
The problem isn't because of the scrollpane. Its because you add the label to a JPanel. By default a panel uses a FlowLayot which has has a vertical/horizontal gap of 5 pixels around each component.
Change the FlowLayout to use a 0 gap, or use a different layout. Maybe a BoxLayout.
component.setBorder(null) removes any border which has been set on the component
The problem doesn't occur when I run it on my Mac, so apparently it's a Windows thing.
In addition to the expected UI defaults for background, foreground, font and opaque, BasicPanelUI installs a border, while com.apple.laf.AquaPanelUI (apparently) does not.
LookAndFeel.installBorder(p,"Panel.border");
You might try setting yours to null.
are you meaning this way ???
import java.awt.*;
import java.io.File;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
public class FilesInTheJList {
private static final int COLUMNS = 5;
private Dimension size;
public FilesInTheJList() {
final JList list = new JList(new File("C:\\").listFiles()) {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredScrollableViewportSize() {
if (size != null) {
return new Dimension(size);
}
return super.getPreferredScrollableViewportSize();
}
};
list.setFixedCellHeight(50);
list.setFixedCellWidth(150);
size = list.getPreferredScrollableViewportSize();
size.width *= COLUMNS;
list.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
list.setCellRenderer(new MyCellRenderer());
list.setVisibleRowCount(0);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
JFrame f = new JFrame("Files In the JList");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(list));
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
FilesInTheJList fITJL = new FilesInTheJList();
}
});
}
private static class MyCellRenderer extends JLabel implements ListCellRenderer {
private static final long serialVersionUID = 1L;
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof File) {
File file = (File) value;
setText(file.getName());
setIcon(FileSystemView.getFileSystemView().getSystemIcon(file));
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setPreferredSize(new Dimension(250, 25));
setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
}
return this;
}
}
}
.
.
similair but better way by #trashgod here
The solution of trashgod worked for me with
LookAndFeel.installBorder(scrollpane,BorderFactory.createEmptyBorder().toString());
You can also remove the JScrollPane's border using
JScrollPane yourScrollPane = new JScrollPane();
yourScrollPane.setBorder(BorderFactory.createEmptyBorder());