Afroid1000
Afroid1000

Reputation: 169

Java Swing: Making a graceful Jscrollpane with or without a JTable

I needed a UI that collects similar pieces of data over and over again which can best be described as a batch entry.To input the batch, i needed a scrollpane that adjusts to whatever size of data the user is inputting. To do this smoothly,I relied on the jtable attached to a jscrollpane, and the row to be a jpanel. I am facing some troubles with this solution, that is

  1. I dont know how to access the textfields in my jpanel
  2. I dont know how to add more jpanels as rows on my jtable.

I may add that i jumped to this solution(which may not necessarily be the best) after trying to add many jpanel to one jscrollpane over and over again but this did not work well.This alternative failed as it seems adding a jpanel through the setviewportview was not designed to accept a new panel to expand the view.A variant of the same is one jpanel attached to one scrollpane, then add many jpanels on the mainpanel attached to the scrollpane but the view did not expand and the scrollpane stayed the same.I have checked out oracle's scrollpane tutorialand seen the dynamic changing of a client and how to revalidate the client but am not sure how this will apply in my case where am adding a new jpanels(unless i add them on one jpanel which i keep setting a new clients preferred size as i add a new jpanel.)

NB:

  1. The jpanel i keep adding is not really new as i am iterating it.
  2. Another third solution i had tried is using a flexible gridlayout which its row is a variable 'x' which the user can choose and the a number of 'x' jpanels can be added, problem was,and this is the main problem, the scrollpane was not gracefull to allow expansion so neither did it adjust to a new view.
  3. Note that the scrollpane i need should not be attached to the JFrame but to a an inner view (like a scrollpane for North or Center only in a borderLayout UI) within the JFrame.
  4. I know that the jpanel as a row can be replaced by a normal row where each cell on the row can act either as a jtextfield,jlabel or whatever component i need, but this approach was taken for a particular need to make the JTable look like an actual physical document.

How do I make a good scrollpane that expands dynamically to addition of jpanels ?

The code below shows two attempts of adding jpanels in the hope of expanding the scrollpane but both fail.

Solution one renders each cell as a jpanel but i cannot access the textfield on the jpanel thus i cannot get the data from the user and i cannot also add another row to create a batch.

    public class JTablePanelRow extends JFrame implements ActionListener{

    JButton addRow = new JButton("Add Row");
    JTable table = new JTable();
    JScrollPane spane = new JScrollPane();
    JPanel mainPanel = new JPanel(new BorderLayout());
    JPanel panelRow = new JPanel();
    JLabel lblName = new JLabel("NAME");
    JTextField txName = new JTextField();
    JLabel lblAge = new JLabel("AGE");
    JTextField txAge = new JTextField();
    TblModel tblmodel = new TblModel();

     public static void main(String[] args) {

     JTablePanelRow tblPane = new JTablePanelRow();
     tblPane.init();

     }

    void init(){
        panelRow.setLayout(new GridLayout(2,2,4,4));
        panelRow.add(lblName);
        panelRow.add(txName);
        panelRow.add(lblAge);
        panelRow.add(txAge); 
        table.setModel(tblmodel);
        table.setRowHeight(50);
        table.getColumn("A").setCellRenderer(new     PanelRenderer(panelRow));
        spane.setViewportView(table);
        addRow.addActionListener(this);
        mainPanel.add(addRow,"North");
        mainPanel.add(spane,"Center");
        this.getContentPane().add(mainPanel);
        this.setVisible(true);
        this.setSize(new Dimension(400,500));
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==addRow){
            tblmodel.addNewRow();

        }
      }
    }


    class TblModel extends AbstractTableModel{


        @Override
        public int getRowCount() {
        return 1;
        }

        @Override
        public int getColumnCount() {
        return 1;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
        return "";
        }

        public void addNewRow(){
               //how to add the jpanel again
        } 

       }

       class PanelRenderer extends DefaultTableCellRenderer{
       JPanel entryPn = new JPanel();

       public PanelRenderer(JPanel entryPn){
       this.entryPn = entryPn;

       }

       @Override
       public Component getTableCellRendererComponent(JTable table, Object value,
  boolean isSelected, boolean hasFocus, int row, int column) {

       entryPn.setPreferredSize(new Dimension(200,50));
       entryPn.setBackground(Color.pink);

       return entryPn;}

          }

Solution two tries to add jpanels through a flexible gridlayout but the jpanels are not added or not scrollable.

public class GridLayoutTrick extends JFrame{

  JPanel mainPanel = new JPanel(new BorderLayout());
  JScrollPane scroll = new JScrollPane();
  JPanel entryPanel = new JPanel();
  JPanel rowPanel = new JPanel();
  int batchNumber = 10;
  JPanel northPanel = new JPanel();


 public static void main(String[] args) {
     GridLayoutTrick glt = new GridLayoutTrick();
     glt.init();
  }

  void init(){
      rowPanel.setBackground(Color.PINK);
      rowPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK,5));
      rowPanel.setPreferredSize(new Dimension(100,50));
      entryPanel.setLayout(new GridLayout(batchNumber,1,4,4));
          for(int i = 0;i < batchNumber;i++){
              entryPanel.add(rowPanel);
          }

      entryPanel.setBackground(Color.BLUE);
      scroll.setViewportView(entryPanel);
      mainPanel.add(northPanel,"North");
      mainPanel.add(scroll,"Center");
      this.getContentPane().add(mainPanel);
      this.setVisible(true);
      this.setSize(new Dimension(400,400));
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      } 
    }

Upvotes: 0

Views: 566

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347184

I'm left scratching my head why you wouldn't just use the editable capabilities of the JTable alone, you're just making life more difficult for yourself by trying to use a JPanel as the cells renderer/editor

You should start by taking a closer look at How to use tables to get a better understanding of how tables are suppose to be used

The following is an overly simplified example which allows you to add any number of rows and edit the values at each cell in the table...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {
        private DefaultTableModel model;

        public TestPane() {
            setLayout(new BorderLayout());
            model = new DefaultTableModel(new Object[]{"Name", "Age"}, 0);
            JTable table = new JTable(model);

            add(new JScrollPane(table));

            JButton btn = new JButton("Add");
            add(btn, BorderLayout.SOUTH);

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    model.addRow(new Object[] {"", ""});
                    table.editCellAt(model.getRowCount() - 1, 0);
                    table.getEditorComponent().requestFocusInWindow();
                }
            });
        }

    }

}

Personally, I'd set up a container object to hold the data and manage it through a custom TableModel, but that's me.

I'd also look at implementing a continuous editing model to allow the user to move easily through the table in a "continuous" editing mode

Upvotes: 0

camickr
camickr

Reputation: 324098

Solution two tries to add jpanels through a flexible gridlayout but the jpanels are not added or not scrollable.

      for(int i = 0;i < batchNumber;i++){
          entryPanel.add(rowPanel);
      }

You only have 1 "rowPanel".

You need to create a separate instance of the "rowPanel" every time you want to add it the the "entryPanel".

  mainPanel.add(northPanel,"North");
  mainPanel.add(scroll,"Center");

Don't use magic constants. Use the fields from the API:

  1. BorderLayout.PAGE_START
  2. BorderLayout.CENTER

Although I really think the suggestions by MadProgrammer are a better solution.

Upvotes: 0

Related Questions