Adding JPanel to JList - java

I want to add a custom objects that extend JPanel into a JList. Everything is fine, but I can not interact with them. For example, I can not type in the JTextField which is added on panel. I Use DefaultListModel<ListItem = new DefaultListModel<ListItem>(); Plase help.
This is custom Object
public class ListItem extends JPanel{
private static final long serialVersionUID = 1L;
private JTextField textField;
public ListItem() {
setLayout(new MigLayout("", "[grow][grow]", "[30px:n:30px][30px:n:30px][30px:n:30px]"));
JLabel lblNewLabel = new JLabel("New label");
add(lblNewLabel, "cell 0 0,alignx trailing");
textField = new JTextField();
add(textField, "cell 1 0,growx");
}
This is the renderer
public class ListItemRenderer implements ListCellRenderer<Object>{
#Override
public Component getListCellRendererComponent(JList<? extends Object> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
Component component = (Component) value;
if(isSelected)
component.setBackground(Color.RED);
return component;
}
This is how I create List
list = new JList<ListItem>(addedItems);
And
ListItem temp = new ListItem();
addedItems.addElement(temp);
list.setCellRenderer(new ListItemRenderer());

I want to add a custom objects that extend JPanel into a JList. Everything is fine, but I can not interact with them. For example, I can not type in the JTextField which is added on panel.
That is because a Jlist does not hold or show components but rather only rendering of components. If you want a list like object that holds components that can be edited, that the user can interact with, then either create your own -- using a JPanel that uses GridLayout and holds a grid of your components, or use a JTable that has at least two columns, one (the "label") that's not editable, and the other (the "text field") that is.

Related

How to find the text of anonymous jtextfield

So i am making a shop system with a gui. I have a menu item that when i press it opens another jframe to input the number of each item sold in a jtextfield, like this:
JPanel salesPanel = new JPanel();
setSize(new Dimension(520,270));
setResizable(false);
setLocation(200,200);
title = new JLabel("<html><u><b>Fill in the number of products sold.</b></u></html>");
salesPanel.setSize(new Dimension(230,30*sw.getProductList().size()));
salesPanel.setLayout(new GridLayout(sw.getProductList().size()+1,3));
...
sw.getProductList().forEach(n ->{
salesPanel.add(new JLabel(Integer.toString(n.getProductID())+":"));
salesPanel.add(new JLabel(Integer.toString(n.getQuantity())));
salesPanel.add(new JLabel(n.getName()));
salesPanel.add(new JTextField());
});
This is how it looks.
Note that sw is the object of the main class which has an ArrayList of the type product which contains the information of each product.
Is there any way that I can text from these JTextFields ? And if not what is another way that I can do this.
EDIT:
in the main ShopWindow class, I have an ArrayList
private ArrayList<Product> productList = new ArrayList<Product>();
class product:
public class Product {
private int productID;
private String name;
private double price;
private int quantity;
private boolean isPerishable;
private double totalProdValue;
...getters and setters for each field
This is a mock solution (only meant to show how to update qty label and clear fields using action listeners)
public class MockFrame {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel framePanel = new JPanel();
ProductPanel bananaPanel = new ProductPanel("268", "25", "Bananas");
ProductPanel sugarPanel = new ProductPanel("321", "200", "Sugar");
JPanel buttonPanel = new JPanel();
JButton update = new JButton("Update");
JButton cancel = new JButton("Cancel");
buttonPanel.setSize(400, 30);
update.setSize(50, 20);
cancel.setSize(50, 20);
buttonPanel.add(update);
buttonPanel.add(cancel);
update.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bananaPanel.setNewQty();
sugarPanel.setNewQty();
}
});
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bananaPanel.clearField();
sugarPanel.clearField();
}
});
frame.setSize(400, 400);
framePanel.setLayout(new BoxLayout(framePanel, BoxLayout.PAGE_AXIS));
framePanel.add(bananaPanel);
framePanel.add(sugarPanel);
framePanel.add(buttonPanel);
frame.add(framePanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class ProductPanel extends JPanel {
private JLabel productId = new JLabel();
private JLabel qty = new JLabel();
private JLabel name = new JLabel();
private JTextField field = new JTextField();
public ProductPanel(String id, String amount, String itemName) {
this.setSize(400, 30);
this.field.setSize(100, 20);
this.field.setColumns(5);
productId.setText(id);
qty.setText(amount);
name.setText(itemName);
this.add(productId);
this.add(qty);
this.add(name);
this.add(field);
}
public void clearField() {
field.setText("");
}
public void setNewQty() {
String newQty = field.getText();
if (newQty != null && !newQty.isBlank()) {
qty.setText(newQty);
}
}
}
}
Main points of this mocked solution:
Use a JPanel to encapsulate a product line item. This will make it easier if you need to remove and/or add product rows.
The product panel contains a method to update qty or clear the fields that will be invokable by the frame buttons (depending on which is clicked).
Simplicity of design - Creating a generic panel for the product eliminate repetitive code.
Obviously, you would have to modify this so that you use the proper layout manager or use absolute positioning to properly aligned components to your liking. Also, you would need to create a Panel for the table header and add the remaining of your products. Also, you may want to break this into public classes and even maybe create a separate class for your frame.
The action listeners could also have a "for-each" loop to update each ProductPanel instead of hard coding each panel individually. That should look something like this:
public void actionPerformed(ActionEvent e) {
JPanel panel = (JPanel)((JButton) e.getSource()).getParent().getParent();
Component[] components = panel.getComponents();
for (Component c : components) {
if (c instanceof ProductPanel) {
((ProductPanel)c).setNewQty();
}
}
}
});
Obviously, your solution will depend on how you decide to encapsulate your components in containers. For this mock, the product panels are inside the frame panel which contains the panels where the buttons were placed. Therefore, I need to get the "grandparent" container for the update and cancel buttons to take advantage of calling the appropriate methods to update and clear in a more dynamic way.
Lastly, you may want to do something more elegant for creating your product panels. For example, you may want to add some factory method to create your product panel instead of having hard-coded product panels like my mock solution. Anyway, I think I demonstrated the solution you were looking for.
UPDATE: If you don't follow Andrew Thompson's recommendation of not using text fields for numeric values, the panel's getNewQty method would need to validate the text obtained to make sure it contains a valid numeric value (which was his point). I would STRONGLY recommend you follow his advice.

