JavaFX initialize array of TextFields - java

I am trying to create array of TextFields in JavaFX but getting error.
I have 10 TextFields: path1, path2... path10.
//initialization
#FXML
private TextField path1,path2,path3,path4,path5,path6,path7,path8,path9,path10;
#FXML
private TextField[] paths = {path1,path2,path3,path4,path5,path6,path7,path8,path9,path10};
However, when I write
String text = paths[0].getText();
paths[1].setText(name);
This first line gets me NullPointerException.
The solution I found is to use Initizlizatior of my Đ¡ontroller, but this is so ugly:
public void initialize(URL fxmlFileColation, ResourceBundle resources) {
paths[0] = path1;
paths[1] = path2;
paths[2] = path3;
paths[3] = path4;
paths[4] = path5;
paths[5] = path6;
paths[6] = path7;
paths[7] = path8;
paths[8] = path9;
paths[9] = path10;
}
How can I get rid of manual assinment in many lines and make
#FXML
private TextField[] paths = {path1,path2,path3,path4,path5,path6,path7,path8,path9,path10};
work?

You cannot make this exact code work. The array creation in
private TextField[] paths = {path1,path2,path3,path4,path5,path6,path7,path8,path9,path10};
happens in the initializer. The loading process for fxml files works like this however:
Create controller class instance (or use a existing one).
Load fxml content injecting objects created by the loader.
Call initialize on the controller, if it exists.
The above code is executed in step 1 while the objects you want to store in the array are created later in step 2.
You can work around this by using the fxml to create a List of the TextFields and inject it to the controller (see my answer to Grouping together JavaFX FXML Objects)
Of course you can also create a array of TextFields containing specific elements using a array initializer in the initialize method:
public void initialize() {
paths = new TextField[] {path1,path2,path3,path4,path5,path6,path7,path8,path9,path10};
}

I don't think you can. You need to declare the variables separately so FXML can initialise the variable. Then, you would need to make your TextField array work by setting each element to something. The best way of doing it would be to use the solution you have in the initialize(...) function and use it for what it meant to do – initialising things.

A simple solution i use
TextField[] txt = new TextField[beanFields.length];
for (int i=0; i<=beanFields.length-1; i++) {
TextField textField = new TextField();
txt[i] = textField;
textField.setPrefWidth(200);
textField.setPrefHeight(32);
}

Related

Updating a LinkedList

