0

I am dynamically creating JTextField based on click event on '+' button. Below is screen shot.

enter image description here

The problem is when I click '+' button, fields created but not shown on JFrame. When I put the cursor on next row under 'item Name', text field becomes visible.

Where is the problem?

enter image description here

Below is my code.

CreateBill()
{

    jf = new JFrame("Create Bill");
    jf.getContentPane().setLayout(null);
    jf.setExtendedState(JFrame.MAXIMIZED_BOTH);
    jf.setBounds(0, 0, d1.width, d1.height);
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);



    createRow();  // This will create first row by default.
    jf.pack();
    jf.setVisible(true);

}


private void createRow() {

    textField = new JTextField();
    textField.setToolTipText("item name");
    textField.setBounds(143, 46+j, 288, 20);
    textField.setColumns(10);
    textField.getDocument().addDocumentListener(new DocumentListener()
    {

        @Override
        public void insertUpdate(DocumentEvent e) {
            updatePrice();

        }

        @Override
        public void removeUpdate(DocumentEvent e) {


        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            updatePrice();

        }

    });

    AutoCompleteDecorator.decorate(textField, names, true);
    jf.getContentPane().add(comboComplete);
    jf.getContentPane().add(textField);
    comboComplete.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            String ItemSel = textField.getText().trim();
            for(Item s:items)
            {
                if(s.getItemName().equals(ItemSel))
                        {
                            textField_1.setText(String.valueOf(s.getUnitPrice()));
                        }
            }


        }
    });


    textFields.add(textField);
    textField_1 = new JTextField();
    textField_1.setEditable(false);
    textField_1.setBounds(639, 46+j, 175, 20);
    jf.getContentPane().add(textField_1);
    textField_1.setColumns(10);



    qty = new JTextField();
    qty.setBounds(455, 46+j, 156, 20);
    jf.getContentPane().add(qty);
    qty.setColumns(10);

    qty.getDocument().addDocumentListener(new DocumentListener()
    {

        @Override
        public void insertUpdate(DocumentEvent e) {
                getTotal();

        }

        @Override
        public void removeUpdate(DocumentEvent e) {


        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            getTotal();

        }

    });

    textFields.add(qty);
    textFields.add(textField_1);


    textField_3 = new JTextField();
    textField_3.setEditable(false);
    textField_3.setBounds(1038, 46+j, 156, 20);
    jf.getContentPane().add(textField_3);

    textField_3.setColumns(10);



    textFields.add(textField_3);

    JButton button = new JButton("+");
    button.setBounds(1235, 45+j, 89, 23);
    jf.getContentPane().add(button);
    button.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("value of J"+j);
            createRow();                    // after pressing '+' button i am calling same method again. by changing value of j.

        }
    });

    j=j+22;

    jf.setVisible(true);

}

I want my all 4 text fields appear simultaneously.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Deepak nigam
  • 99
  • 1
  • 15
  • Use a [`CardLayout`](http://download.oracle.com/javase/8/docs/api/java/awt/CardLayout.html) as shown in [this answer](http://stackoverflow.com/a/5786005/418556). – Andrew Thompson Jan 28 '17 at 17:59

1 Answers1

3

You need to call repaint() on the container after adding a component to it. You also should call revalidate() too, before calling repaint, since this tells the layout managers to layout the new component, but you're using null layouts, something that you really want to avoid doing.

So my suggestion is to either 1) use nested JPanels with appropriate layout managers, and call revalidate and repaint on your containers after adding or removing components, or 2) yeah, use a Cardlayout to swap views as Andrew Thompson astutely recommends. You can have your second JPanel have a JTextField that uses the same Document as the previous JPanel, so it looks like both are using the same JTextField (as the top JTextField).

On looking further at your images, I have to wonder if a JTable might be an even better solution overall. And yeah, after you start using the layout managers, do also call pack() on your top level window after adding all components and before setting it visible.

For an example of a JTable implementation of this, something along the lines of...

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

@SuppressWarnings("serial")
public class CreateRowGui extends JPanel {
    private static final Item[] ITEMS = { 
            new Item("Light Bulb", 2.00), 
            new Item("Toilet Paper", 3.00),
            new Item("Toothpaste", 1.50), 
            new Item("Aspirin", 3.75) };
    private ItemTableModel tableModel = new ItemTableModel();
    private JTable table = new JTable(tableModel);
    private AddRowAction addRowAction = new AddRowAction("Add Row", KeyEvent.VK_A);

    public CreateRowGui() {
        TableCellRenderer moneyRenderer = new DefaultTableCellRenderer() {
            private NumberFormat currFormat = NumberFormat.getCurrencyInstance();

            @Override
            protected void setValue(Object value) {
                if (value != null) {
                    value = currFormat.format(value);
                }
                super.setValue(value);
            }
        };
        table.getColumnModel().getColumn(2).setCellRenderer(moneyRenderer);
        table.getColumnModel().getColumn(3).setCellRenderer(moneyRenderer);

        JPanel btnPanel = new JPanel();
        btnPanel.add(new JButton(addRowAction));
        btnPanel.add(new JButton("Remove Row")); // TODO: need Action for this

        setLayout(new BorderLayout());
        add(new JScrollPane(table));
        add(btnPanel, BorderLayout.PAGE_END);
    }

    class AddRowAction extends AbstractAction {
        private NewRowPanel newRowPanel = new NewRowPanel(ITEMS);

