Java Swing Dynamic Field Selection - java

I'm not sure how to ask this question, and I'm certain that there's some kind of other solution to the problem I'm having so if anyone can point me in the right direction, I'd appreciate it.
In any case, the issue I'm having is that I have a String[] list (called "projects") that I'm using to populate a combo box. I want to use the selection from the combo box to dynamically change the form fields listed in a GUI panel.
My approach, so far, isn't dynamic enough because I will have nearly 100 possible selections from the combo box when I'm done. So far, I've been testing with 3 options in the box, but scaling it up to 100 will involve a lot of code, and I think there MUST be some other solution, right? I just don't know what that solution is.
String[] projects = {"Select a project...", "Option1", "Option2", "Option3"};
String[] Option1= {"phone", "maxphv"};
String[] Option2= {"address1", "address2", "house", "predir", "street", "strtype", "postdir", "apttype", "aptnbr"
, "city", "state", "zip"};
String[] Option3= {"phone"};
ArrayList<String> fieldslist, fieldslbllist;
Ideally, I'd like to take the name of the project selected from the projects String[] combo box and reference that name as the name of another list that contains the fields I want to display in the panel.
But I gather from reading on other questions that the name of a variable is irrelevant once the code is compiled.
At this point, I have a set of code to clear the panel and dynamically select the fields, but I still have to manually code the replacement for each of the 100 options. That's not terrible, I suppose, but I think that there is probably a better way that I am unaware of.
public void resetFields() {
fieldslist.clear();
fieldslbllist.clear();
}
public void setFields() {
if (project.getSelectedIndex() == 0) {
resetFields();
}
else if (project.getSelectedIndex() == 1) {
resetFields();
for (int i = 0; i <= Option1.length; i++) {
fieldslist.add(Option1[i]);
fieldslbllist.add(Option1[i]+"lbl");
}
}
else if (project.getSelectedIndex() == 2) {
resetFields();
for (int i = 0; i <= Option2.length; i++) {
fieldslist.add(Option2[i]);
fieldslbllist.add(Option2[i]+"lbl");
}
}
//... onward to 100
The above is just a loop that resets the display on selection of a new option in the combo box and then loops through the options in the OptionX String[] list and adds the values to the fields Array.
Is this a viable way to handle dynamic UI coding? And, is there any way to set it up so I will only have to specify which fields belong to each value and then not have to code a section for each possible project.getSelectedIndex() value in setFields()?

Use CardLayout, seen here, to change the form dynamically. Given the large number of alternatives, look for a hierarchical breakdown among the choices that might allow you to use two related controls, as shown here.

Related

how to auto fill with composite pattern, polymorphism?

Hi I'm working with composite pattern. I'm going to use this example of Head First Design Pattern to explain https://github.com/bethrobson/Head-First-Design-Patterns/tree/master/src/headfirst/designpatterns/composite/menuiterator
Imagine that every menu and submenu have an Id to indentify, it is 10 length.
Something like this
0100000000 menu_1
0101000000 menu_1's subMenu_1
0102000000 menu_1's subMenu_2
0102010000 subMenu_2's subMenu_3
0200000000 menu_2
And what I have at random is the menu item, but it has an ID, which is a Menu ID to which it belongs. For example
0101000000 menuItem_1
0200000000 menuItem_2
So MenuItem 1 belongs to Menu 1's SubMenu 1 and MenuItem 2 belongs to Menu 2.
It would be coded like this.
menu_1.add(subMenu_1);
subMenu_1.add(menuItem_1);
menu_2.add(menuItem_2);
Now how am I filling the menus?
What I'm doing because of I get only the MenuItems, is that I'm cutting the Id to determine where it belongs.
For example you can see that there two Menus, Menu 1 (0100000000) and Menu 2 (0200000000) so I have to cut the first 2 Strings.
I'm coding like this:
class AllMenus implements MenuComponent {
MenuComponent menu_1
MenuComponent subMenu_1
MenuComponent subMenu_2
MenuComponent subMenu_3
MenuComponent menu_2
#Override
add(MenuComponent menu) {
if(menu instanceof Menu) {
super.add(menu)
} else if(menu instanceof MenuItem) {
String subId = menuItem.getId().subString(0,2)
if(subId.equals("01")) {
if(menu_1 == null) {
menu_1 = new Menu();
add(menu_1);
}
subId = menuItem.getId().subString(0,4);
if(subId.equals("0101")) {
if(subMenu_1 == null) {
subMenu_1 = new Menu();
menu_1.add(subMenu_1);
}
subMenu_1.add(menuItem);
} else if(subId.equals("0102")) {
if(subMenu_2 == null) {
subMenu_2 = new Menu();
menu_1.add(subMenu_2);
}
subId = menuItem.getId().subString(0,6);
if(subId.equals("010201")) {
if(subMenu_3 == null) {
subMenu_3 = new Menu();
subMenu_2.add(subMenu_3);
}
subMenu_3.add(menuItem);
}
}
} else if(subId.equals("02") {
if(menu_2 == null) {
menu_2 = new Menu();
add(menu_2);
}
menu_2.add(menuItem);
}
}
}
}
This is for every MenuItem I get. So as you can see this code is to long just for four Menus,imagine thousands of menu, how can it get better?.
I have read that i should use polymorphism to something that repeat, but I don't know how in this case.
If I understand your question properly, I think there's too much assumption of the eventual structure in your code.
In general, number literals in your code should be treated with suspicion - e.g.
if(subId.equals("010201")
... because it looks like configuration, not code. Of course it's fine to put "configuration" in your code, and sometimes it's practical for that configuration to be program code rather than XML/CSV/JSON/etc. -- but even then, it's good to have a logical separation between a "config" class and a "code" class. For example your "config" class might just contain a method that returns arrays of strings:
public class MenuConfig() {
public String[][] configs() {
return new String[][] {
new String[] {"0100000000", "Main menu"},
new String[] {"0101000000", "Settings"},
new String[] {"0101010000", "Look and feel"},
new String[] {"0102000000", "My account"},
// etc.
}
}
}
You're looking to create a tree of submenus -- look for inspiration in code to manipulate trees (e.g. binary trees) -- see how simple the code is and how it doesn't make assumptions beyond the fact that each node has zero or more subnodes.
You're already using polymorphism -- your menus and submenus conform to a common type (I can't tell from your code whether they all have the same concrete type, but in principle I might expect you to have a variety of classes that implement MenuComponent.
Your code to process one menu just needs to parse the ID to work out where it's supposed to go, then find the place it needs to go, and insert it:
(I'm presenting the IDs with hyphens to make it easier to read)
// split "01-02-03-00-00" into [1,2,3] -- ignoring trailing zeros
List<Integer> path = parse(currentMenu.getId());
MenuComponent m = rootMenuComponent;
while(path.size() > 1) {
m = m.getSubMenu(path.remove(0));
}
m.add(path.remove(0), currentMenu());
This simple algorithm assumes that menus are added in the right order -- that is you must have handled 01-02-00-00-00 before 01-02-01-00-00, or you'll get a null pointer.
If you can't live with that constraint, you need to define what could happen, and decide how to deal with it.
Actually with your current scheme, sorting the keys alphabetically should be sufficient
If they're just going to come out-of-order, you could treat the to-do-list of menus to be added as a queue. If a particular item can't yet be added, put it to the back of the queue and retry after processing the rest.
If you want it not to be necessary to explicitly define intermediate menus, then when m.getSubMenu(subMenuNum) returns null (because you've implicitly "defined" it in the middle of a path) you'll need to create the missing menu at that point.
If you want it to be possible to define the same menu more than once (maybe both implicitly and explicitly) then you need MenuComponent.add() to handle that case, either by merging or overwriting, depending on your requirement.
Assembling a graph of nodes like this is what Spring dependency-injection does -- handling building the graph in order, when the dependencies are defined in arbitrary order. If your aim is study, keep building your own. If you just want build a composite of menus, consider using Spring (or some other DI framework).

Re/Create an immutable object that contains an immutable object list

Background
I am trying to create an immutable object that contains a list of immutable objects, as well as object type totals within the list.
I created a slightly sudo gist to try and show what I mean.
Gist - Adjusting an immutable object that contains an immutable object list.
Explanation
My example shows how I'm currently doing it, it does work. However not for all cases.
My VeggieCartView will have a recyclerview that gets filled with a new/saved VeggieCart.
Each VeggieCart has a list of veggies. Veggie totals etc...
I then have a helper class VeggieChanger, it contains an rx.Consumer<Veggie[]> that gets set and accepts any 1:1 veggie changes from the veggie views.
The VeggieCartView sets the consumer so when any one veggie changes, it creates an updated cart using the changes' corresponding cart factory method. The adapter is used to change/retrieve its list.
Working and not
This works well for changing one at a time, however batching changes is throwing concurrency exceptions.
I realize my gist is not runnable and doesn't show most boilerplate, and that I may be fundamentally wrong with some or all of my approaches. With that said I still hope someone can give me advice on how to better implement what I'm trying to do.
If more information is needed to understand, please ask. Thank you for anybody who does have help to offer,
Jon.
I ended up figuring out my main issue.
By adding this to my VeggieCartView:
public void bagAllCanned() {
final Veggie[] canned = new Veggie[cart.canTotal()];
final Veggie[] bagged = new Veggie[canned.length];
int t = 0;
final List<Veggie> veggies = cart.veggies();
for (int i = 0; i < veggies.size(); i++) {
final Veggie veggie = veggies.get(i);
if (veggie.canned()) {
canned[t] = veggie;
if (veggie instanceof Potato)
bagged[t] = Potato.can(veggie);
else if (veggie instanceof Tomato)
bagged[t] = Tomato.can(veggie);
t++;
}
}
for (int i = 0; i < canned.length; i++) {
veggieChange(canned[i], bagged[i]);
}
}
It fixes the concurrency errors.
I'm still unsure if my approach is correct or not. So even though the question is mostly answered, opinions are still VERY welcome.

Dynamically create JTextAreas?

Working on a GUI in Eclipse using WindowBuilder and ran into a roadblock..
I've created a JWindow with a drop-down box intended to display a list of people from a people array. The structure of my classes are:
public class Person {
String name;
int age;
ArrayList<Goal> goals;
}
public class Goal {
String name;
int daysToComplete;
}
Within this JWindow GUI, the drop-down box lists out all of the Person instances. Once I select a person (let's say Bob) - I want to dynamically create labels and JTextAreas to list out Bob's attribute values, for example:
Name: Bob
Age: 20
Goals:
- Goal 1, complete in X days
- Goal 2, complete in Y days
and so on.. I don't want to statically add 3 labels (Name, Age, Goals) and their respective JTextAreas (Bob, 20, Goal 1/Goal 2), because the structure of Person will likely change in the future.
What is the best way to do this?
Thanks!
If I'm understanding you correctly, you can get what you want by creating anonymous instances of JLabel and JTextArea and placing them into an array list. I don't know the specifics of your environment, but hopefully, you can follow the idea:
ArrayList<JLabel> nameLabelList = new ArrayList<JLabel>();
ArrayList<JLabel> ageLabelList = new ArrayList<JLabel>();
ArrayList<Goal> goalList = new ArrayList<Goal>();
// Event handler method
public void personSelected(person)
{
nameLabelList.add(person.name);
ageLabelList.add(person.age);
// This assumes each person has a single goal. You can adapt the code
// for multiple goals easily
goalLabelList.add(person.goal);
}
Then, after the lists are created, all you have to do is loop through these array lists and spit them out into your UI:
for(int counter = 0; counter < nameLabelList.size; counter++)
{
myContainer.add(nameLabelList.get(counter));
myContainer.add(ageLabelList.get(counter));
myContainer.add(new JLabel(goalList.get(counter).toString()));
}
After adding the contents of the array lists, make sure that they show up in the UI:
myContainer.revalidate();
myContainter.repaint();
You will have to add new Panels to your Main Panel. So your Main Panel should provide a scrollable inner Panel to which you add new lines. If you have ever worked with HTML and Tables you will understand what I am talking about.
Once you got this attribute-show-panel (inner Panel), you can load as many attributes into it with a for loop as you want.
The code would technically be like for each goal in goalArray -> add new linePanel to attribute-show-panel.
whereas a linePanel yould store a label, abutton, etc and many line Panels would list vertically

Multiple and single choice test

I am working on a Single choice and Multiple choice test.
I have couple of questions and 4 answers for each question.
I am shuffling the answers as each answer is assigned to radio button.
This is how i am shuffling the arraylist where Random is a arraylist with items and r1,r2,r3,r4 are radio buttons.
random.add(val);
Collections.shuffle(random);
r1.setText(random.get(0));
r2.setText(random.get(1));
r3.setText(random.get(2));
r4.setText(random.get(3));
I am able to display the answers in jumbled way but when i select the answer i need to show that the answer is correct or wrong.
Sample question and options.
1. which language is used for android programming?
A.PHP
B.JAVA
C.C
D.C++
Correct answer is B i need to display that correct answer is B.
How to acheive this.
EDIT:
I have tried this:
Onclick of each radio button assign the value A and compare the value with xml value if its correct display correct but when i jumble its will not work.
EDIT 2
xml
<Question no="1" text="Which Programming language is used in android develoment" type="SCA" noc="4" jumble="NO" correctans="PHP">
<choice a = "PHP" flag="A">
<choice b = "JAVA" flag="B">
<choice c = "C" flag="C">
<choice d = "C++" flag="D">
You can create a hashmap with Option-isOptionCorrect pair. Like for your case:
HashMap<String, Boolean> choices = new HashMap<String, Boolean>();
choices.put("PHP", false);
choices.put("JAVA", true);
choices.put("C", false);
choices.put("C++", false);
Now shuffle the key-value pairs. Your correct choice will be one which has value true in the HashMap.
Egor is clear for what he is suggesting but I'll let you work with your current implementations.
class Option{
//You can add any other parameters if required.
String optionText;
boolean isAnswer;
}
// Use arraylist of Option class like this
ArrayList<Option> options = new ArrayList<Option>(); // in your case random
// Now suffle it.
Collections.shuffle(options);
// get the user selected option and verify using.
if(options.get(userSelectedOptionPosition).isAnswer){
//show "You are Correct!"
}else{
// show "You are In correct!"
}
Hope this will help you.
Here is a somewhat naive solution but it should work.
class Question {
String message;
String answer;
List<String> options; //"Java", "PHP", etc
}
Shuffle the keys of the Map in your Question object
In your radio buttons, do something like r1.setText(random.get(0))
On click, do
String choice = null;
for (RadioButton rb : rBtns) {
if (rb.isSelected) {
choice = rb.getText(); break();
}
}
if (choice.equals(question.getAnswer))
return true; //correct
else
return false; //wrong
The best approach here is not to operate with Strings, but to create a Question class, which will contain all the info about a question: its value, list of answers and the index of the right answer. When parsing the XML, create a list of Question objects and then work with them. There won't be any mapping problems anymore. Hope this helps.
You can hold your answers in your 'random' arraylist in a model, instead of pure strings;
private class AnswerModel {
string answer;
boolean flag;
//... getters and setters...
}
Here you can set your true answer' s flag to true, and all others to false. That way, you can simply return if the answer was correct.
There are already several good answers here. But another option is to write your own shuffle function. The shuffle algorithm is a very simple algorithm that runs in linear time, at least for arrays. By writing your own shuffle function, you can keep track of where the correct answer ends up.
To make this simple, I'm posting code that returns the new index of a specified index in a shuffled collection. The function mutates (alters) the original collection, so this should work.
/**
* #return Returns the new index of the element that was placed at correctIndex,
* or -1 if the correctIndex parameter was out of bounds.
*/
public int shuffleAnswers(Collection<String> collection, index correctIndex) {
String[] shuffleArray = new String[collection.size()];
int returnValue = -1;
collection.toArray(shuffleArray); // Convert to array
collection.clear(); // We have to add the elements again later
// Pick random elements
for (int i = shuffleArray.length; i > 0; i--) {
int randIndex = Math.random() * i;
if (returnValue == -1 && randIndex == correctIndex) {
// This only works if elements are added to the end
// So you may want to limit the function to ArrayLists or LinkedLists
returnValue = collection.size();
}
// Add the randomly selected element to the collection
collection.add(shuffleArray[randIndex]);
// We must ensure that we don't lose elements
// So we swap them down from the end
shuffleArray[randIndex] = shuffleArray[i - 1];
}
return returnValue;
}
Note like the comments say, this only works with collections that add elements to the end of the collection, and that fills arrays from the first to last element added to the collection.
This is perhaps slower than the other solutions, but note that you require a shuffle anyway, so it shouldn't affect running speed much.
Another option is just to have a correctAnswer String and then compare the user's choice to the string (with .equals()).
This is what would I do.
Since you are remembering answer in your XML file and not answers position, then on selected radio button take text (in this case it would be "PHP", "Java", "C++" or "C") and compare it with correct answer.
Jumbling can't affect on this, otherwise you are doing something wrong.

Getting content from JTextFields created dynamically

I am learning Java with Swing and I have some problems with using JTextField. In my program I want to dynamically add a few JTextFields with some text:
while( (ln = bufFile.readLine()) != null ) {
// inIdPanel is JPanel
inIdPanel.add(new JTextField(ln));
}
And it works good. However, the content of these JTextFields can be modified by users, and later I want to call getText() from all of them. Is this possible? How can I do this?
I saw this question: Java Swing: JButton creates new JTextField(s) but this isn't enough to solve my problem (I think using arrays in my case is not a good idea but maybe I'm wrong).
The reason why you cannot call getText() is that you have not stored a reference to the JTextField when you created it. You will need to use an array or collection to store the JtextFields as you create them so you can call the method on them later. A collection will be easier than an array because you do not know how many lines you will read in so you want it to be able to grow.
List<JTextField> fields = new ArrayList<JTTextField>();
while( (ln = bufFile.readLine()) != null ) {
JTextField field = new JTextField(ln);
inIdPanel.add(field);
fields.add(field);
}
Then you can call the .getText() from all of them
for(JTextField field: fields){
System.out.println(field.getText());
}
For an easy solution, just add an ArrayList<JTextField> textFieldList and add to the code you posted:
while((ln = bufFile.readLine()) != null) {
textFieldList.add(new JTextField(ln));
inIdPanel.add(textFieldList.get(textFieldList.size()-1));
}
Then, when you want to access the text fields, you simply iterate through them, e.g.
for (JTextField jtf : textFieldList) {
/* Operate on jtf, call methods, etc */
}
You could replace the ArrayList with an array if there is a defined limit on how many text fields you could add, but the list is nice if that quantity is unknown.

Categories