My case is very specific here:
First, I have an already-defined array of Strings that contains the default options (which are always the same for all users) of a JComboBox:
private static final String[] JOB_TYPE = {options go here};
These options are loaded into the JComboBox as follows:
private JComboBox jobType = new JComboBox(JOB_TYPE);
Then, there are other options that can vary from one user to another that I have to load inside the JComboBox as well. Since arrays have a fixed number of elements, I had to find another way to add the specific options besides the default ones into the JComboBox. And so what I did was, I created a LinkedList and used the asList method from Arrays to load the default options of the array inside it, then add the other options which vary from one user to another:
private List<String> allJobs = new LinkedList<String>(Arrays.asList(JOB_TYPE));
allJobs can now be passed on as an argument for the JCombobox using the toArray() method:
private JComboBox jobType = new JComboBox(allJobs.toArray());
Now, I have all the default options in allJobs, and since it's no longer an array, I can also add to it the specific options which will be loaded as follows:
for (int j = 0; j < modelJobCustomType.getSize(); j++) {
allJobs.add(((XmlJobCustomElem) modelJobCustomType.getElementAt(j)).getName());
}
Now, here's my problem:
When I check the content of the JComboBox, I only find the default options, and not the specific ones even though I can see using the debugger that the list size has increased and it contains the specific options as well.
My guess is, since jobType, JOB_TYPE and allJobs are global variables, the ComboBox is being populated way before the compiler gets to the part of the code where it loads the specific options as well, and that's probably why I can only see the default options.
Is this correct? And if so, how can I fix this problem. Keep in mind that those global variables have to stay global because they are also being used in many other parts of the class.
Thanks for your help
The combobox is given the array derived from a list. Afterwards adding to the list will not change the array value (arrays are fixed length values),
There also is a JComboBox with a Vector parameter, that allows adding (as opposed to an array).
However the nicest is a JComboBox with a ComboBoxModel<E> parameter that is the most high-level. There is a default implementation DefaultComboBoxModel:
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>(JOB_TYPE);
model.addElement("custom0");
model.addElement("custom1");
combobox = new JComboBox(model);
You can initialize your class like this to let you keep all the jobs in allJobs at initialization rather than the custom ones only existing in the JComboBox as in Jamie's solution. You could also do all the initializing of the instance fields inside the constructors:
class Main {
private static final List<String> FIXED_OPTIONS = Arrays.asList("fixed0", "fixed1", "fixed2");
private List<String> allJobs = new ArrayList<>(FIXED_OPTIONS);
{
int count = 6;
for (int i = 0; i < count; i++) {
allJobs.add("custom" + i);
}
}
private JComboBox jobType = new JComboBox(allJobs.toArray());
}
You can look at the source: JComboBox
181: /**
182: * Constructs JComboBox with specified list of items.
183: *
184: * #param itemArray array containing list of items for this JComboBox
185: */
186: public JComboBox(Object[] itemArray)
187: {
188: this(new DefaultComboBoxModel(itemArray));
189:
190: if (itemArray.length > 0)
191: setSelectedIndex(0);
192: }
The constructor creates a new instance of the DefaultComboBoxModel.

Smooth way to iteratre through two lists JavaFX

I have two sets of Objects, first stores Labels and the second one stores Buttons. Every label is connected to its own button i.e label11 is placed on btn11 and so on. I want to change visibility of labels if I click its button. I managed to do this in this way:
btn11.setOnAction(x->label11.setVisible(true));
btn12.setOnAction(x->label12.setVisible(true));
btn21.setOnAction(x->label21.setVisible(true));
btn22.setOnAction(x->label22.setVisible(true));
btn111.setOnAction(x->label111.setVisible(true));
btn112.setOnAction(x->label112.setVisible(true));
btn121.setOnAction(x->label121.setVisible(true));
btn122.setOnAction(x->label122.setVisible(true));
Doing this way I have to write every single line one by one which makes my code unnecessary long and hard to read.I tried placing them into two lists but couldn't find a way to iterate through two lists in one loop.
So far I managed to create something like this:
for(int i=0;i<listOfButtons.size();i++){
listOfButtons.get(i).setOnAction(x->listOfLabels.get(i).setVisible(true));
}
But I'm getting error which says that variables in lambda expressions should be final.
I'd be glad for any help.
Thank you in advance.
Assign the values you need to a final variable before using it in the lambda. This will also increase readability.
for (int i = 0; i < listOfButtons.size(); i++) {
final Button button = listOfButtons.get(i);
final Label label = listOfLabels.get(i);
button.setOnAction(x -> label.setVisible(true));
}
You can do it in a "smooth"-er way by utilizing some additional Java 8 functionality instead of the traditional for and final declarations which should be resolving an issue with i and can therefore be fixed with final int fi = i; and use the final one instead in your lambda. Or this:
IntStream.range(0, buttons.size())
.forEach(i -> buttons.get(i).setOnAction(e -> labels.get(i).setVisible(true)));
Additionally, given your example of poorly named label and button references, I would also suggest you put the pair in a convenience class and a single list of the components.
class ButtonLabelPair {
Button button;
Label label;
}
List<ButtonPairs> buttonPairs = ...
And set the actions like:
buttonPairs.forEach(p -> p.button.setOnAction(e -> p.label.setVisible(true)));
Or, you could do it in the constructor of the pair:
class ButtonLabelPair {
...
public ButtonLabelPair(Button button, Label label) {
this.button = button;
this.label = label;
this.button.setOnAction(e -> this.label.setVisible(true));
}
}

