I have a single column TableView display Labels with an icon. What I'd like to do is add an animated icon to the label or cell so users know it's loading. I'm not sure how to go about this. I've looked around the internet and haven't come up with anything. I haven't had any luck trying to extend the label and customize the drawing, it's not quite as straight forward as overriding a paint method. So if anyone could point me in the right direction on how I could achieve this effect.
I think you cannot do that since the cell renderer will travel across cells and invoke paint for each cell. So I don't think you can do that and show animated icons. What you can do is to change some property of the row that is loading and invoke transition.
You can get some ideas from this code and get some similar effect:
https://github.com/james-d/Animated-Table-Row/blob/master/src/animatedtablerow/AnimatedTableRow.java
Thought I already posted this but in case this comes up for anyone else looking to do something similar, here was my solution:
sourceColumn.setCellFactory(new Callback<TableColumn<SourceItem, SourceItem>,TableCell<SourceItem, SourceItem>>(){
public TableCell<SourceItem, SourceItem> call(TableColumn<SourceItem, SourceItem> param){
TableCell<SourceItem, SourceItem> cell = new TableCell<SourceItem, SourceItem>(){
#Override
public void updateItem(SourceItem item, boolean empty) {
if (!empty) {
HBox box = new HBox();
box.setAlignment(Pos.CENTER_LEFT);
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
box.getChildren().addAll(item.getLabel(), spacer);
if (item instanceof ShareSourceItem) {
if (((ShareSourceItem)item).isResolving()) {
box.getChildren().addAll(loadImage(mediaPath+"loader2.gif"));
}
}
setGraphic(box);
}
}
};
return cell;
}
});
Related
I'm working on an Java app that changes components size after it's window size changes and i have a problem with ComboBox dropdown. After window size changes, first expanding of the dropdown doesnt change it's width and i get this. When i expand the list second time, it works well, but only when i use following code:
comboBoxWindowSize.setCellFactory(new Callback<ListView<WindowSize>, ListCell<WindowSize>>() {
#Override
public ListCell<WindowSize> call(ListView<WindowSize> param) {
ListCell cell = new ListCell<WindowSize>() {
#Override
public void updateItem(WindowSize windowSize, boolean empty) {
super.updateItem(windowSize, empty);
setPrefHeight(padH25);
getListView().setPrefWidth(padW150);
if (!empty) {
setText(windowSize.toString());
} else {
setText(null);
}
}
};
return cell;
}
});
When i don't use this code, width stays incorrect all the time. I want dropdown width to be equal to ComboBox width. Will you help me fix that?
Ok, i found an answer. It's very ugly, but it actually works.
Just like i said above, it's size is incorrect only in the first expanding. So all i had to do, is to expand and collapse it programmatically after i change window size:
comboBoxWindowSize.show();
comboBoxWindowSize.hide();
And thats all.
The last column of my Table Viewer contains a check box only. The check box appears in the left side of the cell, and because the column name is pretty long it looks ugly as hell. How can I center the check box in the middle of the cell ? Is it possible without using images ? Here is how I create the column:
// third column - check box. temporary
TableColumn column = new TableColumn(viewer.getTable(), SWT.NONE);
column.setText("PrettyLongColumnName");
column.setWidth(100);
TableViewerColumn checkColumn = new TableViewerColumn(viewer, column);
checkColumn.setLabelProvider(new ColumnLabelProvider() {
// the checkboxes should be disposed and rebuilt when input changes
#Override
public void update(ViewerCell cell) {
MyObject system = (MyObject) cell.getElement();
TableItem item = (TableItem) cell.getItem();
Button button;
if (buttonsMap.containsKey(cell.getElement())) {
button = rightTableButtons.get(cell.getElement());
} else {
button = new Button((Composite) cell.getViewerRow().getControl(), SWT.CHECK);
button.setEnabled(true);
buttonsMap.put(cell.getElement(), button);
TableEditor editor = new TableEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
editor.setEditor(button, item, cell.getColumnIndex());
editor.layout();
}
}
}
});
TL;DR: Not available nativley, but can be implemented via epic hackery.
Checkboxes are actually an OS feature. As SWT is cross-platform, we rely on it being provided by OS.
AFIK the only thing provided by all OS's (Gtk/Win32/Cocoa) is a single checkbox on the first column.
Other
fancy functionality has to be implemented manually.
One way I've seen people do it is to draw custom icons and then update the icon when you click on it with event listeners.
One example on how to draw icons on the right is in this snippet. You'd have to add click listener to change the icon when clicked into checked/unchecked.
Note, this may cause your application to look inconsistent across platforms and themes (e.g dark theme).
To get around this, we have had some people that actually generate a native checkbox, then programatically take a screen shot, then draw it in the right side of a column. I think this is hackery at it's finest.
Let me know if you have questions.
My layout issue
I have a little issue with ListView and I'm not sure if it's because of some knowledge I missing or if my approach is flawed. Have to admit I'm not yet clear with how JavaFX handle the layout in the many possible cases.
The above screenshot shows the result I get twice with the exact same code, except that on the second one an invisible shape I use for coherent layout is made visible for debug.
The various classes involved by the CellFactory extend Group, I tried with some other Parent without much success so far.
How to reproduce
Rather than sharing my StarShape, StarRow and some other misc classes (I'd be happy to if requested) I wrote a sample reproducing the issue. The class extends Application and overrides the start(...) method as such:
#Override
public void start(Stage primaryStage) throws Exception {
final StackPane root = new StackPane();
final Scene scene = new Scene(root, 400, 600);
final ListView<Boolean> listView = new ListView<>();
listView.setCellFactory(this::cellFactory);
for (int i = 0; i < 5 ; i++) {
listView.getItems().add(true);
listView.getItems().add(false);
}
root.getChildren().add(listView);
primaryStage.setScene(scene);
primaryStage.setTitle("ListView trims the invisible");
primaryStage.show();
}
where this::cellFactory is
private ListCell<Boolean> cellFactory(ListView<Boolean> listView) {
return new ListCell<Boolean>() {
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
final Rectangle tabShape = new Rectangle();
tabShape.setHeight(20);
tabShape.setWidth(40);
tabShape.setVisible(item);
final Label label = new Label(item.toString());
label.setLayoutX(40);
final Group cellRoot = new Group();
cellRoot.getChildren().add(tabShape);
cellRoot.getChildren().add(label);
setGraphic(cellRoot);
}
}
};
}
The above will display a ListView<Boolean> with black shapes in front of true items (because of the tabShape.setVisible(item); bit). The false items are looking like regular Label objects as if the invisible shape in their Group wasn't there (but it is).
Closing comments
Debugging this, it turns out groups with the invisible shapes are given negative layoutX property values. Thus Label controls aren't aligned as I'd like them to be. It doesn't happen when I call setLayoutX and setLayoutY outside of a ListView (the invisible shapes do force offsets), but it's probably not the only place where it would happen.
What's happening and how to avoid it? Alternatively, as I'm guessing I'm approaching this wrong, what'd be the right way? In other words, what is the question I should be asking instead of this?
Taking from #dlatikay's comment, instead of setting the placeholder items to invisible, you can render them transparent by setting their opacity to 0.0.
Applied to the MCVE from your question, this would be done by replacing:
tabShape.setVisible(item);
with:
tabShape.setOpacity(item ? 1.0 : 0.0);
In terms of user experience, you could take this one step further. Instead of setting the "inactive" stars to fully transparent, you could set them to be near-transparent, as in this mockup (with opacity set to 0.1):
The benefits that I see are:
It indicates not only the rating of an item in the list, but also the maximum rating.
It avoids awkward empty spaces for list items with zero stars.
I'm guessing I'm approaching this wrong
No, you're not. As with all layouts, there's often multiple ways to approach the same problem. Your approach is actually correct, and you're very close to a working solution.
You can achieve what you're after with a mere 1 line change. That is, changing the Group to an HBox.
An HBox ensures that elements are ordered horizontally, one after another. They also allow invisible elements to still take up space.
I also commented out one line: label.setLayoutX(40). I did this because HBox will not respect this setting, and actually you don't need it to. It will automatically shift the elements horizontally by as much is required.
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
}
else {
final Rectangle tabShape = new Rectangle();
tabShape.setHeight(20);
tabShape.setWidth(40);
tabShape.setVisible(item);
final Label label = new Label(item.toString());
//label.setLayoutX(40);
final HBox cellRoot = new HBox();
cellRoot.getChildren().add(tabShape);
cellRoot.getChildren().add(label);
setGraphic(cellRoot);
}
}
When I make those changes, your layout will render like so:
Important: Your example and your screenshots are slightly different. You may want to use a VBox for your star example (V for 'vertical', H for 'horizontal').
I'm trying to change the highlight/focus/hover color of menu items.
I'm trying to change the blue background to another color, but nothing seems to work?
I've tried a few things with no luck from: How do you set the style for a JavaFX ContextMenu using css? and How to style menu button and menu items
.context-menu:focused {
-fx-background-color:white;
-fx-focus-color:white;
}
.menu-item:focused {
-fx-background-color:white;
-fx-focus-color:white;
}
.menu:focused {
-fx-background-color:white;
-fx-focus-color:white;
}
and many other variations...
Also some example code that's using the menu item's
// Menu
final ContextMenu contextMenu = new ContextMenu();
and construct a MenuItem:
maximizeMenuItem = new MenuItem(Config.getString("Maximize"));
maximizeMenuItem.setOnAction(new EventHandler<ActionEvent>() { /* do stuff */ }
I could try a:
contextMenu.setStyle("-fx-focus-color:white");
or
maximizeMenuItem.setStyle("-fx-focus-color:white");
but I can't seem to figure out which -fx- css tag controls that blue background color...
If possible, please post the FXML solution as well as the in-line code solution.
Ok, a little embarrassed. I had my layers messed up to where my stylesheet wasn't being applied like I thought it was.
So the correct way to change the menu-item's background color when focused is:
.menu-item:focused {
-fx-background-color: #969A9F;
}
Once I found and sorted out my css layering problem, it now works as expected as result it:
I'm trying to do something pretty simple. I want to place an icon in a column for a particular row in a table. If it's a folder, display a folder icon. If it's a file, display a file icon.
Does anyone know how to do this in JavaFX 2?
I've tried so many things and this seems like it should be pretty simple or at least an example somewhere.
Okay so I had a huge dummy moment. Turns out that I had my image url path wrong.
I did find a site that provides a great example for adding elements for table. This helped me understand everything.
Now if the 4 different ways I tried before would've worked, I don't know because my image url path was wrong. But anyway here is the link and a code snippet.
Bottom line was that you need to have the CellValueFactory and the CellFactory. I was attempting to use either or. The updateItem template method in TableCell relies on the value dervied from CellValueFactory.
http://blog.ngopal.com.np/2011/10/01/tableview-cell-modifiy-in-javafx/
TableColumn albumArt = new TableColumn("Album Art");
albumArt.setCellValueFactory(new PropertyValueFactory("album"));
albumArt.setPrefWidth(200);
// SETTING THE CELL FACTORY FOR THE ALBUM ART
albumArt.setCellFactory(new Callback<TableColumn<Music,Album>,TableCell<Music,Album>>(){
#Override
public TableCell<Music, Album> call(TableColumn<Music, Album> param) {
TableCell<Music, Album> cell = new TableCell<Music, Album>(){
#Override
public void updateItem(Album item, boolean empty) {
if(item!=null){
HBox box= new HBox();
box.setSpacing(10) ;
VBox vbox = new VBox();
vbox.getChildren().add(new Label(item.getArtist()));
vbox.getChildren().add(new Label(item.getAlbum()));
ImageView imageview = new ImageView();
imageview.setFitHeight(50);
imageview.setFitWidth(50);
imageview.setImage(new Image(MusicTable.class.getResource("img").toString()+"/"+item.getFilename()));
box.getChildren().addAll(imageview,vbox);
//SETTING ALL THE GRAPHICS COMPONENT FOR CELL
setGraphic(box);
}
}
};
System.out.println(cell.getIndex());
return cell;
}
});
In case the provided answers did not work for you (like it didn't for me), this was the solution I found (Of course you still needs to create the tableView and add the columns to it):
//Create your column that will hold the image
private final TreeTableColumn<YourObjectClass,ImageView> columnImage= new TreeTableColumn<YourObjectClass,ImageView>("Image");
public void start() {
//Set your cellValueFactory to a SimpleObjectProperty
//Provided that your class has a method "getImage()" this will work beautifully!
columnImage.setCellValueFactory(c-> new SimpleObjectProperty<ImageView>(new ImageView(c.getValue().getValue().getImage())));
}