Java scrollpane not adding to panel instead displaying white screen

So I have a table that is displaying data from a database but since there's a lot of data being outputted into the table it goes way beyond the bounds I have set for the JFrame.
I've tried adding a ScrollPane below but instead of allowing me to scroll down the GUI it will display white over the whole GUI! All I need to do is make it so I can scroll down to view the whole table!
Here is my code:
public class ViewAll extends JFrame{
//Jtextfields, buttons, labels
private static JButton one = new JButton("Back");
private static JLabel lblMembTitle = new JLabel("<html><h1>All Members</h1></html>");
private static JLabel lblPlayTitle = new JLabel("<html><h1>All Playlists</h1><br /></html>");
//Containers, Panels, Scrollpanes
private Container mainCon = this.getContentPane();
private static JPanel pnlTable = new JPanel();
//Tables
private static JTable tblShowAllMemb = new JTable();
private static JTable tblShowAllPlay = new JTable();
JScrollPane scrollPane = new JScrollPane(pnlTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
public ViewAll(){
super("Search/Edit/Delete Members");
this.setBounds(400, 800, 854,400);
//Add panel and create table model
mainCon.add(pnlTable);
MemTableModel tblMembers = new MemTableModel();
PlayTableModel tblPlaylist = new PlayTableModel();
this.add(scrollPane);
//Add table and set tableModel
pnlTable.add(lblMembTitle);
pnlTable.add(tblShowAllMemb);
tblShowAllMemb.setModel(tblMembers);
pnlTable.add(lblPlayTitle);
pnlTable.add(tblShowAllPlay);
tblShowAllPlay.setModel(tblPlaylist);
}
}
It seems that you add your JPanel pnlTable to multiple containers, first to scrollPane, then to Container mainCon (so it is not in scrollPane, as a component can be only in one container). So when you add scrollPane separetly to ViewAll it display nothing, because it is empty.
So use:
mainCon.add(scrollPane);
or delete mainCon.add(pnlTable); and use only:
this.add(scrollPane);

Get layout constraints for a component in Java Swing

Below is a mock up of code that I've been working on.
public class Pane {
private final JPanel pane;
private JPanel namePanel;
private final JTextField panIdField;
public Pane() {
pane = new JPanel();
pane.setLayout(new MigLayout("", "[][grow]", "[][][][][]"));
namePanel = new JPanel();
pane.add(namePanel, "cell 1 1,growx");
panIdField = new JTextField();
pane.add(panIdField, "cell 1 2,growx");
panIdField.setColumns(10);
}
public void replaceNameField(JPanel newNamePanel) {
this.namePanel = newNamePanel;
// Object constraintsForNamePanel =
pane.remove(namePanel);
pane.add(newNamePanel, constraintsForNamePanel);
}
}
In Container there's method
public void add(Component comp, Object constraints)
Is there any way that we can programatically get the constraints that we set, like getConstraints(...) so that we can use it for later use?
In my code, I want to use it to replace a old component with a new one at the same place.
What do I have to do after
Object constraintsForNamePanel =
to get the constraints for namePanel.
Currently, I'm using
pane.add(newNamePanel, "cell 1 1,growx");
It is working but the problem is I'm using WindowsBuilder for UI and my UI is like to change when I add new components to the pane and I don't want to copy and paste the constraints.
Got the solution, I had to do following.
public void replaceNameField(JPanel newNamePanel) {
MigLayout layout = (MigLayout) pane.getLayout();
Object constraintsForNamePanel = layout.getComponentConstraints(this.namePanel);
pane.remove(this.namePanel);
this.namePanel = newNamePanel;
pane.add(newNamePanel, constraintsForNamePanel);
}

ListCellRendererComponent() method updates all the objects(elements) of JList similar to last added element

hi there i implemented a JList which contains JLabels as elements. My aim is implementing a contact list for server/client chat application. So, when a client connects to a server, JList will build to show his/her contact list.I selected to use JLabels cause they can have icons and text as well. However, i'm getting some trouble about overridden cellrenderer method. Whenever, a client gets online/offline JList updates its state and set all the items similar to the last added item. It's something like this,
this is the first time adding an offline state client into the friend list of a person;
afterwards, this is the second time adding a different client
and finally third time...
Furthermore, i remember that in tutorial it mention about JList overrides pain method and draw whole elements again and again when there is a change in the list. Well actually i'm a newbie about this rendering subject and this thing gets very annoying. Here you can see my renderer class;
RendererSample
and add an element into the model like this way in my main class
labelSetText = tempon.getNickName();
onlineStatus = tempon.isIsOnline();
model.addElement(createPanel());
and also createPanel() returns a JLabel which is like;
public static JLabel createPanel() {
JLabel panel = new JLabel();
return panel;
}
i hope that i have been clear about my problem. I have to achieve that when a contact changes his/her state or when a contact is added this action shouldn't affect the other elements. I will be very appreciate for every answers (and also if you can add brief explanation that what and why you did i will be grateful.) well thanks anyway
You should not put components such as JLabel into JList. Instead, use a model to hold the data and add a renderer to customize the presentation. See How to Use Lists for some examples.
Here is a very basic example of a renderer that adds icon:
import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.*;
public class TestUserList {
public static class UserRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean hasFocus) {
if (value instanceof UserEntry) {
UserEntry user = (UserEntry) value;
JLabel label = (JLabel) super.getListCellRendererComponent(
list, user.getName(), index, isSelected, hasFocus);
if (user.isOnline())
label.setIcon(UIManager
.getIcon("OptionPane.informationIcon"));
else
label.setIcon(UIManager.getIcon("OptionPane.errorIcon"));
return label;
}
return super.getListCellRendererComponent(list, value, index,
isSelected, hasFocus);
}
}
public TestUserList() {
JFrame frame = new JFrame("User List");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JList list = new JList();
JScrollPane scrollPane = new JScrollPane(list);
JPanel content = new JPanel(new BorderLayout());
content.add(scrollPane, BorderLayout.CENTER);
final DefaultListModel model = new DefaultListModel();
model.addElement(new UserEntry("John", true));
model.addElement(new UserEntry("Jesse", false));
list.setModel(model);
list.setCellRenderer(new UserRenderer());
frame.add(content);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public class UserEntry {
private String name;
private boolean online;
public UserEntry(String name, boolean online) {
super();
this.name = name;
this.online = online;
}
public String getName() {
return name;
}
public Boolean isOnline() {
return online;
}
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestUserList();
}
});
}
}

JTable - compound editor focus

I have a custom editor composed of several components. Something like:
class MyCellEditor extends AbstractCellEditor implements TableCellEditor {
JTextArea textArea;
JButton button;
JPanel panel;
MyCellEditor() {
textArea = new JTextArea();
button = new JButton();
panel = new JPanel(new BorderLayout());
panel.add(textArea, BorderLayout.CENTER);
panel.add(button, BorderLayout.EAST);
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
textArea.setText((String) value);
return panel;
}
public Object getCellEditorValue() {
return textArea.getText();
}
}
I want the inner textArea to grab focus when editing starts. It works just fine when I click the cell, but not when I navigate the table with keyboard and start typing in this cell.
How can I fix this?
I had the same problem some time ago and took me ages to find a solution. Tried a lot with focuslistener and stuff, but nothing really seemed to work the way I wanted it to until I found this useful article by Santhosh Kumar.
Its well written and should fix your problem.

Categories