Why the Item deleted from an array but isn't deleted from my table?

I've put together a few methods that are suppose to delete a searched item from an array and the data from the array is also being put into a JTable through a method called createLoginTable().
When my delete button actionListener Method is carried out the element or login is successfully deleted from the array: 'listOfLogins' but the element does not appear to be deleted from the JTable as it is still there.
Here are the methods starting with the actionListener:
if(e.getSource()==deleteLoginButton)
{
int loopNo = list.nextLogin; ///Variables used in the 'removeLogin' Method
String foundLogin = list.listOfLogins[foundLocation].toString();
Login[] loginList = list.listOfLogins;
LoginList list = new LoginList(); //The 'list' is wiped
list.removeLogin(loginList, foundLogin, loopNo);
list.writeLoginsToFile(); //Writes logins to file (not integral to the array)
String[][] loginTableLogins = new String[50][2]; //Wipes the JTable Array
createLoginsTable(); //Creates the JTable
searchLoginButton.setEnabled(true);
editLoginButton.setEnabled(false);
deleteLoginButton.setEnabled(false);
addLoginButton.setEnabled(true);
}
This is the 'removeLogin' Method (This is in a seperate 'list' class):
public void removeLogin(Login[] array, String unwantedLogin, int loop)
{
for(int i=0;i<loop;i++)
{
String currentLogin = array[i].toString();
if(!currentLogin.equals(unwantedLogin))
{
Login login = new Login();
addLogin(array[i]);
}
}
}
plus 'addLogin' Method (although i am assured this is not the source of my issue):
public void addLogin(Login tempLogin)
{
listOfLogins[nextLogin] = tempLogin;
System.out.println(listOfLogins[nextLogin]);
nextLogin++;
System.out.println(nextLogin);
}
And the 'createLoginsTable' method:
public void createLoginsTable()
{
for(int i=0;list.nextLogin>i;i++)
{
loginTableLogins[i] = list.listOfLogins[i].toArray();
System.out.println(list.listOfLogins[i].toString());
}
JTable loginsTable = new JTable(loginTableLogins, loginTableTitles);
JScrollPane loginsScrollPane = new JScrollPane(loginsTable);
loginsScrollPane.setBounds(400, 200, 200, 250);
testPanel.add(loginsScrollPane);
}
I have used 'System.out.println's so I am 99% certain that the element has been removed from the array (it is also apparent through my writeLoginsToFile Method) So I hope this information helps.
Your code is a little bit hard to decipher, next time maybe put in also the enclosing class, or some details about that class. What does the following line do:
LoginList list = new LoginList(); //The 'list' is wiped
You say the list is wiped, I think what is does is: it declares a list local variable and assigns a new object to it (and it masks the other list variable which you used a few lines earlier). Now, in the createLoginsTable() method you don't have this local variable, you have the "list" which I guess is a public field in your class. Now what you can do, is or pass the local list variable to the above function as a parameter createLoginsTable(list) or try the wiping line without the declaration so only:
list = new LoginList(); //The 'list' is wiped
Anyway, your code seams a little bit troubled it, maybe you should refactor it a little bit. Hope it helps.
You're not returning the table after you delete the item.
When you call the method to delete it and write out the table, that table is not returned after you remake the table.
Take this:
JScrollPane loginsScrollPane = new JScrollPane(loginsTable);
Bring it outside of your method. What I think might be happening is when you create your loginsScrollPane locally inside the method, it's not being added properly to your testPanel.
I think what might be happening is when you add it, and the method ends it's loosing that data that is contained. Declare your scrollpane, and your jtable where you declare your frame.

Prepend beginning to object name and set text in java

