Link two Spinner with a button - button

I want the user to do this: when they select an item from the first spinner and the item on the other spinner and click on the button they get the result in a TextView. How can I do that?
I created already two spinner , TextView and one button. For example: if the user select from the first spinner their name and the second spinner their id, when they click to the button the text view will show their telephone number .
UPDATE:
Here is the code for the MainActivity
package second.program.p1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
Spinner v1;
Spinner v2;
ArrayAdapter<CharSequence> adapter;
TextView T1;
Button b;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
v1 = (Spinner) findViewById(R.id.v1);
adapter = ArrayAdapter.createFromResource(this,R.array.city_names,android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
v1.setAdapter(adapter);
v1.getSelectedItemPosition();
v2 = (Spinner) findViewById(R.id.v2);
adapter = ArrayAdapter.createFromResource(this,R.array.city_names,android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
v2.setAdapter(adapter);
T1 = (TextView) findViewById((R.id.T1));
b = (Button) findViewById(R.id.b);
}
};
Strings.xml
<resources>
<string name="app_name">P1</string>
<string-array name="city_names">
<item>" "</item>
<item>"Med"</item>
<item>"Ryu"</item>
<item>"Jedd"</item>
<item>"Hai"</item>
</string-array>
</resources>
So , i want after the user select "Med" from the first spinner (v1) and "Hai" from the second spinner (v2) he press the Button ,after that in the TextView will appers the km (the distance between the two cites, but i dont add it yet)
i did the two spinners ,
now what i should do with the TextView and the Button ?

As you have updated the question, so i can now answer it properly,
There are two methods that are provided by Android which we need to override to handle the items selection of spinners.
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// On selecting a spinner item
String cityName = parent.getItemAtPosition(position).toString();
// Showing selected spinner item
Toast.makeText(MainActivity, "Selected city: " + cityName, Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView<?> view) {
// This method helps when we have to handle when user hasn't selected anything
from spinner on which further events are dependent.
}
Now do button click on your Button b like this in your onCreate:
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view){
String firstCityName = v1.getSelectedItem().toString();
String secondCityName = v2.getSelectedItem().toString();
//now you have both items/cities you selected from spinners
// so its time to do some checks and show user the distance
if(firstCityName.equals("Meh") && secondCityName.equals("Med"){
T1.setText("the value which you have stored somewhere");
} else if(firstCityName.equals("Med") && secondCityName.equals("Ryu"){
T1.setText("30KM / hardcode it or get the value you stored somewhere");
} //.... if-else-if for remaining cases
}
}
Some advices :
1- Please use proper naming conventions.
Spinner spinnerStartingCity = new .....
Button btnSubmit = new ....
2- Topics like these are not so hard to find over internet, first search properly, because what almost all coders DO ?? GOOGLE -> copy/paste !! :D
3- Before googling, if you want to be a Developer not just coder, first go through proper tutorials, android dev site, and online tutorials for thorough understanding because learning lasts longer than copy pasting :)
Happy Developing

Related

JavaFX: ComboBox with custom cell factory - buggy rendering

