I'd like to be able to display a mutable list (using Swing) such that the first item is at the bottom in a fixed position, and subsequent items appear above it. Just the way a stack of stuff would appear in reality. The behavior is that of a FIFO queue (add to the top, remove from the bottom).
I can imagine a solution involving "padding" the list and then sorting it in reverse, or something like that, but I wondered if there might be a more direct way.
Example:
item[0]="Adam"
item[1]="Baker"
item[2]="Charlie"
should appear in 5-row list as:
+----------
|
|
| Charlie
| Baker
| Adam
+----------
If you don't want to create a custom model, then you can use the DefaultListModel. Then instead of using:
model.addElement( element );
you can use:
model.add(0, element);
and the elements will be displayed in the order you wish.
The following code also show how you might make the list look bigger than it really is:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
public class ListBottom2 extends JFrame
{
JList list;
JTextField textField;
public ListBottom2()
{
DefaultListModel model = new DefaultListModel();
model.add(0, "Adam");
model.add(0, "Baker");
model.add(0, "Charlie");
list = new JList(model);
list.setVisibleRowCount(5);
JPanel box = new JPanel( new BorderLayout() );
box.setBackground( list.getBackground() );
box.add(list, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane( box );
scrollPane.setPreferredSize(new Dimension(200, 95));
add( scrollPane );
textField = new JTextField("Use Enter to Add");
getContentPane().add(textField, BorderLayout.NORTH );
textField.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JTextField textField = (JTextField)e.getSource();
DefaultListModel model = (DefaultListModel)list.getModel();
// model.addElement( textField.getText() );
model.add(0, textField.getText());
int size = model.getSize() - 1;
list.scrollRectToVisible( list.getCellBounds(size, size) );
textField.setText("");
}
});
}
public static void main(String[] args)
{
ListBottom2 frame = new ListBottom2();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
Provide an implementation of ListModel and that can wrap whatever data structure is appropriate.
Just reverse your array. In one line: (This isn't exactly efficient, but it'll work):
JList list = new JList(Collections.reverse(Arrays.asList(items)).toArray());
Note: It doesn't make sense to have a different UI component which reads the data differently. A ListModel holds the data according to the contract between itself and JList. Creating a new contract is pointless, when its trivial to reorganize the data based on how you want to visualize it, and more importantly, based on UI standards of the operating system.
If anything, you want a reverse ListModel, but, on the same note, it doesn't make sense to have a full implementation of ListModel that just goes in the opposite direction, when really, all you need to do is reverse the order of the backing data structure.
So, that's what you do.
Edit to add:
I read more of what you're trying to do and it looks like you want a fixed size list where data starts at the end (like a stack). In that case, what you really need to do is implement your own ListModel. Take a look at AbstractListModel and you should be able to extend it and provide your own data.
If you do that, your class will then be essentially (consider this p-code, this may not be 100% right):
class ReverseListModel extends AbstractListModel {
public Object getElementAt(int i) {
int dataIndex = fixedSize - i;
if(dataIndex > data.length)
return "";
else
return data[dataIndex];
}
public int getSize() {
return fixedSize;
}
}
Collections utility is enough to reverse a collections Collections.reverse(list)
Related
This is my first post here, and I am very green with Java. This is something I'm trying to make to improve my java knowledge.
I have a button, which when clicked produces a shuffled card deck as a Jlist.
When pressed again, I would very much like it to refresh the JList, or recreate it somehow. Instead, it simply
creates a new list, so I now have 2 JLists.
button1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
cards.choseCards(); //Creates an ArrayList with the suffled cards
JList<String> displayList = new JList<>(cards.deck.toArray(new String[0]));
frame.add(displayList);
frame.pack();
cards.cleanUpDeck(); //Removes all the cards from the ArrayList
}
});
The key here is that Swing uses a model-view type of structure similar to model-view-controller (but with differences) where the model holds the data that the view (the component) displays.
What you are doing is creating an entirely new JList, but what you want to do is to update the model of the existing and displayed JList, either that or create a new model for this same existing JList. JLists use a ListModel for their mode, often implemented as a DefaultListModel object, and so you will want to update or replace this model such as by creating a new DefaultListModel object and then inserting it into the existing JList by calling its setModel(ListModel model) method.
For example your code could look something like this (made with lots of guesses since we don't know what your real code looks like):
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// create new model for the JList
DefaultListModel<String> listModel = new DefaultListModel<>();
cards.choseCards(); //Creates an ArrayList with the suffled cards
// add the cards to the model. I have no idea what your deck field looks like
// so this is a wild guess.
for (Card card : cards.deck) {
listModel.addElement(card.toString()); // do you override toString() for Card? Hope so
}
// Guessing that your JList is in a field called displayList.
displayList.setModel(listModel); // pass the model in
cards.cleanUpDeck(); //Removes all the cards from the ArrayList
}
});
this my code .....
public class A {
JLabel A = new JLabel() ;
public JLabel newform(){
A.setBounds(0 , 197, 409, 245);
A.setIcon(createImageIcon("/Pictures/BG.png"));
return A; }
public void swinginDown_NF ( ){
AnimationClass AC = new AnimationClass();
AC.jLabelYDown(A.getY(), 270, 6, 1,A);
}
class B ....
public class B {
JLabel B = new JLabel() ;
public JLabel Box(){
B.setBounds(170 , 197, 409, 245);
B.setIcon(createImageIcon("/Pictures/BBD.png"));
B.addMouseListener(new java.awt.event.MouseAdapter() {
public final void mouseClicked(java.awt.event.MouseEvent evt) {
A a_class = new A();
a_class.swinginDown_NF();
} });
return B; }
Main...
JFrame frame = new JFrame(" AA ");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
JLabel Label = new JLabel() ;
A a = new A();
B b = new B() ;
Label.add(B.Box());
Label.add(A.newform());
frame.getContentPane().add( Label , BorderLayout.CENTER);
My problem when I click in label Box nothing happen in label newform ...
when I click in label Box function swingDown_NF its open but the label doesnt go down ...... Why ???
You're creating a new A object within the swinging down method, one that is completely separate and unique from the displayed object, and so changing the state of the new object will have no effect on the displayed one. You'll have to pass in a reference of the displayed one to where it's needed. For example you could have the B class accept an A parameter in its constructor.
Some side recommendations:
Please learn and follow Java naming and formatting conventions as your code is very difficult to follow.
You have class and field with the same names, A and B. Again, this will only confuse us and the future you -- never do this.
Avoid null layouts and setBounds if at all possible as it leads to very rigid and hard to debug GUI's. Instead use layout managers.
Your posted code won't compile. It looks like you've tried to simplify your real code for this post which is fine, but in the process you've posted bad non-compilable code including calling an instance method as if it were static. Yes, do simplify your code, but please don't post bad code in the process since you want us to understand your code and your problem well.
I have a JPanel on which I've dynamically added quite a few JButtons. All of this is working perfectly. Later on in my program execution, I need to refer back to these buttons and pull out the button text. I'm having trouble figuring out how to refer back to them.
When I created each button, I gave it a unique name. Let's say this is the code where I created the button:
public void createButton(Container parent, String btnName) {
JButton btn = new JButton("xyz");
btn.setName(btnName);
btn.addActionListner(new ActionListner() {
//code
}
parent.add(btn);
}
In another method, I'm trying to retrieve the label on the button since it may have changed at run time. Do I need to keep an array of these buttons as they are created? Or is there a way that I can refer back to them directly?
This is what I was working on, but it's stupid. Can anyone suggest a correct approach?
public String getBtnLabel(String btnName) {
JButton btn = (JButton) btnName;
return btn.getText();
}
If the answer is that I just need to create the array and then iterate over it, that's fine. Just looking for other options.
You need to use a Map<String, JButton> so when you create your dynamic buttons you give them some sort of unqiue name:
//somewhere at the top of your class
private final Map<String, JButton> myButtonMap = new HashMap<>();
public void createButton(Container parent, String btnName) {
JButton btn = new JButton("xyz");
btn.setName(btnName);
btn.addActionListner(new ActionListner() {
//code
}
parent.add(btn);
myButtonMap.put(btnName, btn);
}
And then simply get from the map
public String getBtnLabel(String btnName) {
return myButtonMap.get(btnName).getText();
}
This will obviously throw an NPE if the button isn't defined...
Also you will need to delete from your map when you're done with it otherwise you're asking for a memory leak...
I suggest you to use a Map< String, JButton >.
At creation time you put new button into it with buttons.put( name, btn )
In event handler you use JButton btn = buttons.get( name )
Yes you need to keep references to the buttons. An array would be an option, but since arrays are awkward to use, you should prefer a List.
If you have a a reference to the JPanel containing the buttons, you could get them from it. but that is likely to be rather bothersome.
I would recommend keeping a list of your buttons or a reference to them in a map, however you could do this:
for (Component i : parent.getComponents()) {
if (i.getName().equals(btnName)) {
JButton b = (JButton) i;
// do stuff..
}
}
Using the parent component and iterating over the added components.
Hello
I'm making a program that tracks the number of users that are logged on by entering names through textfield0. I hit a road block being new to java I still don't understand some things I was wondering how one would add names to the array User in real time and have the array be count it show it in textfield1.
Thank you in advance.
public class UserTracking {
public static void main(String[] args){
final Kong f = new Kong();
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200,110);
f.setVisible(true);
}
}
class Kong extends JFrame implements ActionListener
{
JTextField textfield0 = new JTextField();
JTextField textfield1 = new JTextField();
JLabel label0 = new JLabel("User");
JLabel label1 = new JLabel("Number of Users:");
JButton btnon = new JButton("Log on");
JButton btnoff = new JButton("Log off");
String User[] ={};
public Kong()
{
super("Shazam");
JPanel panel1 = new JPanel(new BorderLayout());
panel1.add(textfield0,BorderLayout.CENTER);
panel1.add(label0,BorderLayout.WEST);
JPanel panel2 = new JPanel(new BorderLayout(10,10));
panel2.add(btnon,BorderLayout.WEST);
panel2.add(btnoff,BorderLayout.EAST);
JPanel panel3 = new JPanel(new BorderLayout());
panel3.add(textfield1,BorderLayout.CENTER);
panel3.add(label1,BorderLayout.WEST);
JPanel frame = new JPanel(new BorderLayout(5,5));
frame.add(panel1,BorderLayout.NORTH);
frame.add(panel2,BorderLayout.EAST);
frame.add(panel3,BorderLayout.SOUTH);
setContentPane(frame);
}
public void actionPerformed(ActionEvent e)
{
}
}
If you've had experience with C++, think of Java arrays as C++ arrays in that once their size is defined, it stays that way. If you wish to increase the capacity, you need to recreate the array and add the contents of the old array inside. Essentially this is what vector did.
In Java if you want an expandable array, you essentially want a List object. I would strongly encourage you to use an ArrayList which is essentially the equivalent of vector in Java, allowing you to add as many objects as you want without worrying about its capacity.
If you ever need an array, you can convert it using the toArray() method (though in my experience, ArrayList does everything you'd require).
The size of an array in java can't be changed after the first initialization.
If you want a dynamic sized data container, you can use any implementation of the List interface. For example ArrayList :
ArrayList<String> user = new ArrayList<String>();
And to add each user to the ArrayList :
user.add(username);
Use an ArrayList.
What you need is not an array, but a more flexible datatype (that is able to "grow" in size) like some List implementation: ArrayList is actually backed by an array, but you don't need to manage (re)allocation yourself.
Instead of taking String array you can take ArrayList it would be easier. Something like this
ArrayList<String> user = new ArrayList<String>();
On some action like button or text change event write this
user.add(textField1.getText());
The actionPerformed method should
add a string into the User array (which should be named users)
ask the array its new length
change the value in the count textfield (textfield1, which really has a bad name)
The problem is that Java arrays have a fixed length. They can't grow. That'w why java.util.ArrayList exists : it's a class that behaves like a dynamic-length array. Read its javadoc to know how to use it.
You are also missing linking buttons to ActionListener. Once that is done, you could use ArrayList to add users to the list as they login and remove users from list as they logoff.
private ArrayList<String> loggedOnUsers = new ArrayList<String>();
and
void actionPerformed(event)
{
if(it's logOn button action)
{
add user to loggedOnUsers list
}
else
{
remove user from loggedOnUsers list
}
}
I noticed I can use getName() as part of the trick.
What is java.awt.Component.getName() and setName() used for?
But I don't really have a clue where to start. What type of listener should I use (assuming the textfield / or box is currently blinking / selected)
This is my previous question, and thank you for the help guys.
How do I use requestFocus in a Java JFrame GUI?
I realize that for each component (Textfield) that I am creating, I have to insert a statement like requestFocus (or using transferFocus).
Is it possible to apply this policy to all the fields???
I have several textfields and ComboBox. The problem I hit is that I don't want to write methods for every single field / box.
For example, I write a method like this
private JTextField getFirstNameEntry() {
.... do something
}
because my instructor writes his program like this
private JPanel getJContentPane() {
jContentPane = new JPanel();
jContentPane.setLayout(new java.awt.FlowLayout(FlowLayout.LEADING));
jContentPane.add(makeLabel(" First Name *", 100, 20));
jContentPane.add(getFirstNameEntry(), null);
jContentPane.add(makeLabel(" Middle Initial", 100, 20));
jContentPane.add(getMiddleInitialEntry(), null);
// etc
return jContentPane;
However, to save redundancy (that was my motive at first), say I have a box, I can simply add the following code inside the method above: getJContentPane()
titleBox = new JComboBox(new String[]{"Mr.","Mrs.","Ms.","Dr.","Prof.","Rev."});
jContentPane.add(titleBox);
But doing this, I still need to create a method to do addItemListener
private void setComboBoxFocus() {
titleBox.addItemListener(
new ItemListener(){
public void itemStateChanged(ItemEvent e){
if(e.getStateChange() == ItemEvent.SELECTED)
{
String titleSelected = titleBox.getSelectedItem().toString();
System.out.println(titleSelected);
titleBox.transferFocus();
}
}
});
}
However, this doesn't really save redundancy at all. If I have more than one ComboBox to be added, I would have to write another similar method. In fact, even in the case with one ComboBox (titleBox), I would still end up with writing a method for titleBox.
So my question is: is there a way to write a general method that can call focus to all (maybe one for ComboBox type)?
Thank you and sorry for the long post.
Why not take a JComboBox argument to your setComboBoxFocus() method, which allows you to set that listener to any JComboBox you may have? Like so:
private void setComboBoxFocus(JComboBox box) {
box.addItemListener(
new ItemListener(){
public void itemStateChanged(ItemEvent e){
if(e.getStateChange() == ItemEvent.SELECTED)
{
String titleSelected = box.getSelectedItem().toString();
System.out.println(titleSelected);
box.transferFocus();
}
}
});
}