I'm trying to pick up java quickly and looking for a way to set the text of a number of labels in my java app.
What I have is a java app that starts\stops\checks status of windows services. I have a method, which is passed an array of these service names and each of these services has a corresponding label that contains it's status. For example, DummyService1 is contained in the array and there is a label called txt_DummyService1. My method (short version) does the following
public static void Checker(String Array[])
{
//check status of DummyService1
"txt_"+DummyService.Text = "started";
}
I realize that this isn't the way that you do this, but could anybody help me out with the best way to do this?
There's no way to generate a "variable" name from a String in this manner. Yes, you might use reflection, but that already rasies questions about the quality of the design.
Instead. Place each label into a Map keyed by it's name.
private Map<String, JLabel> labelLookup = new HashMap<>(25); // Instance variable.
In you constructor (or where ever you build your UI), add each label to the Map.
/* Other UI code */
labelLookup.put("DummyService1", txt_DummyService1);
Now, when you need to do you changes, simply look up the label by it's name
// You had better have a VERY good reason for making this static...
public void checker(String services[])
{
for (String service : services) {
JLabel label = labelLookup.get(service);
if (label != null) {
label.setText("Started");
}
}
}
For example...
Actually I was looking for something more like the following
public static void Checker()
{
try
{
Object Instance = getClass().getDeclaredField("txt_DummyService").get(this);
Method m = Instance.getClass().getMethod("setText",String.class);
m.invoke(Instance,"started");
}
catch(Exception e)
{
//exception handling
}
}
You cannot manipulate variable names at runtime since these are only available to the compiler. One solution to your problem is to keep a Map<String, JLabel> (assuming you are using JLabel and not some other component) to associate a name with each JLabel. I'm sure there are several other possible solutions depending on the exact design of your code.

JTextField getText() not working