I have a ComboBox with a custom cell factory, but the rendering is buggy and I don't know what I'm doing wrong.
The ComboBox's data is based on an enum, let's call it MyEnum. So my ComboBox is defined as:
#FXML
private ComboBox<MyEnum> comboBox;
I am setting it up like this:
comboBox.getItems().addAll(MyEnum.values());
comboBox.setButtonCell(new CustomRenderer());
comboBox.setCellFactory(cell -> new CustomRenderer());
comboBox.getSelectionModel().select(0);
My CustomRenderer looks like this:
public class CustomRenderer extends ListCell<MyEnum> {
#Override
protected void updateItem(MyEnum enumValue, boolean empty) {
super.updateItem(enumValue, empty);
if (enumValue== null || empty) {
setGraphic(null);
setText(null);
} else {
setGraphic(enumValue.getGraphic());
}
}
}
The getGraphic() method of MyEnum returns a HBox which can contain any number of elements, but in my specific case it's just an ImageView:
public enum MyEnum{
ONE(new ImageView(new Image(MyApp.class.getResourceAsStream("image1.png"),
15,
15,
true,
true))),
TWO(new ImageView(new Image(MyApp.class.getResourceAsStream("image2.png"),
15,
15,
true,
true)));
private final HBox graphic;
MyEnum(Node... graphicContents) {
graphic = new HBox(graphicContents);
graphic.setSpacing(5);
}
public HBox getGraphic() {
return graphic;
}
}
Now, when I start the app, the first bug is that the ComboBox doesn't show anything selected, even though I have the comboBox.getSelectionModel().select(0) in the initialization:
When I click on it, the dropdown is correct and shows my two entries with their images:
When I select one of the entries, everything still seems fine:
But when I open the dropdown again, then it looks like this:
So suddenly the selected image is gone from the dropdown.
After I select the other entry where the icon is still displayed, and reopen the dropdown, then both images are gone. The images are still shown in the ButtonCell though, just not in the dropdown.
I first thought maybe it has something to do specifically with ImageViews, but when I replaced them with other nodes, like Labels, it was still the same 2 bugs:
Nothing shown as selected on app start
Everything that I click in the dropdown box is then gone from the dropdown
If a runnable sample is needed, let me know. But maybe someone can already spot my mistake from the given code.
Thx
Your issue is that a node cannot appear in the scene graph more than once.
From the node documentation:
A node may occur at most once anywhere in the scene graph. Specifically, a node must appear no more than once in all of the following: as the root node of a Scene, the children ObservableList of a Parent, or as the clip of a Node.
If a program adds a child node to a Parent (including Group, Region, etc) and that node is already a child of a different Parent or the root of a Scene, the node is automatically (and silently) removed from its former parent.
You are trying to reuse the same node in both the button for the list selection and in the selection list itself, which is not allowed.
Additionally, as noted in comments by kleopatra: "it's wrong to use nodes as data". One reason for that (among others), is that if you want to have more than one view of the same data visible at the same time, you won't be able to because the node related to the data can only be attached to the scene at a single place at any given time.
I'd especially recommend not placing nodes in enums. In my opinion, that it is really not what enums are for.
What you need to do is separate the model from the view. The cell factory is creating a view of the changing value of the enum which is the model. This view only needs to be created once for the cell and it needs to be updated (by providing the view with the appropriate image) whenever the cell value changes.
Example Code
You can see in the example that, because we have created two separate views of the same Monster (the dragon), the example is able to render both views at the same time. This is because the views are completely different nodes, that provide a view to the same underlying data (an enum value and its associated image).
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class ChooseYourDoomApp extends Application {
public static final String CSS = "data:text/css," + // language=CSS
"""
.root {
-fx-background-color: lightblue;
-fx-base: palegreen;
}
""";
#Override
public void start(Stage stage) throws Exception {
ComboBox<Monster> choiceOfDoom = new ComboBox<>(
FXCollections.observableArrayList(
Monster.values()
)
);
choiceOfDoom.setButtonCell(new MonsterCell());
choiceOfDoom.setCellFactory(listView -> new MonsterCell());
choiceOfDoom.getSelectionModel().select(Monster.Dragon);
StackPane layout = new StackPane(choiceOfDoom);
layout.setPadding(new Insets(20));
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
}
public class MonsterCell extends ListCell<Monster> {
private ImageView imageView = new ImageView();
#Override
protected void updateItem(Monster item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
imageView.setImage(null);
setGraphic(null);
} else {
imageView.setImage(monsterImages.get(item));
setGraphic(imageView);
}
}
}
public enum Monster {
Medusa,
Dragon,
Treant,
Unicorn
}
private Map<Monster, Image> monsterImages = createMonsterImages();
private Map<Monster, Image> createMonsterImages() {
Map<Monster, Image> monsterImages = new HashMap<>();
for (Monster monster : Monster.values()) {
monsterImages.put(
monster,
new Image(
Objects.requireNonNull(
ChooseYourDoomApp.class.getResource(
monster + "-icon.png"
)
).toExternalForm()
)
);
}
return monsterImages;
}
public static void main(String[] args) {
launch(args);
}
}
Icons are placed in the resource directory under the same hierarchy as the package containing the main application code.
https://iconarchive.com/show/role-playing-icons-by-chanut.html
Dragon-icon.png
Medusa-icon.png
Treant-icon.png
Unicorn-icon.png

How to control specific text selection in a TextArea (JavaFX)?

