I have a JList containing an ArrayList of custom objects and I'm trying to create a drag and drop into fields. I'm having trouble understanding how to package and receive the object in Transferable.
This is about as far as I've gotten:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
public class FlightProjectInterface extends JFrame{
//create GUI Objects
private JFrame primaryFrame;
private JPanel createFlightPanel;
private JPanel aircraftLayout;
private JList personsJList, personsOnFlightJList;
private JTextField pilotLabel, coPilotLabel, backseat1Label, backseat2Label;
public FlightProjectInterface(){
//establish frame
super("Create Flight");
setLayout( new FlowLayout());
//aircraftPanel
aircraftLayout = new JPanel();
aircraftLayout.setLayout(new GridLayout(2,2));
pilotLabel = new JTextField("Drag Pilot Here");
//build person load list
DefaultListModel listModel = new DefaultListModel();
for (Person person : Database.persons)
listModel.addElement(person);
personsJList = new JList(listModel);
personsJList.setVisibleRowCount(5);
personsJList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
personsJList.setDragEnabled(true);
add( new JScrollPane(personsJList) );
aircraftLayout.add(pilotLabel);
add(aircraftLayout);
}//end constructor
}
Clarification: I'm having trouble taking the object selection from the JList and creating a Transferable out of it. With the code above, the toString representation of the object is simply pasted in the text field, so I'm not able to pull object data from the dropped location. How can I "package" the object itself and drop it into a placeholder that I can reference the object itself from the GUI?
Ideally, there would be 4 fields that each contains an object that can be dropped. The person would be removed from the list if they are dropped, but returned to the list if replaced.
Drag and Drop can be a complex beast, not made any easier by the conflicting information that's available. Personally, I like to avoid the Transfer API, but I'm old school like that.
The glue to DnD really is the DataFlavor. I prefer to roll my own, makes life a lot easier.
In this example, I've used a single TransferHandler, but realistically, you really should have one for dragging and one for dropping, in particular, you should have one for each component you want to drop onto.
The main reason for this is, I put a trap in my canImport method to reject it if your dragging over a JList, so you can only drop it on the JLabel, this is a little hack and probably not the best idea.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DnDTransferableTest {
public static void main(String[] args) {
new DnDTransferableTest();
}
public DnDTransferableTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
#SuppressWarnings("serial")
public class TestPane extends JPanel {
private JList<ListItem> list;
private JLabel label;
public TestPane() {
list = new JList<ListItem>();
list.setDragEnabled(true);
list.setTransferHandler(new ListTransferHandler());
DefaultListModel<ListItem> model = new DefaultListModel<ListItem>();
for (int index = 0; index < 10; index++) {
model.addElement(new ListItem("Item " + index));
}
list.setModel(model);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.BOTH;
add(new JScrollPane(list), gbc);
label = new JLabel("Drag on me...");
gbc.gridx++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.NONE;
add(label, gbc);
label.setTransferHandler(new ListTransferHandler());
}
}
#SuppressWarnings("serial")
public class ListTransferHandler extends TransferHandler {
#Override
public boolean canImport(TransferSupport support) {
return (support.getComponent() instanceof JLabel) && support.isDataFlavorSupported(ListItemTransferable.LIST_ITEM_DATA_FLAVOR);
}
#Override
public boolean importData(TransferSupport support) {
boolean accept = false;
if (canImport(support)) {
try {
Transferable t = support.getTransferable();
Object value = t.getTransferData(ListItemTransferable.LIST_ITEM_DATA_FLAVOR);
if (value instanceof ListItem) {
Component component = support.getComponent();
if (component instanceof JLabel) {
((JLabel)component).setText(((ListItem)value).getText());
accept = true;
}
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
return accept;
}
#Override
public int getSourceActions(JComponent c) {
return DnDConstants.ACTION_COPY_OR_MOVE;
}
#Override
protected Transferable createTransferable(JComponent c) {
Transferable t = null;
if (c instanceof JList) {
#SuppressWarnings("unchecked")
JList<ListItem> list = (JList<ListItem>) c;
Object value = list.getSelectedValue();
if (value instanceof ListItem) {
ListItem li = (ListItem) value;
t = new ListItemTransferable(li);
}
}
return t;
}
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
System.out.println("ExportDone");
// Here you need to decide how to handle the completion of the transfer,
// should you remove the item from the list or not...
}
}
public static class ListItemTransferable implements Transferable {
public static final DataFlavor LIST_ITEM_DATA_FLAVOR = new DataFlavor(ListItem.class, "java/ListItem");
private ListItem listItem;
public ListItemTransferable(ListItem listItem) {
this.listItem = listItem;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{LIST_ITEM_DATA_FLAVOR};
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(LIST_ITEM_DATA_FLAVOR);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return listItem;
}
}
public static class ListItem {
private String text;
public ListItem(String text) {
this.text = text;
}
public String getText() {
return text;
}
#Override
public String toString() {
return getText();
}
}
}
Related
In this code, perfs is a String[] and model is a DefaultTableModel associated to a JTable.
for(int i =0; i<100000; i++){
model.addRow(perfs);
}
I would like the rows to appear as they are added, but they only appear once the loop is over. Could this have something to do with te fact the table is in a JScrollPane ?
Here's the full code in case it might be useful
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class gwy implements ActionListener{
private JFrame frame;
private JPanel panel;
private JButton button;
String[] nomsColonnes = {"INSTANCE","Solvabilité","Profondeur(ms)","Largeur(ms)", "Heur. basique(ms)", "Heur. opérations variées", "Heur. distances au carré"};
private DefaultTableModel model = new DefaultTableModel(nomsColonnes, 0);
private JTable tableResultats = new JTable(model);
JScrollPane scrollPane;
public gwy(){
frame = new JFrame();
button = new JButton("Résoudre");
button.addActionListener(this);
scrollPane = new JScrollPane(tableResultats);
tableResultats.setFillsViewportHeight(true);
panel = new JPanel();
panel.setSize(70,50);
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10,10));
panel.setLayout(new BoxLayout(panel,BoxLayout.PAGE_AXIS));
panel.add(button);
panel.add(scrollPane);
frame.add(panel, BorderLayout.CENTER);
//frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("GUI");
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new gwy();
}
#Override
public void actionPerformed(ActionEvent e) {
for(int i =0; i<10000000; i++){
model.addRow(nomsColonnes);
}
}
}
The problem is, Swing is single threaded. This means that until your loops completes, it's blocking the Event Dispatching Thread from processing any new events, which would otherwise update the UI.
Swing is also not thread safe, meaning you should not update the UI, or any state the UI depends on, from out the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
A common way to achieve this would be to use a SwingWorker which can be used to create new rows in a seperate thread, but which can sync the updates back to the Event Dispatching Thread safely.
See Worker Threads and SwingWorker for more details
Runnable example
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private MyTableModel model;
public TestPane() {
setLayout(new BorderLayout());
model = new MyTableModel();
JTable table = new JTable(model);
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
int row = e.getFirstRow();
table.scrollRectToVisible(table.getCellRect(row, 0, true));
}
}
});
add(new JScrollPane(table));
JButton populate = new JButton("Populate");
populate.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
populate.setEnabled(false);
PopulateWorker worker = new PopulateWorker(model);
worker.execute();
}
});
add(populate, BorderLayout.SOUTH);
}
}
public class PopulateWorker extends SwingWorker<Void, MyRowData> {
private MyTableModel model;
public PopulateWorker(MyTableModel model) {
this.model = model;
}
public MyTableModel getModel() {
return model;
}
#Override
protected void process(List<MyRowData> chunks) {
for (MyRowData row : chunks) {
model.addRow(row);
}
}
#Override
protected Void doInBackground() throws Exception {
for (int index = 0; index < 10_000; index++) {
publish(new MyRowData(Integer.toString(index)));
// Decrease this to make it faster
Thread.sleep(125);
}
return null;
}
}
public class MyRowData {
private String identifer;
public MyRowData(String identifer) {
this.identifer = identifer;
}
public String getIdentifer() {
return identifer;
}
}
public class MyTableModel extends AbstractTableModel {
private String[] columnNames = new String[]{"INSTANCE"};
private List<MyRowData> data = new ArrayList<>(32);
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
MyRowData row = data.get(rowIndex);
switch (columnIndex) {
case 0: return row.getIdentifer();
}
return null;
}
public void addRow(MyRowData row) {
data.add(row);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
}
}
nb:: There is a Thread.sleep inside the doInBackground method of the SwingWorker, this is very important. If you remove it, the worker will complete before the UI get's updated. Instead, you can modify the Thread.sleep to increase or decrease the speed at which the updates occur. Just beware, if it's low enough, you might end up with multiple rows appearing at the same time, depending on the thread load.
I have a code in which I must drag two images from my desktop and drop it on a frame in two draggable buttons. The buttons have already been made on the frame. But while dragging the images, they can only be dragged to one button. The images don't get dragged to the other one. I have made a DragListener class where the dragging methods prevail and the main class DragInitialListener where I have passed objects of class DragButton so that two draggable buttons are created. I have tried everything I could think of, made two DragListener classes, passed the methods differently but the image could only be dragged in one button. I want both the buttons to be able to hold images. Please help me with it. Here's the code that I have made so far:
//This is the main class
public class DragInitialListener extends javax.swing.JFrame {
private volatile int draggedAtX, draggedAtY;
public DragInitialListener() {
initComponents();
Droptargets();
Droptarget();
}
public void Droptarget()
{
DragListener d;
DragButton db = new DragButton();
db.setSize(170,140);
d= new DragListener(db);
DropTarget drop = new DropTarget(this,d);
this.getContentPane().add(db);
}
public void Droptargets()
{
DragListener dd;
DragButton db1 = new DragButton();
db1.setSize(170,140);
dd= new DragListener(db1);
DropTarget drop1 = new DropTarget(this,dd);
this.getContentPane().add(db1);
}
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
pack();
}// </editor-fold>
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new DragInitialListener().setVisible(true);
}
});
}
// Variables declaration - do not modify
// End of variables declaration
}
//This is the DragListener class
public class DragListener extends JButton implements DropTargetListener
{
JButton imagebutton = new JButton();
// JButton imagebutton1 = new JButton();
private volatile int draggedAtX, draggedAtY;
DragListener(JButton image) {
imagebutton=image;
}
#Override
public void dragEnter(DropTargetDragEvent dtde) {
}
#Override
public void dragOver(DropTargetDragEvent dtde) {
}
#Override
public void dropActionChanged(DropTargetDragEvent dtde) {
}
#Override
public void dragExit(DropTargetEvent dte) {
}
#Override
public void drop(DropTargetDropEvent ev) {
ev.acceptDrop(DnDConstants.ACTION_COPY);
Transferable t = ev.getTransferable();
//DropTarget test = (DropTarget) ev.getSource();
DataFlavor[] df= t.getTransferDataFlavors();
for(DataFlavor f:df)
{
try
{
if(f.isFlavorJavaFileListType())
{
List<File> files =(List<File>) t.getTransferData(f);
for(File file : files)
{
displayImage(file.getPath());
}
}
}
catch(Exception ex)
{
JOptionPane.showMessageDialog(null, ex);
}
}
}
private void displayImage(String path)
{
BufferedImage img = null;
try
{
img =ImageIO.read(new File(path));
}
catch(Exception e)
{
}
ImageIcon icon = new ImageIcon(img);
imagebutton.setIcon(icon);
}
}
Start simple, get one button to work, if you can get one to work, you can get 100 to work
This is a very simple example, which makes use of the transfer API, because you really only care about dropping and not dragging
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 {
public TestPane() {
setLayout(new GridBagLayout());
JButton btn = new JButton("Drop here");
btn.setVerticalTextPosition(JButton.BOTTOM);
btn.setHorizontalTextPosition(JButton.CENTER);
btn.setTransferHandler(new ImageTransferHandler());
add(btn);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public static class ImageTransferHandler extends TransferHandler {
public static final DataFlavor[] SUPPORTED_DATA_FLAVORS = new DataFlavor[]{
DataFlavor.javaFileListFlavor,
DataFlavor.imageFlavor
};
#Override
public boolean canImport(TransferHandler.TransferSupport support) {
boolean canImport = false;
for (DataFlavor flavor : SUPPORTED_DATA_FLAVORS) {
if (support.isDataFlavorSupported(flavor)) {
canImport = true;
break;
}
}
return canImport;
}
#Override
public boolean importData(TransferHandler.TransferSupport support) {
boolean accept = false;
if (canImport(support)) {
try {
Transferable t = support.getTransferable();
Component component = support.getComponent();
if (component instanceof JButton) {
Image image = null;
if (support.isDataFlavorSupported(DataFlavor.imageFlavor)) {
image = (Image) t.getTransferData(DataFlavor.imageFlavor);
} else if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
List files = (List) t.getTransferData(DataFlavor.javaFileListFlavor);
if (files.size() > 0) {
image = ImageIO.read((File) files.get(0));
}
}
ImageIcon icon = null;
if (image != null) {
icon = new ImageIcon(image);
}
((JButton) component).setIcon(icon);
accept = true;
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
return accept;
}
}
}
So, by doing nothing more then changing the layout and replicating the button using
public TestPane() {
setLayout(new GridLayout(5, 5));
for (int index = 0; index < 5 * 5; index++) {
JButton btn = new JButton("Drop here");
btn.setVerticalTextPosition(JButton.BOTTOM);
btn.setHorizontalTextPosition(JButton.CENTER);
btn.setTransferHandler(new ImageTransferHandler());
add(btn);
}
}
I was able to achieve...
Updated...
So apparently I might have misunderstood the question, not the first time. From what's been explained to me, you might want to drag multiple images and have them applied to the buttons. Surprising, the process doesn't change that much.
In this example, I've applied the TransferHandler to the JPanel instead of the button and supplied it the buttons I want updated. You could easily update this to have a variable number of buttons, but I've started with two.
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 {
public TestPane() {
setLayout(new GridBagLayout());
JButton left = makeButton("Left");
JButton right = makeButton("Right");
add(left);
add(right);
setTransferHandler(new ImageTransferHandler(left, right));
}
protected JButton makeButton(String text) {
JButton btn = new JButton(text);
btn.setVerticalTextPosition(JButton.BOTTOM);
btn.setHorizontalTextPosition(JButton.CENTER);
return btn;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public static class ImageTransferHandler extends TransferHandler {
public static final DataFlavor[] SUPPORTED_DATA_FLAVORS = new DataFlavor[]{
DataFlavor.javaFileListFlavor,};
private JButton left, right;
public ImageTransferHandler(JButton left, JButton right) {
this.left = left;
this.right = right;
}
#Override
public boolean canImport(TransferHandler.TransferSupport support) {
boolean canImport = false;
for (DataFlavor flavor : SUPPORTED_DATA_FLAVORS) {
if (support.isDataFlavorSupported(flavor)) {
canImport = true;
break;
}
}
return canImport;
}
#Override
public boolean importData(TransferHandler.TransferSupport support) {
boolean accept = false;
if (canImport(support)) {
try {
Transferable t = support.getTransferable();
Image image = null;
if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
List files = (List) t.getTransferData(DataFlavor.javaFileListFlavor);
JButton buttons[] = new JButton[]{left, right};
for (int index = 0; index < Math.min(files.size(), 2); index++) {
if (files.size() > 0) {
image = ImageIO.read((File) files.get(index));
ImageIcon icon = null;
if (image != null) {
icon = new ImageIcon(image);
}
buttons[index].setIcon(icon);
}
}
accept = true;
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
return accept;
}
}
}
Now, there are rules you will need to define yourself, for example, what happens when the user only drags a single image? Do you apply it to the first button (as I have) every time, or do you try and find the button without an image and update it? What happens if all the buttons have images? Where does it go then?
Do you reject drags with more than 2 images?
I just started using Java Swing, and I was going through the following post: Dynamic fields addition in java/swing form.
I implement the code in this post with some modification, and it worked fine. As mentioned in the post, JPanel is not resizing itself when we add more rows. Can someone throw more light on this issue with easy to understand explanation that how can we resize JPanel as soon as we hit +/- button? Here is the code :
Row class
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;
#SuppressWarnings("serial")
public class Row extends JPanel {
private JTextField quantity;
private JTextField item;
private JTextField price;
private JButton plus;
private JButton minus;
private RowList parent;
public Row(String initialQuantity, String initalPrice, String initialItem, RowList list) {
this.parent = list;
this.plus = new JButton(new AddRowAction());
this.minus = new JButton(new RemoveRowAction());
this.quantity = new JTextField(10);
this.item = new JTextField(10);
this.price = new JTextField(10);
this.quantity.setText(initialQuantity);
this.price.setText(initalPrice);
this.item.setText(initialItem);
add(this.plus);
add(this.minus);
add(this.quantity);
add(this.item);
add(this.price);
}
public class AddRowAction extends AbstractAction {
public AddRowAction() {
super("+");
}
public void actionPerformed(ActionEvent e) {
parent.cloneRow(Row.this);
}
}
public class RemoveRowAction extends AbstractAction {
public RemoveRowAction() {
super("-");
}
public void actionPerformed(ActionEvent e) {
parent.removeItem(Row.this);
}
}
public void enableAdd(boolean enabled) {
this.plus.setEnabled(enabled);
}
public void enableMinus(boolean enabled) {
this.minus.setEnabled(enabled);
}
}
RowList class
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class RowList extends JPanel{
private List<Row> rows;
public RowList() {
this.rows = new ArrayList<Row>();
Row initial = new Row("1","0.00","", this);
//addHeaders();
//addGenerateBillButton();
addItem(initial);
}
public void addHeaders(){
JLabel qty = new JLabel("Quantity");
//qty.setBounds(10, 0, 80, 25);
add(qty);
JLabel item = new JLabel("Item");
//item.setBounds(70, 0, 80, 25);
add(item);
JLabel price = new JLabel("Price");
//price.setBounds(120, 0, 80, 25);
add(price);
}
public void addGenerateBillButton(){
JButton billGenerationButton = new JButton("Generate Bill");
add(billGenerationButton);
}
public void cloneRow(Row row) {
Row theClone = new Row("1","0.00","", this);
addItem(theClone);
}
private void addItem(Row row) {
rows.add(row);
add(row);
refresh();
}
public void removeItem(Row entry) {
rows.remove(entry);
remove(entry);
refresh();
}
private void refresh() {
revalidate();
repaint();
if (rows.size() == 1) {
rows.get(0).enableMinus(false);
}
else {
for (Row e : rows) {
e.enableMinus(true);
}
}
}
}
Main class
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Enter Items");
RowList panel = new RowList();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You can call the pack() of the frame again.
Try this: Add this in your Row class
public class AddRowAction extends AbstractAction
{
public AddRowAction()
{
super("+");
}
public void actionPerformed(ActionEvent e)
{
parent.cloneRow(Row.this);
((JFrame) SwingUtilities.getRoot(parent)).pack(); // <--- THIS LINE
}
}
public class RemoveRowAction extends AbstractAction
{
public RemoveRowAction()
{
super("-");
}
public void actionPerformed(ActionEvent e)
{
parent.removeItem(Row.this);
((JFrame) SwingUtilities.getRoot(parent)).pack(); // <--- THIS LINE
}
}
You can get the root component (JFrame) using the SwingUtilities.getRoot(comp) from the child component and call the pack() method after your new Row is added to your RowList.
This would resize your JPanel. But your RowList will be horizontal. This is where LayoutManager comes into play.
You can know more about different LayoutManagers here.
To fix this problem, in your RowList panel, set your layout to:
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
Within your refresh method call doLayout() method after revalidate()
Probably a noob question, but im new to java. I have a need for a checkbox list which I found is not supported in swing, but I found this custom control here
http://www.devx.com/tips/Tip/5342
So I created a class file named CheckBoxList, and copied the code from the link into it:
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
public class CheckBoxList extends JList
{
protected static Border noFocusBorder =
new EmptyBorder(1, 1, 1, 1);
public CheckBoxList()
{
setCellRenderer(new CellRenderer());
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox)
getModel().getElementAt(index);
checkbox.setSelected(
!checkbox.isSelected());
repaint();
}
}
}
);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
protected class CellRenderer implements ListCellRenderer
{
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus)
{
JCheckBox checkbox = (JCheckBox) value;
checkbox.setBackground(isSelected ?
getSelectionBackground() : getBackground());
checkbox.setForeground(isSelected ?
getSelectionForeground() : getForeground());
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont());
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ?
UIManager.getBorder(
"List.focusCellHighlightBorder") : noFocusBorder);
return checkbox;
}
}
}
The problem is I don't know how to implement it in my GUI file. I tried a lot of code, but they never showed an example. Just
To use the class, simply instantiate it, then pass it an array of
JCheckBox objects (or subclasses of JCheckBox objects) by calling
setListData
So does that mean that I will not see the control in the Graphical Design view? My client wants to be able to edit it himself and add stuff so I want it to be easy and graphical if possible. If someone could show an example of instantiating it or give a good hint I would appreciate it. Thanks!
Can you just tell me how?
Use a one column JTable and an appropriate renderer and editor. Based on this example, the code below relies on the default renderer for a data value of type Boolean.Class. A more general example is cited here.
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
/** #see https://stackoverflow.com/a/13919878/230513 */
public class CheckTable {
private static final CheckModel model = new CheckModel(5000);
private static final JTable table = new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(150, 300);
}
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
JCheckBox jcb = (JCheckBox) super.prepareRenderer(renderer, row, column);
jcb.setHorizontalTextPosition(JCheckBox.LEADING);
jcb.setText(String.valueOf(row));
return jcb;
}
};
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("CheckTable");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1, 0));
f.add(new JScrollPane(table));
f.add(new DisplayPanel(model));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
private static class DisplayPanel extends JPanel {
private DefaultListModel dlm = new DefaultListModel();
private JList list = new JList(dlm);
public DisplayPanel(final CheckModel model) {
super(new GridLayout());
this.setBorder(BorderFactory.createTitledBorder("Checked"));
this.add(new JScrollPane(list));
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
dlm.removeAllElements();
for (Integer integer : model.checked) {
dlm.addElement(integer);
}
}
});
}
}
private static class CheckModel extends AbstractTableModel {
private final int rows;
private List<Boolean> rowList;
private Set<Integer> checked = new TreeSet<Integer>();
public CheckModel(int rows) {
this.rows = rows;
rowList = new ArrayList<Boolean>(rows);
for (int i = 0; i < rows; i++) {
rowList.add(Boolean.FALSE);
}
}
#Override
public int getRowCount() {
return rows;
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public String getColumnName(int col) {
return "Column " + col;
}
#Override
public Object getValueAt(int row, int col) {
return rowList.get(row);
}
#Override
public void setValueAt(Object aValue, int row, int col) {
boolean b = (Boolean) aValue;
rowList.set(row, b);
if (b) {
checked.add(row);
} else {
checked.remove(row);
}
fireTableRowsUpdated(row, row);
}
#Override
public Class<?> getColumnClass(int col) {
return getValueAt(0, col).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
return true;
}
}
}
The code is expecting a list of JCheckBox objects - so this works
CheckBoxList cbList = new CheckBoxList(); // the class you have
JCheckBox check1 = new JCheckBox("One");
JCheckBox check2 = new JCheckBox("two");
JCheckBox[] myList = { check1, check2}; list of checkbox object
cbList.setListData(myList); // set the list data for the object
Small Swing program using your class below
util;
import javax.swing.*;
public class HelloWorldSwing {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CheckBoxList cbList = new CheckBoxList();
JCheckBox check1 = new JCheckBox("One");
JCheckBox check2 = new JCheckBox("two");
JCheckBox[] myList = { check1, check2};
cbList.setListData(myList);
frame.getContentPane().add(cbList);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
i got stuck at adding a button to a JComboBox editor, I succeeded to add a button but I got some issues like when I first enter to the editor an action perform event gets fired which is unacceptable and the other is I can't get the text typed.
Result:
Problems:
#Override
public Component getEditorComponent() {
return panel;
}
This is the problem, if I return panel.jtexfield I only get a text field without a button, so what's the trick here?
Here is my code
import Store.util.DatabaseHelper;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import org.hibernate.HibernateException;
import org.netbeans.lib.awtextra.AbsoluteLayout;
public class NewComboTest extends JFrame {
private ArrayList<Object> shopCart = new ArrayList<Object>();
private JComboBox cb;
private static final Object[] comboContents = {
"First", "Second", "Third", "Fourth", "Fifth"
};
public NewComboTest() {
super("New Combo Test");
setLayout(null);
cb = new JComboBox();
cb.setRenderer(new NewComboRenderer());
cb.setEditor(new NewComboEditor());
cb.setEditable(true);
cb.setSize(new Dimension(350, 100));
for (int i = 0; i < comboContents.length; i++) {
cb.addItem(comboContents[ i]);
}
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("_____________" + cb.getSelectedItem());
shopCart.add(cb.getSelectedItem());
System.out.println("items added" + shopCart);
}
});
cb.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
System.out.println("KeyReleased" + cb.getEditor().getItem().toString());
populateModel(cb.getEditor().getItem().toString());
}
});
getContentPane().add(cb, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 200, 480, 50));
setSize(1200, 450);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] arg) {
new NewComboTest();
}
private class NewComboEditor extends JPanel implements ComboBoxEditor {
JTextField tf;
JButton eraseButton;
textPanel panel = new textPanel();
public NewComboEditor() {
}
#Override
public void addActionListener(ActionListener l) {
tf.addActionListener(l);
}
#Override
public Component getEditorComponent() {
return panel;
}
public Component getEditorComponent2() {
return panel;
}
#Override
public Object getItem() {
return tf.getText();
}
#Override
public void removeActionListener(ActionListener l) {
tf.removeActionListener(l);
}
#Override
public void selectAll() {
tf.selectAll();
}
#Override
public void setItem(Object o) {
if (o != null) {
tf.setText(tf.getText());
} else {
tf.setText("");
}
}
private class textPanel extends JPanel {
JTextField jTextField1 = new JTextField();
JButton jButton1 = new JButton();
public textPanel() {
setLayout(new BorderLayout());
jButton1.setBackground(new java.awt.Color(255, 255, 255));
jButton1.setForeground(new java.awt.Color(0, 51, 51));
jButton1.setText("X");
jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jTextField1.setText("");
}
});
add(jTextField1, BorderLayout.CENTER);
add(jButton1, BorderLayout.EAST);
}
public String getText(){
return jTextField1.getText();
}
}
}
private class NewComboRenderer extends JLabel implements ListCellRenderer {
public NewComboRenderer() {
setOpaque(true);
}
public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setText(value.toString());
setBackground(isSelected ? Color.BLUE : Color.white);
setForeground(isSelected ? Color.white : Color.red);
return this;
}
}
/* public void populateModel(String text) throws HibernateException {
java.util.List l = DatabaseHelper.GetProductsBy(text);
for (Object object : l) {
cb.addItem(object);
}
ignore this its unnecessary.
*/
}
}
I also wish to set the text font and size to the same as the set up at the combo box.
The first set of problems I can see is, you define a JTextField and JButton in the NewComboEditor, but also define a textPanel, which contains all these things any way. But instead of using the components on the textPane, you use the newly created components (in the NewComboEditor) instead...In fact, I'm not even sure how that could work, because you never initilise these components (in the NewComboEditor), so there should be a NullPointerException...
If that wasn't enough problems, the JTextField and JButton aren't added to anything anyway...
Instead...
NewComboEditor shouldn't need to extend from anything (or it could extend from textPane instead if you really wanted to).
All references to the field should be made to the text field in the textPane
As an example...
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionListener;
import javax.swing.ComboBoxEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CustomComboBoxEditor {
public static void main(String[] args) {
new CustomComboBoxEditor();
}
public CustomComboBoxEditor() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JComboBox cb = new JComboBox();
cb.addItem("Apple");
cb.addItem("Banana");
cb.addItem("Orange");
cb.setEditable(true);
cb.setEditor(new MyComboBoxEditor());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(cb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MyComboBoxEditor implements ComboBoxEditor {
private EditorPane editorPane;
public MyComboBoxEditor() {
editorPane = new EditorPane();
}
#Override
public Component getEditorComponent() {
return editorPane;
}
#Override
public void setItem(Object anObject) {
editorPane.setText(anObject == null ? null : anObject.toString());
}
#Override
public Object getItem() {
return editorPane.getText();
}
#Override
public void selectAll() {
editorPane.selectAll();
}
#Override
public void addActionListener(ActionListener l) {
editorPane.addActionListener(l);
}
#Override
public void removeActionListener(ActionListener l) {
editorPane.removeActionListener(l);
}
}
public class EditorPane extends JPanel {
private JTextField field;
private JButton button;
public EditorPane() {
field = new JTextField(10);
button = new JButton("X");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
add(field, gbc);
gbc.weightx = 0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridx++;
add(button, gbc);
}
#Override
public void addNotify() {
super.addNotify();
field.requestFocusInWindow();
}
public void selectAll() {
field.selectAll();
}
public void setText(String text) {
field.setText(text);
}
public String getText() {
return field.getText();
}
public void addActionListener(ActionListener listener) {
field.addActionListener(listener);
}
public void removeActionListener(ActionListener listener) {
field.removeActionListener(listener);
}
}
}
Now, if you want to set the field's properties to be the same as the combo box, I would simply pass a reference of the combo box to the editor and allow it to extract the properties you need (i.e. font, color, etc.)