JButton not showing up in scroll pane - java

I'm trying to setup a basic GUI Library that will import a list of books and display each book as a JButton within a scroll pane. But, before getting there I'm just trying to orient the panels first and adding a test button to make sure the basics are working before moving on to the details.
I've tried moving code around to add panels in different orders to see if that was an issue but keep getting the same result. I'm completely new to this, so my understanding of it is very limited.
public class LibraryPanel extends JPanel{
private Library library;
private JPanel bookButtons, importBooks;
JScrollPane bookList;
JTextField importField;
JButton load;
public LibraryPanel() {
setPreferredSize(new Dimension(200,500));
Library library = new Library();
setLayout(new BorderLayout());
this.setBorder(BorderFactory.createTitledBorder("Library"));
// Import Books Panel
importBooks = new JPanel();
importBooks.setLayout(new BoxLayout(importBooks,BoxLayout.X_AXIS));
importBooks.setBorder(BorderFactory.createTitledBorder("Import Books"));
importField = new JTextField(15);
importBooks.add(importField);
load = new JButton("Load");
importBooks.add(load);
this.add(importBooks,BorderLayout.SOUTH);
load.addActionListener(new loadButtonListener());
// Book List buttons
JPanel bookButtons = new JPanel();
bookButtons.setLayout(new BoxLayout(bookButtons,BoxLayout.Y_AXIS));
JButton testButton = new JButton("TEST Button");
bookButtons.add(testButton);
//for(int i = 0; i<library.getBooks().size(); i++) {
// BookButton button = new BookButton(library.getBook(i));
//button.addActionListener(new BookButtonListener());
// bookButtons.add(button);
//}
// Scroll Pane
bookList = new JScrollPane();
bookList.setBorder(BorderFactory.createTitledBorder("Book List"));
bookList.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
bookList.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(bookList,BorderLayout.CENTER);
bookList.add(bookButtons);
}
private class loadButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String filename = new String(importField.getText());
library.loadLibraryFromCSV(filename);
}
}
}
However, I'm having an issue with the test button not showing up at all within the scroll pane. The panels are there but not the test button.

You can't "add" components to a JScrollPane, this isn't how they work. A JScrollPane uses a JViewport as it's primary component, which is then used to determine when the scrollbars should be used.
See How to Use Scroll Panes for more details
Instead of...
bookList = new JScrollPane();
//...
bookList.add(bookButtons);
simply do...
bookList = new JScrollPane(bookButtons);
//...
//bookList.add(bookButtons);

Related

Add new fields with press of a button

I´m creating a program in NetBeans where the user can create their own CSV-file. I've made a GUI. When I press the button, I want a new JLabel and a new JTextField to appear underneath the existing ones, for as many times as the button is pressed. How do I do that?
I've done something similiar and I'm using a JPanel with GroupLayout to solve this problem and that's my code:
EDIT - Your question aroused my interest and I've changed my code to your needs (of course only the basic, you will have to improve it)
Global variables
private GroupLayout m_layout;
private SequentialGroup m_verticalSg;
private ArrayList<Component> m_labelList;
private ArrayList<Component> m_textFieldList;
private ParallelGroup m_horizontalPgLabels;
private ParallelGroup m_horizontalPgTextfields;
Method createLayout()
Creates the layout for your panel which should contain the label & textfield components
private void createLayout()
{
m_layout = new GroupLayout(YOUR_PANEL);
YOUR_PANEL.setLayout(m_layout);
//This SequentialGroup is used for the VerticalGroup
m_verticalSg = m_layout.createSequentialGroup();
m_verticalSg.addContainerGap();
//Two ParallelGroups are used. One for all labels and the other one for all textfields
m_horizontalPgLabels = m_layout.createParallelGroup(GroupLayout.Alignment.LEADING);
m_horizontalPgTextfields = m_layout.createParallelGroup(GroupLayout.Alignment.LEADING);
//These component lists are used for linkSize() -> Equalize components width
m_labelList = new ArrayList<>();
m_textFieldList = new ArrayList<>();
m_layout.setHorizontalGroup(m_layout.createParallelGroup()
.addGroup(m_layout.createSequentialGroup()
.addContainerGap()
.addGroup(m_horizontalPgLabels)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) //Create gap between horizontal groups
.addGroup(m_horizontalPgTextfields)
.addContainerGap()));
m_layout.setVerticalGroup(m_layout.createParallelGroup().addGroup(m_verticalSg.addContainerGap()));
}
Method addNewRow()
Call this method from your button click event
private void addNewRow()
{
if(m_layout == null)
createLayout();
Dimension dimLabel = new Dimension(100, 15);
Dimension dimTextfield = new Dimension(200, 20);
//Create a new label
JLabel lbl = new JLabel();
lbl.setText("Your text");
lbl.setIcon(null/*Your icon*/);
lbl.setSize(dimLabel);
lbl.setPreferredSize(dimLabel);
//Create a new textfield
JTextField txtField = new JTextField();
txtField.setSize(dimTextfield);
txtField.setPreferredSize(dimTextfield);
//Add components to arrays and increase index
m_labelList.add(lbl);
m_textFieldList.add(txtField);
//Create new ParallelGroup for the vertical SequentialGroup
ParallelGroup newVerticalParallelGroup = m_layout.createParallelGroup(GroupLayout.Alignment.LEADING);
newVerticalParallelGroup.addComponent(lbl);
newVerticalParallelGroup.addComponent(txtField);
m_verticalSg.addGroup(newVerticalParallelGroup);
m_verticalSg.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
//Add the new label to the horizontal label group
m_horizontalPgLabels.addComponent(lbl, GroupLayout.Alignment.CENTER);
//Add the new textfield to the horizontal textfield group
m_horizontalPgTextfields.addComponent(txtField);
m_layout.linkSize(SwingConstants.HORIZONTAL, m_labelList.toArray(new Component[m_labelList.size()]));
m_layout.linkSize(SwingConstants.HORIZONTAL, m_textFieldList.toArray(new Component[m_textFieldList.size()]));
}
The last step is to add an ActionListener to your button to call the method addNewRow().
jButton1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
addNewRow();
}
});
Feel free to ask me if something is unclear.

