I have problem with showing data using JavaFX lib.
Description of my problem:
I need to show lists of String's and every list have different count of elements.
All that i invented is create listview for every list.
But i have trouble with showing:
For example: I have listview with String's - and following simple code for it:
ListView<String> lst = new ListView<>();
ObservableList<String> observableList = FXCollections.observableArrayList("Hello", "Hello -2");
lst.setItems(observableList);
lst.setPrefHeight(100);
lst.setOrientation(Orientation.HORIZONTAL);
Scene scene = new Scene(new VBox(lst));
primaryStage.setScene(scene);
primaryStage.show();
And when i run it - i see something like:
But i want that elements in listview fills in space in view, i do not want this stripy space in right side.
In my task i have different lists with different count of elements.
If you can help me or get me idea - how it should be done - it will be great!
Thanx!
I think this is the solution that are you looking for:
public class TestController {
#FXML
private ListView<String> listView;
#FXML
public void initialize() {
ObservableList<String> observableList = FXCollections.observableArrayList("Hello", "Hello -2", "More", "Item");
listView.setItems(observableList);
listView.setPrefHeight(100);
Platform.runLater(() -> System.out.println(listView.getWidth()));
listView.setOrientation(Orientation.HORIZONTAL);
listView.setCellFactory(param -> new CustomListCell());
}
private class CustomListCell extends ListCell<String> {
CustomListCell() {
// If you want to use as a separate class you can use the getListView() instead of listView.
prefWidthProperty().bind(listView.widthProperty()
.divide(listView.getItems().size()) // set the width equally for each cell
.subtract(1)); // subtracted 1 to prevent displaying of a scrollBar, but you can play with
// this if you have many values in the listView
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item);
}
}
}
}
Related
So I'm using ComboBox to create a list of items of which the user can choose one. The list is hierarchical/indented in the following way:
Header 1
Item 1
Item 2
Header 1.1
Item 1
Item 2
Header 2
Item 1
Item 2
This looks good, but here comes the problem. When a user clicks on one of the indented items, the item is displayed WITH indentation. So the ComboBox displays, for example, the following:
Item 1
That looks really bad. I want it to be displayed like this (without the preceding indentation):
Item 1
Code for running the program:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// Parent root = FXMLLoader.load(getClass().getResource("interface.fxml"));
ComboBox<String> genre1AddBook = new ComboBox<String>();
ObservableList<String> genresAddBook;
genresAddBook = FXCollections.observableArrayList("SKĂ–NLITTERATUR", "\tDeckare", "\tFantasy");
genre1AddBook.getItems().addAll(genresAddBook);
StackPane root = new StackPane();
root.getChildren().add(genre1AddBook);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 400, 300));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I make the ComboBox display the indented items without the indentation and still display a hierarchical/indented structure?
Given the class you posted, you can use a buttonCell on the combo box that strips the whitespace from the string.
genre1AddBook.setButtonCell(new ListCell<>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty) ;
if (empty || item == null) {
setText("");
} else {
setText(item.stripLeading());
}
}
});
But this is a really bad design; I assume in your real application you would not actually use String as the data class for your ComboBox, if the values in the combo box have additional data associated with them (such as the parent or child nodes in the hierarchical structure). You should set a cell factory on the combo box that indents the cells in the dropdown according to their position in the hierarchy, and a button cell which doesn't indent the text.
I have a project I am working on. I am trying to make a dictionary. For that, I have a .csv file with about 55000 words.I am using the trie data structure which has a startsWith() method which checks whether there is a word in the .csv file which matches the given prefix. I had managed to get it to work to find all words that match the given prefix and display them. Now, I have to develop this into a JavaFX app.
So, I thought of using a ComboBox which has its editable attribute set to true so that I could type into it and then the handler associated with the textProperty() of its editor would display all the words starting with given prefix in the listview of the combobox.
Now, the problem I have is that whenever I click the arrow button of the combobox the application stops responding (I think it's because the list view tries to resize itself to fit the items which are 55000).
So, what I want to know is how to disable the arrow button entirely. I have tried to set its background-color to transparent but even then it can still be clicked I want to make it so that it is disabled and transparent basically the combobox ends up looking like a text field.
If there are better, more efficient ways of implementing a dictionary I would appreciate it if you could guide me.
The ListView is a virtual control that only shows a certain number of cells at a time, it doesn't need to "resize itself to the number of items" in any way that would lock up your GUI.
Does this demo program do what you want?
public class Main extends Application {
public static void main(String[] args) throws IOException, URISyntaxException {
launch(args);
}
#Override
public void start(Stage stage) {
List<String> rawWords = Collections.emptyList();
try {
URI wordURI = new URI("https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(wordURI.toURL().openStream(), StandardCharsets.UTF_8));
rawWords = reader.lines().collect(Collectors.toCollection(() -> new ArrayList<>(6000)));
} catch (IOException | URISyntaxException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
// make the list at least as big as in the question
while(rawWords.size() < 55000) {
ArrayList<String> nextWords = new ArrayList<>(rawWords.size() * 2);
nextWords.addAll(rawWords);
nextWords.addAll(rawWords);
rawWords = nextWords;
}
Collections.sort(rawWords);
ObservableList<String> wordList = FXCollections.observableArrayList(rawWords);
FilteredList<String> filteredList = new FilteredList<>(wordList);
ComboBox<String> combo = new ComboBox<>(filteredList);
combo.setEditable(true);
combo.getEditor().textProperty().addListener((obs, oldVal, newVal) -> {
filteredList.setPredicate(s -> newVal == null || newVal.isEmpty() || s.startsWith(newVal));
});
VBox vbox = new VBox(8,new Label("Dictionary ComboBox"),
combo,
new Label("\n\n\n\nThis space intentionally left blank.\n\n\n\n"));
vbox.setPadding(new Insets(8));
Scene scene = new Scene(vbox, 400, 300);
stage.setTitle("Demo - Filtered Combobox List");
stage.setScene(scene);
stage.show();
}
}
I have a TableView named as "customers" with TableColumns related to customer details.
In the same TableView, I want to add one more TableColum as "Action" in which User should have possibilities to Add,View and delete Product details for particular Customer.
Tried multiple things by googling it but till now didn't get any solution for it.
Just add a TableColumn with a custom cellFactory. BTW: You probably want to use a single column/row for the Buttons, in which case you can simply use HBox or VBox, but you could of course replace the layout in the following code:
TableColumn<Product, Void> buttonColumn = new TableColumn<>("Action");
buttonColumn.setCellFactory(col -> new TableCell<Product, Void>() {
private final VBox container;
{
Button add = new Button("Add");
Button view = new Button("View");
Button delete = new Button("Delete");
delete.setOnAction(evt -> {
// delete this row item from TableView items
getTableView().getItems().remove(getIndex());
});
view.setOnAction(evt -> {
// call some method with the row item as parameter
viewProduct(getTableRow().getItem());
});
add.setOnAction(...);
container = new VBox(5, add, view, delete);
}
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : container);
}
});
tableView.getColumns().add(buttonColumn);
I have an issue with changing cell to an icon with the following code:
TableColumn typeCol = new TableColumn("Account Type");
typeCol.setCellFactory(new Callback<TableColumn<Account, String>, TableCell<Account, String>>() {
#Override
public TableCell<Account, String> call(TableColumn<Account, String> param) {
TableCell<Account,String> cell = new TableCell<Account, String>(){
#Override
public void updateItem(Account item, boolean empty){
if (item != null){
VBox vb = new VBox();
vb.setAlignment(Pos.CENTER);
ImageView imgVw = new ImageView(item.getTypeIcon());
imgVw.setFitHeight(10);
imgVw.setFitWidth(10);
vb.getChildren().addAll(imgVw);
setGraphic(vb);
}
}
};
return cell;
}
});
typeCol.setMinWidth(100);
typeCol.setCellValueFactory(
new PropertyValueFactory<Account, String>("type"));
The issue here is that for some reason I get en error of 'method does not override or implement a method form a supertype'. Any idaes?
TableCell<S, T> extends Cell<T>, not Cell<S>. Therefore the correct signature for the updateItem method of TableCell<Account, String> is
public void updateItem(String item, boolean empty)
Assuming your cellValueFactory
new PropertyValueFactory<Account, String>("type")
returns ObservableValues containing the URLs of images, you can use
ImageView imgVw = new ImageView(item);
instead of
ImageView imgVw = new ImageView(item.getTypeIcon());
Since the value passed to the updateItem method is the one that is contained in the ObservableValue returned by the cellValueFactory.
Sample code for placing an image in a table cell:
import javafx.scene.control.*;
import javafx.scene.image.*;
public class ImageTableCell<S> extends TableCell<S, Image> {
private final ImageView imageView = new ImageView();
public ImageTableCell() {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(Image item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
imageView.setImage(null);
setText(null);
setGraphic(null);
}
imageView.setImage(item);
setGraphic(imageView);
}
}
This will work fine if your table doesn't represent millions of items. If you have many items and can't hold all of the potential images in memory, then you would need a TableCell instead of TableCell where the string is just the URL of the image rather than the actual image data itself. Then you would keep an LRU cache of image data which you would update in the updateItem, fetching the image data from the cache if it was there, otherwise loading it from the url. Such an approach could get a little tricky as you would probably want to be careful not to do too much dynamic loading of images as the user scrolls. In general, if you just have a few hundred or thousand thumbnail images, then the straight-forward approach defined in the code above would suffice rather than the alternate cache based approach.
I am using Javafx v8.0.25-b18.
The problem I occur is that the size of the dynamic combox's dropdown list doesn't change, so if I had initially two items in the dropdown, then the dropdown size will be good for two items, but if I now populate the dynamic combox with three items then I get a small scrollbar inside!?, If I remove an item - I will have a blank space in the combox !?
I want to "reset" the dropdown size each time I put values into it, so it will be the right size each time it gets populated at runtime.
To clarify even more I am adding three images:
1. The first screenshot shows the initial dropdown size of 2
The second screenshot shows the same combox, where now at runtime I am adding 2 values, I EXPECT it to have now a dropdown with the size of 4, but instead the dropdown size stays 2 and only adds an unwanted scrollbar
Last screenshot is when I remove items and only one item remains in the combox, I EXPECT to see a dropdown of 1 item, but instead I unfortunately see a dropdown the size of 2 thus an empty space instead of the second item
I am adding the simple code to create this scenario, I want to thank #Gikkman that helped getting this far and the code is actually his!
public class Test extends Application {
private int index = 0;
#Override
public void start(Stage primaryStage) throws IOException {
VBox vbox = new VBox();
vbox.setSpacing(10);
vbox.setAlignment(Pos.CENTER);
final ComboBox<String> box = new ComboBox<>();
box.setPrefWidth(200);
box.setVisibleRowCount(10);
Button add = new Button("Add");
Button remove = new Button("Remove");
add.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
box.getItems().add("Item " + index++);
box.getItems().add("Item " + index++);
}
});
remove.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if( index > 0 )
box.getItems().remove(--index);
}
});
vbox.getChildren().addAll(add, remove, box);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Try this:
box.hide(); //before you set new visibleRowCount value
box.setVisibleRowCount(rows); // set new visibleRowCount value
box.show(); //after you set new visibleRowCount value
It works for me with editable comboBox and I think it will work in your case.
I had same problem and I solved it with a quick trick.
Just try to show and immediately hide !
add.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
box.getItems().add("Item " + index++);
box.getItems().add("Item " + index++);
box.show();
box.hide();
}
});
Just like to offer my two cents here. You may add the following codes to your combobox which define a custom listview popup that has variable height according to the current number of items. You can tweak the maximum number of items to be displayed in the popup.
yourComboBox.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> param) {
ListCell cell = new ListCell<String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
int numItems = getListView().getItems().size();
int height = 175; // set the maximum height of the popup
if (numItems <= 5) height = numItems * 35; // set the height of the popup if number of items is equal to or less than 5
getListView().setPrefHeight(height);
if (!empty) {
setText(item.toString());
} else {
setText(null);
}
}
};
return cell;
}
});
You don't have to change the number of entries to be displayed. The implementation will handle that automatically.
Say you want to display at most 10 items. Then, you use comboBox.setVisibleRowCount( 10 ); If there are less than 10 items at any time, Javafx will only show as many rows as there are items.
Actually, changing the number of visible rows at runtime can sometimes cause errors, from my experience, so you are better of with just having a set number.
Hope that helps.
I have some problems understanding what the problem is. I made a short example bellow, can you try it and then say what it doesn't do that you want to do.
public class Test extends Application{
private int index = 0;
#Override
public void start(Stage primaryStage) throws IOException{
VBox vbox = new VBox();
vbox.setSpacing(10);
vbox.setAlignment(Pos.CENTER);
ComboBox<String> box = new ComboBox<>();
box.setPrefWidth(200);
box.setVisibleRowCount(10);
Button add = new Button("Add");
Button remove = new Button("Remove");
add.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
box.getItems().add("Item " + index++);
}
});
remove.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if( index > 0 )
box.getItems().remove(--index);
}
});
vbox.getChildren().addAll(add, remove, box);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can use two JavaFx list. First one is previous com box list, another one is final combo box list. then you can change dynamically using yourCombo.getItems().setAll(Your List);
Here is my sample code:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ComboBoxTest extends Application {
#Override
public void start(final Stage primaryStage) throws Exception {
primaryStage.centerOnScreen();
primaryStage.setHeight(200);
primaryStage.setWidth(300);
List<String> list1 = new ArrayList<>();
list1.add("one");
list1.add("two");
list1.add("three");
List<String> list2 = new ArrayList<>();
list2.add("one");
list2.add("two");
list2.add("three");
list2.add("four");
final ComboBox<String> combo = new ComboBox<String>();
combo.getItems().setAll(list1);
Button button = new Button("Change combo contents");
button.setOnAction(event -> {
if ( combo.getItems().size() == 3 ) {
combo.getItems().setAll(list2);
} else {
combo.getItems().setAll(list1);
}
combo.show();
});
VBox box = new VBox(20, combo, button );
box.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene( new StackPane(box) ));
primaryStage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}