        public AddRowAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            newRowPanel.reset();
            int reply = JOptionPane.showConfirmDialog(table, 
                    newRowPanel.getMainPanel(), 
                    "Select Item and Quantity",
                    JOptionPane.OK_CANCEL_OPTION, 
                    JOptionPane.PLAIN_MESSAGE);
            if (reply == JOptionPane.OK_OPTION) {
                Item item = newRowPanel.getSelectedItem();
                int quantity = newRowPanel.getQuantity();
                tableModel.addRow(item, quantity);
            }
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("CreateRowGui");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new CreateRowGui());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

class NewRowPanel {
    private JPanel mainPanel = new JPanel();
    private JComboBox<Item> itemsCombo;
    private JSpinner quantitySpinner = new JSpinner(new SpinnerNumberModel(0, 0, 20, 1));

    @SuppressWarnings("serial")
    public NewRowPanel(Item[] items) {
        itemsCombo = new JComboBox<>(items);
        itemsCombo.setRenderer(new DefaultListCellRenderer(){
            @Override
            public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
                    boolean cellHasFocus) {
                if (value != null) {
                    value = ((Item) value).getName();
                } else {
                    value = "";
                }
                return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            }
        });
        mainPanel.add(new JLabel("Item:"));
        mainPanel.add(itemsCombo);
        mainPanel.add(Box.createHorizontalStrut(15));
        mainPanel.add(new JLabel("Quantity"));
        mainPanel.add(quantitySpinner);
    }

    public void reset() {
        itemsCombo.setSelectedIndex(-1);
        quantitySpinner.setValue(0);
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }

    public Item getSelectedItem() {
        return (Item) itemsCombo.getSelectedItem();
    }

    public int getQuantity() {
        return (int) quantitySpinner.getValue();
    }
}

class ItemTableModel extends AbstractTableModel {
    private static final String[] COL_NAMES = { "Item Name", "Quantity", "Unit Price", "Total" };
    private static final long serialVersionUID = 1L;
    private List<ItemWithCount> itemsWithCount = new ArrayList<>();

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

    @Override
    public int getRowCount() {
        return itemsWithCount.size();
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        switch (columnIndex) {
        case 0:
            return super.getColumnClass(columnIndex);
        case 1:
            return Integer.class;
        case 2:
        case 3:
            return Double.class;
        }
        return super.getColumnClass(columnIndex);
    }

    @Override
    public String getColumnName(int column) {
        return COL_NAMES[column];
    }

    @Override
    public Object getValueAt(int row, int column) {
        ItemWithCount itemWithCount = itemsWithCount.get(row);
        switch (column) {
        case 0:
            return itemWithCount.getItem().getName();
        case 1:
            return itemWithCount.getCount();
        case 2:
            return itemWithCount.getItem().getUnitPrice();
        case 3:
            return itemWithCount.getCount() * itemWithCount.getItem().getUnitPrice();
        }
        return null;
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        ItemWithCount itemWithCount = itemsWithCount.get(rowIndex);
        switch (columnIndex) {
        case 1:
            itemWithCount.setCount((int) aValue);
            fireTableRowsUpdated(rowIndex, rowIndex);
            break;

        default:
            break;
        }
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex == 0 || columnIndex == 1;
    }

    public void addRow(Item item, int quantity) {
        ItemWithCount itemWithCount = new ItemWithCount(item, quantity);
        itemsWithCount.add(itemWithCount);
        int row = itemsWithCount.size() - 1;
        fireTableRowsInserted(row, row);
    }

    private class ItemWithCount {
        private Item item;
        private int count;

        public ItemWithCount(Item item, int count) {
            this.item = item;
            this.count = count;
        }

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        public Item getItem() {
            return item;
        }

    }
}

class Item {
    private String name;
    private double unitPrice;

    public Item(String name, double unitPrice) {
        this.name = name;
        this.unitPrice = unitPrice;
    }

    public String getName() {
        return name;
    }

    public double getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(double unitPrice) {
        this.unitPrice = unitPrice;
    }

    @Override
    public String toString() {
        return "Item [name=" + name + ", unitPrice=" + unitPrice + "]";
    }

}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • For better help, consider creating and posting a [mcve]. – Hovercraft Full Of Eels Jan 28 '17 at 18:02
  • 1
    *"You also **should** call .."* `pack()` in case the frame size is not large enough to accommodate the field. Alternately, use a card layout and pack on start-up (which I prefer). – Andrew Thompson Jan 28 '17 at 18:02
  • 1
    @AndrewThompson: yep. I have to wonder if a JTable might not be a cleaner solution for this GUI. – Hovercraft Full Of Eels Jan 28 '17 at 18:04
  • *"I have to wonder if a JTable might not be a cleaner solution.."* I have not looked closely at the problem, but if you think a table is a better way to go, I'll trust your judgement. :) – Andrew Thompson Jan 28 '17 at 18:05
  • Thanks! Problem is resolved after adding revalidate and repaing method in 'createRow'. – Deepak nigam Jan 28 '17 at 18:21
  • @Deepaknigam also be sure to not set bounds manually, it will give you more problems than advantages, see [null Layout is evil](http://www.leepoint.net/GUI/layouts/nulllayout.html) and [this post](http://stackoverflow.com/questions/6592468/why-is-it-frowned-upon-to-use-a-null-layout-in-swing) too. See how to use a [layout manager](https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html) especially `Flow Layout` and `BoxLayout` or `GridLayout` or `GridBagLayout` which could help you get a similar output or a `JTable` as shown in this useful answer – Frakcool Jan 29 '17 at 14:50