I'm using JavaFX and have a TextArea. In the textArea I need some way to ensure the user may only select one line at a time. So for my delete button what I have so far is:
deleteButton.setOnAction(e -> {
String s = "DELETE: ";
s += receipt.getSelectedText();
receipt.replaceSelection(s);
});
How can I enforce that user can only select one full line at a time? Each line will have \n as a breaker, so I was thinking I can some how use it as a key. An issue is the users ability to select more than one line at a time or a partial line. And yes, I must use textArea. I have right now where the delete button is reading what got deleted and displays it. The readt of my code is working great with this one issue. I have about 15 classes that all take in textAreas as a parameter where, when a button is clicked, it appends it to the TextArea and then it saves it to a specified object as a certain attribute. I just need highlight control, or a way to added a checkbox or a way to read where the user clicks that highlights the entire row (but if the click somewhere else, it highlights/selects that line, or try to highlight themselves, it doesn't let them).
You can use the filter in a TextFormatter to intercept and modify changes to the selection.
Here is a quick example. You could also modify the change to actually effect the functionality you are looking for on delete as well.
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.stage.Stage;
public class TextAreaSelectByLine extends Application {
#Override
public void start(Stage primaryStage) {
TextArea textArea = new TextArea();
UnaryOperator<Change> filter = c -> {
int caret = c.getCaretPosition();
int anchor = c.getAnchor() ;
if (caret != anchor) {
int start = caret ;
int end = caret ;
String text = c.getControlNewText();
while (start > 0 && text.charAt(start) != '\n') start-- ;
while (end < text.length() && text.charAt(end) != '\n') end++ ;
c.setAnchor(start);
c.setCaretPosition(end);
}
return c ;
};
TextFormatter<String> formatter = new TextFormatter<>(filter);
textArea.setTextFormatter(formatter);
Scene scene = new Scene(textArea, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Getting id from a ActionEvent in JavaFX

I have a group of ToggleButtons that need to find out their id. Currently I can check the id in the getSource() and toString() method like this:
#FXML
public void btnCell(ActionEvent actionEvent) {
System.out.println(actionEvent.getSource());
}
Prints: ToggleButton[id=btn00, styleClass=toggle-button]''
Can I extract the id without relying on some shady substring busniess on that string?
hope it helps:
import javafx.scene.Node;
....
#FXML
public void btnCell(ActionEvent actionEvent) {
final Node source = (Node) actionEvent.getSource();
String id = source.getId();
Scene scene = source.getScene();
scene.lookup("#"+id).getStyleClass() ;
}
In case this is still relevant, I use a whole bunch of programmatically generated buttons (representing menu items in a POS application), identified through MyButton.setUserData(MyProdID), which is loaded from the product IDs in a database table. Then you can get that with MyButton.getUserData() in the ActionEvent handler.

JavaFX TreeView buggy behaviour

I created a TreeView with checkbox tree items. I wanted to add listener to each checkbox, so I created a sample problem. But I got some pretty weird behavior. When I click on a checkbox, it actually triggered the handler for both itself and the one above it.
Here's the code I wrote:
package sample;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.AnchorPane;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable{
#FXML
public AnchorPane container;
public static class CustomTreeCell extends CheckBoxTreeCell<String> {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(item);
((CheckBox) getGraphic()).selectedProperty().addListener(((observable, oldValue, newValue) -> {
System.out.println("Clicked on: " + item);
}));
}
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
TreeView<String> treeView = new TreeView<>();
treeView.setCellFactory((TreeView<String> param) -> new CustomTreeCell());
container.getChildren().add(treeView);
CheckBoxTreeItem root = new CheckBoxTreeItem<>("Root");
treeView.setRoot(root);
CheckBoxTreeItem leaf_A = new CheckBoxTreeItem<>("Leaf A");
CheckBoxTreeItem leaf_B = new CheckBoxTreeItem<>("Leaf B");
CheckBoxTreeItem leaf_C = new CheckBoxTreeItem<>("Leaf C");
root.getChildren().addAll(leaf_A, leaf_B, leaf_C);
}
}
After running the application, when I click on Leaf B, it prints out this:
Clicked on: Leaf A
Clicked on: Leaf B
I have been trying to figure out why this is happening for days but still couldn't find out why.
Any hints are appreciated!
Thanks!
updateItem is called any time the cell is reused to show a different item. This can happen quite often, and the API (deliberately) does not specify exactly when (i.e. it's an implementation detail). Typically, though, it happens frequently if, for example, the user scrolls in the table.
Your updateItem() method adds new listeners to the check box every time, and never removes those listeners. So what you might observe is more and more listeners getting fired the more you have scrolled in the table. (Try adding enough items to your table to allow scrolling, scroll around a lot, and then check/uncheck a check box.)
The intended use is to set the selectedStateCallBack to map to a property in your model class. Then if you want to react to changes, you can do so by observing the property in the model. This is explained in CheckBoxTableCell changelistener not working.

Using JavaFX 2.2 Mnemonic (and accelerators)

I'm trying to make JavaFX Mnemonic work. I have some button on scene and what I want to achieve is to fire this button event by pressing Ctrl+S.
Here is a code sceleton:
#FXML
public Button btnFirst;
btnFirst.getScene().addMnemonic(new Mnemonic(btnFirst,
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)));
Button's mnemonicParsing is false. (Well, while trying to make this work I've tried to set it to true, but no result). JavaFX documentation states that when a Mnemonic is registered on a Scene, and the KeyCombination reaches the Scene unconsumed, then the target Node will be sent an ActionEvent. But this doesn't work, probably, I'm doing wrong...
I can use the standard button's mnemonic (by setting mnemonicParsing to true and prefix 'F' letter by underscore character). But this way user have to use Alt key, that brings some strange behaviour on browsers with menu bar (if application is embedded into web page than browser's menu activated after firing button event by pressing Alt+S).
Besides, standard way makes it impossible to make shortcuts like Ctrl+Shift+F3 and so on.
So, if there some way to make this work?
For your use case, I think you actually want to use an accelerator rather than a mnemonic.
button.getScene().getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#Override public void run() {
button.fire();
}
}
);
In most cases it is recommended that you use KeyCombination.SHORTCUT_DOWN as the modifier specifier, as in the code above. A good explanation of this is in the KeyCombination documentation:
The shortcut modifier is used to represent the modifier key which is
used commonly in keyboard shortcuts on the host platform. This is for
example control on Windows and meta (command key) on Mac. By using
shortcut key modifier developers can create platform independent
shortcuts. So the "Shortcut+C" key combination is handled internally
as "Ctrl+C" on Windows and "Meta+C" on Mac.
If you wanted to specifically code to only handle a Ctrl+S key combination, they you could use:
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)
Here is an executable example:
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SaveMe extends Application {
#Override public void start(final Stage stage) throws Exception {
final Label response = new Label();
final ImageView imageView = new ImageView(
new Image("http://icons.iconarchive.com/icons/gianni-polito/colobrush/128/software-emule-icon.png")
);
final Button button = new Button("Save Me", imageView);
button.setStyle("-fx-base: burlywood;");
button.setContentDisplay(ContentDisplay.TOP);
displayFlashMessageOnAction(button, response, "You have been saved!");
layoutScene(button, response, stage);
stage.show();
setSaveAccelerator(button);
}
// sets the save accelerator for a button to the Ctrl+S key combination.
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#Override public void run() {
fireButton(button);
}
}
);
}
// fires a button from code, providing visual feedback that the button is firing.
private void fireButton(final Button button) {
button.arm();
PauseTransition pt = new PauseTransition(Duration.millis(300));
pt.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
button.fire();
button.disarm();
}
});
pt.play();
}
// displays a temporary message in a label when a button is pressed,
// and gradually fades the label away after the message has been displayed.
private void displayFlashMessageOnAction(final Button button, final Label label, final String message) {
final FadeTransition ft = new FadeTransition(Duration.seconds(3), label);
ft.setInterpolator(Interpolator.EASE_BOTH);
ft.setFromValue(1);
ft.setToValue(0);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
label.setText(message);
label.setStyle("-fx-text-fill: forestgreen;");
ft.playFromStart();
}
});
}
private void layoutScene(final Button button, final Label response, final Stage stage) {
final VBox layout = new VBox(10);
layout.setPrefWidth(300);
layout.setAlignment(Pos.CENTER);
layout.getChildren().addAll(button, response);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
}
public static void main(String[] args) { launch(args); }
}
// icon license: (creative commons with attribution) http://creativecommons.org/licenses/by-nc-nd/3.0/
// icon artist attribution page: (eponas-deeway) http://eponas-deeway.deviantart.com/gallery/#/d1s7uih
Sample output:
Update Jan 2020, using the same accelerator for multiple controls
One caveat for accelerators in current and previous implementations (JavaFX 13 and prior), is that you cannot, out of the box, define the same accelerator key combination for use on multiple menus or controls within a single application.
For more information see:
JavaFX ContextMenu accelerator firing from wrong tab
and the related JDK-8088068 issue report.
The linked issue report includes a work-around you can use to allow you define and use the same accelerator within multiple places within an application (for example on two different menu items in different context menus).
Note that this only applies to trying to use the same accelerator in multiple places within an application, if you don't need try to do that, then you can ignore this information.

Resources