Can other methods make use of JScrollPane's viewport?

It seems like the only way to display text with multiple styles in a text area is to use a JEditorPane. To explore this, I wrote a small app to list all the fonts in their font.
Everything works, and the list is displayed in a JEditorPane, which is inside a JScrollPane.
However, it takes a few seconds to launch and to repopulate, because it generates the entire new list before displaying it. The code is below, following MCV. Minimalizing it to just the message and text made less work so the lag is no longer nearly as noticeable, but my question (below) was more about rendering and keeping track of JScrollPane's position anyway than about this app.
FontFrame.java
public class FontFrame extends JFrame {
private FontFrame() {
JFrame frame = new JFrame("Fonts Displayer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FontPanel fontPanel = new FontPanel();
frame.getContentPane().add(fontPanel);
frame.getRootPane().setDefaultButton(fontPanel.getDefaultButton());
frame.setMinimumSize(new Dimension(600, 600));
frame.setPreferredSize(new Dimension(1000, 700));
frame.pack();
frame.setVisible(true);
}
public static FontFrame launchFontFrame() {
return new FontFrame();
}
public static void main(String[] args) {
FontFrame.launchFontFrame();
}
}
FontPanel.java
class FontPanel extends JPanel {
private String message = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz";
private JEditorPane fontPane;
private JTextField messageInput;
private JButton changeButton;
protected FontPanel() {
buildPanel();
}
private void buildPanel() {
setLayout(new BorderLayout());
// Build message input panel
JPanel inputPanel = new JPanel();
messageInput = new JTextField(message);
messageInput.setFont(new Font("SansSerif", Font.PLAIN, 14));
messageInput.setColumns(40);
JLabel messageLabel = new JLabel("Message:");
changeButton = new JButton("Change");
changeButton.addActionListener((ActionEvent e) -> {
String text = messageInput.getText();
if (!text.isEmpty() && !text.equals(message)) {
message = text;
refreshFontList();
}
});
inputPanel.add(messageLabel);
inputPanel.add(messageInput);
inputPanel.add(changeButton);
// Build scrolling text pane for fonts display
fontPane = new JEditorPane();
fontPane.setContentType("text/html");
fontPane.setEditable(false);
fontPane.setVisible(true);
JScrollPane scrollPane = new JScrollPane(fontPane);
refreshFontList();
// Add components to main panel
add(inputPanel, BorderLayout.NORTH);
add(scrollPane, BorderLayout.CENTER);
}
private void refreshFontList() {
String[] list = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
StringBuilder messages = new StringBuilder();
// Set global table style settings
messages.append("<table>");
// Make a row for each font
for (String font : list) {
messages.append("<tr>");
//Append data cells
messages.append("<td>").append(font).append("</td>")
.append("<td style=\"font-family:").append(font).append("\">")
.append(message)
.append("</td>");
messages.append("</tr>");
}
messages.append("</table>");
fontPane.setText(messages.toString());
}
JButton getDefaultButton() {
return changeButton;
}
}
Is there any way of redoing refreshFontList so it can generate only what fontPane would show in its viewport first, possibly by using JScrollBar, and then generate the rest of the list right after so the computation time doesn't cause lag in display? Is there a different component that would work better?

Clear a JPanel from a JFrame

I'm working on an assignment for class where we need to create a JComboBox and each option opens a new window where you can do whatever you want on those new windows. Please keep in mind I'm very new to GUI and new to Java in general, in case my questions are dumb.
I have a question and an issue...
My question:
When the user selects "The Matrix" option a new window pops up with a quote and two buttons. Right now I have two JPanels (panel and panel2) panel adds the quote to the NORTH position and then panel2 adds the two buttons to the CENTER position both using BorderLayout. My question is am I doing this correctly...Could I use just panel to add the quote and the buttons or is it necessary to create separate panels for separate items being added to the JFrame? When I had them both added to the same panel the quote was not on the window when I ran the program.
panel.add(matrixQuote);
newFrame.add(panel, BorderLayout.NORTH);
That's how I had it when it wasn't showing up ^^^
I GOT THE ISSUE WITH CLEARING THE JFRAME FIXED
I am trying to add an ActionListener to the bluePill button and instead of opening another new window I thought I could clear everything from the existing window when the button is pressed and then display something new on said window. The only info I could find on this is how I have it in the actionPerformed method below. I'll post a snippet of what I'm talking about directly below and then all my code below that just in case.
All my code...
public class MultiForm extends JFrame{
private JComboBox menu;
private JButton bluePill;
private JButton redPill;
private JLabel matrixQuote;
private int matrixSelection;
private JFrame newFrame;
private JPanel panel;
private JPanel panel2;
private static String[] fileName = {"", "The Matrix", "Another Option"};
public MultiForm() {
super("Multi Form Program");
setLayout(new FlowLayout());
menu = new JComboBox(fileName);
add(menu);
TheHandler handler = new TheHandler();
menu.addItemListener(handler);
}
public void matrixPanel() {
TheHandler handler = new TheHandler();
//Create a new window when "The Matrix" is clicked in the JCB
newFrame = new JFrame();
panel = new JPanel();
panel2 = new JPanel();
newFrame.setLayout(new FlowLayout());
newFrame.setSize(500, 300);
newFrame.setDefaultCloseOperation(newFrame.EXIT_ON_CLOSE);
matrixQuote = new JLabel("<html>After this, there is no turning back. "
+ "<br>You take the blue pill—the story ends, you wake up "
+ "<br>in your bed and believe whatever you want to believe."
+ "<br>You take the red pill—you stay in Wonderland, and I show"
+ "<br>you how deep the rabbit hole goes. Remember: all I'm "
+ "<br>offering is the truth. Nothing more.</html>");
panel2.add(matrixQuote);
newFrame.add(panel2, BorderLayout.NORTH);
//Blue pill button and picture.
Icon bp = new ImageIcon(getClass().getResource("Blue Pill.png"));
bluePill = new JButton("Blue Pill", bp);
panel2.add(bluePill);
bluePill.addActionListener(handler);
//Red pill button and picture
Icon rp = new ImageIcon(getClass().getResource("Red Pill.png"));
redPill = new JButton("Red Pill", rp);
panel2.add(redPill);
newFrame.add(panel2, BorderLayout.CENTER);
newFrame.setVisible(true);
}
private class TheHandler implements ItemListener, ActionListener{
public void itemStateChanged(ItemEvent IE) {
//listen for an item to be selected.
if(IE.getStateChange() == ItemEvent.SELECTED) {
Object selection = menu.getSelectedItem();
if("The Matrix".equals(selection)) {
matrixPanel();
}
else if("Another Option".equals(selection)) {
}
}
}
public void actionPerformed(ActionEvent AE) {
if(AE.getSource() == bluePill) {
newFrame.remove(panel);
newFrame.remove(panel2);
newFrame.repaint();
}
}
}
//MAIN
public static void main(String[] args) {
MultiForm go = new MultiForm();
go.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
go.setSize(400, 200);
go.setVisible(true);
}
}
Use a Card Layout. You can swap panels as required.
The tutorial has a working example.
You can use:
jpanel.removeAll();
Either to delete a certain JComponent by using the JComponent itself like:
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.add(panel);
frame.remove(panel);
panel.getGraphics().clearRect(0, 0, panel.getWidth(), panel.getHeight());

Add Object to JPanel after button click

I have created a JScrollPane with a JPanel inside it and I want to add JPanel/JLabel/Other objects after pressing the button. For example after three button presses I want to get something like this:
I tried myJPane.add(testLabel) with testlabel.setBounds()but no result, I don't want to use GridLayout because of the unchangeable sizes. I would like it if the added objects had different sizes - adjusted to the text content.
What should I use for it and how?
Thanks in advance.
Best regards,
Tom.
Here is a JPanel inside a JScrollPane that adds JLabels to it when pressing the button:
public class Example extends JFrame {
public Example() {
JPanel boxPanel = new JPanel();
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.PAGE_AXIS));
JTextField textField = new JTextField(20);
JButton sendButton = new JButton("Send");
sendButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JLabel label = new JLabel(textField.getText());
label.setOpaque(true);
label.setBackground(Color.RED);
boxPanel.add(label);
boxPanel.add(Box.createRigidArea(new Dimension(0,5)));
textField.setText("");
boxPanel.revalidate();
// pack();
}
});
JPanel southPanel = new JPanel();
southPanel.add(textField);
southPanel.add(sendButton);
add(new JScrollPane(boxPanel));
add(southPanel, BorderLayout.PAGE_END);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new Example();
}
}
The BoxLayout will stack the labels on top of each other.
Notes:
setOpaque(true) must be called on label for it to honor the background color.
Box.createRigidArea is used for creating gaps. Use it as you wish.
The call to revalidate() is imperative in order to display the new components immediately.
Calling pack() (on the JFrame) will resize it each time to fit all the new components. I just put it there for demonstration since the initial frame size is too small to display the initial components added.
I will use a BoxLayout, creating a vertical box, and after each button action, it will add a new JPanel to this box.
Example:
public class YourChat extends JPanel{
private JScrollPane sc;
private Box bv;
public YourChat(){
bv = Box.createVerticalBox();
sc = new JScrollPane(bv);
//your functions (panel creation, addition of listeners, etc)
add(sc);
}
//panel customized to have red backgroud
private class MyPanel extends JPanel(){
private JLabel label=new JLabel();
public MyPanel(String text){
setBackgroundColor(Color.red);
add(label);
}
}
//inside the action listener
public void actionPerformed(ActionEvent e) {
sc.add(new MyPanel(textField.getText()));
textField.setText("");
}
}
For extra information check on:
[https://docs.oracle.com/javase/tutorial/uiswing/layout/box.html]
See also the example
[http://www.java2s.com/Code/Java/Swing-JFC/VerticalandhorizontalBoxLayouts.htm]
Use BoxLayout if you want only add vertically, otherwise you can use FlowLayout for both directions.

Switching Panels in Swing

I have a Swing application using Card Layout which basically changes the displayed panel depending on what the user selects from a drop-down menu.
One of my panels has a form. I would need for when the submit buton is pressed for all the inputs to be collected and the Panel to be switched to another one. (This second panel is defined in a separate class) I would also need for all the input to be somehow passed to a method in the new panel.
Any suggestions?
Dario
If you look at the <--s in the following code, each should solve each different question you have in your post. I figured you should know how to make a submit button, so I didn't include that. (Note: this is not running code, just suggestions);
public class MainPanel entends JPanel {
CardLayout layout = new CardLayout(); <-- card layout
JPanel panel = new JPanel(layout); <-- set layout to main panel
NewPanel newPanel = new NewPanel(); <-- you new panel
JPanel p1 = new JPanel(); <-- random panel
JTextField text = new JTextField() <-- text field in form
JButton button = new JButton();
JComboBox cbox = new JComboBox(new String[] {"newPanel", "p1"}); <-- hold panel names
public MainPanel(){
panel.add(newPanel, "newPanel"); <-- name associated with panel
panel.add(p1, "p1");
...
cbox.addAItemListener(new ItemListener(){
public void itemStateChnaged(ItemEvent e){
layout.show(panel, (string).getItem()); <-- show Panel from combobox
}
});
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
String txt = text.getText();
newPanel.printText(txt); <-- Using method from other class
}
});
}
}
public class NewPanel extends JPanel {
public void printText(String text){ <-- method from other class
System.out.println(text);
}
}

Categories

Resources