Search code examples
javaswinguser-interfacejscrollpane

How to position Java swing components when there is a JScrollPane


I am new to making GUIs and Java Swing and I'm trying to make a GUI which displays a table from a SQL database. The table is displayed using a JScrollPane. At first I thought that my other components (JLabel and JTextField) weren't being added to the content pane but they actually were they were just hidden underneath the ScrollPane. After reducing the ScrollPane's dimensions, now these other components show up but they are unable to be positioned with the setBounds method and always appear in the same place so that the last component added covers up the other ones entirely. As well as the code I've also included a screenshot of what the GUI looks like.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

public class LibraryAppGUI extends JFrame {
    String sql;
    String DB_PATH = LibraryAppGUI.class.getResource("LibraryManagement3.sqlite").getFile();
    
    private JTable table;
    private String columns[] = {"PatronFirstName", "PatronLastName"};
    private TableModelListener tableModelListener;
    
    public LibraryAppGUI () {
        DefaultTableModel model = new DefaultTableModel(columns, 0);
        table = new JTable(model);
        
        try{populateSQL(table);} catch(Exception e1) {e1.printStackTrace();}
        table.setCellSelectionEnabled(true);
        table.setPreferredScrollableViewportSize(new Dimension(600, 300));
        table.setFillsViewportHeight(false);
        
        
        JScrollPane scrollPane = new JScrollPane(table);
        scrollPane.setVisible(true);
        getContentPane().add(scrollPane, BorderLayout.PAGE_START);
    }
    

    
    public void createSQL() throws ClassNotFoundException, SQLException {
        Class.forName("org.sqlite.jdbc");
        Connection connection = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH);
        PreparedStatement stmt = connection.prepareStatement("");
        
    }
    public void populateSQL(JTable table) throws ClassNotFoundException, SQLException {
        sql = "select PatronFirstName, PatronLastName\r\n" + 
                "FROM Patron\r\n";
        Class.forName("org.sqlite.JDBC");
        Connection connection = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH);
        PreparedStatement stmt = connection.prepareStatement(sql);
        ResultSet res = stmt.executeQuery();
        
        while(res.next()) {
            Object[] row = new Object[columns.length];
            for (int i = 1; i <= columns.length; i++) {
                row[i-1] = res.getObject(i);
            }
            ((DefaultTableModel) table.getModel()).insertRow(res.getRow()-1, row);  
        }
        res.close();
        connection.close();
    }
    
    
    public static void main(String[] args) {
        LibraryAppGUI window = new LibraryAppGUI();
        //label to prompt user
        JLabel welcome = new JLabel("Welcome to the library. Choose your patron: ");
        welcome.setBounds(50,50, 100, 30);
        window.getContentPane().add(welcome);
        
        JTextField user = new JTextField("Enter the full name in this box.");
        user.setBounds(150,150,100,30);
        window.getContentPane().add(user);
        
        window.setDefaultCloseOperation(EXIT_ON_CLOSE);
        window.pack();
        window.setLocationRelativeTo(null);
        window.setVisible(true);
        
    }
} 

GUI


Solution

  • Why do you create your Swing components in two different places?

    1. Don't create components in the main() method.
    2. The label, text field and table should all created and added to the frame in the constructor, the same way you create and add the scrollpane/table.

    In your code below:

    JLabel welcome = new JLabel("Welcome to the library. Choose your patron: ");
    welcome.setBounds(50,50, 100, 30);
    window.getContentPane().add(welcome);
    
    JTextField user = new JTextField("Enter the full name in this box.");
    user.setBounds(150,150,100,30);
    window.getContentPane().add(user);
    

    The setBounds(...) statements should NOT be used. By default the content pane of the frame uses a BorderLayout. The layout manager will set the size/location of the components based on the rules of the layout manager.

    When you don't specify a constraint when you add the components, then the BorderLayout.CENTER is used. However you can only add one component to the CENTER, so only the text field is given the proper size/location. The label is ignored.

    So, assuming you move the GUI code from the main() method to the constructor, the proper design would be to do something like:

    JPanel top = new JPanel();
    top.add(welcome);
    top.add(user);
    
    add(top, BorderLayot.PAGE_START);
    

    then you would also use:

    //getContentPane().add(scrollPane, BorderLayout.PAGE_START);
    add(scrollPane, BorderLayout.CENTER);
    

    This will now display the lable and text field at the top of the frame and the scrollpane in the center. The scrollbars will then automatically adjust as the frame size is changed.