I've been looking all over, and i cant find anyone who can solve this problem. I'm making a game, and in that game, i have editable controls. the controls window is a seperate JFrame, and when i click the confirm button, it is supposed to write the items in the JTextFields (holding the controls) to a file. but that wasnt working, so instead i have it print the arraylist that holds the values. here is the code:
public void writeControls() {
ArrayList<String> al = new ArrayList<String>();
al.add(up.getText());
al.add(down.getText());
al.add(left.getText());
al.add(right.getText());
al.add(jump.getText());
al.add(duck.getText());
al.add(attack.getText());
for (int i = 0; i < al.size(); i++) {
System.out.println(al.get(i));
}
System.exit(0);
}
the problem is this: if i change the final JTextField attack or any other one for that matter, and click submit, the system prints out the default controls. for example, if the JTextFields have the values w,a,s,d,r,t,q and i change the value q to i, it prints out q. what am i doing wrong? thanks in advance!
EDIT 1:
code for the textfields, and the FILES.... is simply a string stored in a different class. the class setText() is below the textfields.
up = new JTextField(setText(FILES.controlsFileFinalDir, 1));
down = new JTextField(setText(FILES.controlsFileFinalDir, 2));
left = new JTextField(setText(FILES.controlsFileFinalDir, 3));
right = new JTextField(setText(FILES.controlsFileFinalDir, 4));
jump = new JTextField(setText(FILES.controlsFileFinalDir, 5));
duck = new JTextField(setText(FILES.controlsFileFinalDir, 6));
attack = new JTextField(setText(FILES.controlsFileFinalDir, 7));
public String setText(String fileDir, int lineNum) {
String txt = "";
txt = io.readSpecificLine(fileDir, lineNum);
txt = switchCase(txt);
return txt;
}
switchcase() is only taking what you have written in the text file that these are getting the values from, and translating them. so if the value is 0, it is turned into Space, etc. io.readSpecificLine(); is only to get the line of text from the file. does this help?
EDIT 2:
i just was dinking around and found out that if i set the JTextField text by using setText(""); then use getText(); it works. so the problem is that when i change it manually, and use getText(); it wont work. Why?
To update the text to a currently existing JTextField, I would establish the JTextField as a class variable, and create a setter/getter method to adjust it (which I'm assuming you're doing).
According to your methods, you would use something like:
up.setText(setText(FILES.controlsFileFinalDir, 7));
Edit: **The first setText is the JTextField.setText, the second setText is your public method you posted. I'm assuming your second getText() isn't working because you're probably not setting the text correctly.
Without seeing more code, I can't really give a better guess.
The main possibilities:
(1) The text fields have their editable property set to false.
(2) You are creating multiple copies of the JTextFields, then editing a new one on the screen, but referring to the old one when you get the value.
(3) You have a ValueChanged or LostFocus event handler that is resetting the text fields to their defaults
(4) It is actually JFormattedTextField not a JTextField
If I was you, I would try to debug the programm. You will probably do some Mistake in your code, you won't be able to see, by just checking the code.
For example in which order do you call the functions and so on, maybe you have a fault here, or maybe you have several threads, so you try to read the Textfields without even set them and so on ... It's hard to say without reviewing the whole Code.
So if you use eclipse you can follow this link for an explanation on how to debug: http://www.vogella.com/articles/EclipseDebugging/article.html
Netbeans or any other IDE should support debugging as well.
This may seem like a strange thing to suggest, but I think this is an issue with pointers. If you create a new string before passing it in, JTextField will be able to change it internally and return what you expect when asked for the modified value.
down = new JTextField("" + setText(FILES.controlsFileFinalDir, 2));
// or
down = new JTextField(new String(setText(FILES.controlsFileFinalDir, 2)));
You might want to try the following:
create a class Test.java
import java.util.ArrayList;
import javax.swing.JTextField;
public class Test implements Runnable {
private ArrayList<JTextField> textFields = null;
private ArrayList<String> stringList = null;
public Test(ArrayList<JTextField> textFields, ArrayList<String> stringList) {
this.textFields = textFields;
this.stringList = stringList;
}
#Override
public void run() {
for ( JTextField textField : this.textFields )
this.stringList.add( textField.getText() );
}
}
and then, at the place where you use the "getText() method .. "
do the following...
ArrayList<JTextField> textFields = new ArrayList<JTextField>();
// add all the JTextField to textFields
ArrayList<String> stringList = new ArrayList<String>();
Test test = new Test( textFields, stringList );
SwingUtilities.invokeLater( test );
// check if the stringList is populated.
If this work, then what I believe is that, for some reason, the JTextField hasn't finished
"setting" the text, and before it finishes your getText() was called. I've had similar problems before, and this solved my problem that time, but still, this might not be the perfect solution.
First, you should change your "setText()" method name to something like "getTextFromFile()" it would be more readable
Then, if you are setting and reading the new text in different threads, my bet is that the setText() is taking long to return, because it is accessing the file system, while the method that read the values run instantly
I would try to do run a little test:
public void test(){ // must be run after the JTextFields be initialized
up.setText("TEST")
System.out.println(up.getText());
up.setText(setText(FILES.controlsFileFinalDir, 1));
System.out.println(up.getText());
}
If the test() prints the correct values, then we can assume that if you set and read the new value in the same thread it works fine
The other test I would do is:
public void testThread(){
new Thread(){
public void run(){
while(true){
if(up!=null){
System.out.println(up.getText());
}
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}.start();
}
It will print the value of up each 1 second, so that you can see if after some time you get the new value. If it does, then the answer is: Your setText() is taking long to run and you are reading the value before the new value is set
SOLUTION
none of the above answers were working for me, so i finally decided to just start over with that class. the few things i changed were the way i made the JTextFields. I made them as an array instead of individual objects. Second is the way i put what they say. When i initialized them, i was unable to get them to create WITH the text in the parameters. so i had to do that seperately. i changed some of the method names so as to reduce future confusion, and it worked! so im not sure what was up with that, maybe it was the way i did it, maybe just a fluke. it happens sometimes, so im sorry for the delay and waste of your time! thanks for all the answers anyway!
Try this:
textbox.setText(setFile(args)); // your function for set file

Categories