I have a TableView in my code for which I am saving the index and width of each TableColumn when the application shuts down (values are saved to a Properties file). When I start the application again, I want to reset the index and width of each TableColumns back to what is was when the application was shut down.
// If all columns are known, reorder the columns and set the width
if (allKnown) {
ObservableList<TableColumn<T, ?>> columns = FXCollections.observableArrayList();
// Column order and width
TableColumn<T, ?> column;
for (int i = 0; i < colIndex.length; i++) {
column = getOriginalColumns().get(colIndex[i]);
column.setPrefWidth(colWidths[i]);
columns.add(column);
}
getTableView().getColumns().setAll(columns);
}
getTableView().setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
Setting the index works correctly, the column is set in the correct location of the TableView. However, this does not work for the column's prefWidth (all columns are resizable). I have looked at different fora and the culprit seems to be the CONSTRAINED_RESIZE_POLICY. I want to keep that option, since the main screen of my application can be resized. If I use unconstrained, the width works, but then I loose the ability to auto-scale the TableView when the application's main window resizes.
Has anyone experienced the same and found a solution? Your help is greatly appreciated.
The only solution I have found is to initially set the minWidth instead of the prefWidth. then a few pulses after showing the UI, fix the minWidth so column resizing works normally again.
Related
I have a messaging application running in Android which has the setup like in setup of the screen
the order is as below
<View>
<BorderPane>
<center>
<ScrollPane>
<content>
<VBox> //issue is here
</content>
<ScrollPane>
<center>
<bottom>
<TextField>
<bottom>
</BorderPane>
</View>
When I add children to VBox with
VBox.getChildren().add(TextLabel);
The ScrollPane gets new VBox and shows that on the screen.
However when i add more children that what current screen can fit i scroll to end of the ScrollPane by setting vvalueProperty();
ScrollPane.vvalueProperty().bind(VBox.heightProperty());
(Above code is essential to recreate the issue)
This works perfectly fine when running it on computer but on mobile i have this weird issue where scrollPane drops VBox when i add more children than what can be fit on the screen. And when i click on the VBox area the screen refreshes and i get the desired content on the screen
Video demonstrating ScrollBar issue in gluon
For convenience i have set following color code
ScrollBar - Red
VBox - Blue
As an alternative to binding I also tried
ScrollBar.setVvalue(1.0);
setVvalue() did not have same issue but this on the other hand was not showing the last message in the view.
Right now i have tried all possible combinations including replacing VBox with FlowPane and observed same behavior.
I can reproduce your issue on an Android device. Somehow, as discussed in the comments above, the binding of the vertical scroll position is causing some race condition.
Instead of trying to find out the cause of that issue, I'd rather remove the binding and propose a different approach to get the desired result: the binding is a very strong condition in this case.
When you try to do in the same pass this:
vBox.getChildren().add(label);
scrollPane.setVvalue(vBox.getHeight());
you already noticed and mentioned that the scroll position wasn't properly updated and you were missing the last item added to the vBox.
This can be explained easily: when you add a new item to the box, there is a call to layoutChildren() that will take some time to be performed. At least it will take another pulse to get the correct value.
But since you try to set immediately the vertical scroll position, the value vBox.getHeight() will still return the old value. In other words, you have to wait a little bit to get the new value.
There are several ways to do it. The most straightforward is with a listener to the box's height property:
vBox.heightProperty().addListener((obs, ov, nv) ->
scrollPane.setVvalue(nv.doubleValue()));
As an alternative, after adding the item to the box, you could use:
vBox.getChildren().add(label);
Platform.runLater(() -> scrollPane.setVvalue(vBox.getHeight()));
But this doesn't guarantee that the call won't be done immediately. So it is better to do a PauseTransition instead, where you can control the timing:
vBox.getChildren().add(label);
PauseTransition pause = new PauseTransition(Duration.millis(30));
pause.setOnFinished(f -> scrollPane.setVvalue(vBox.getHeight()));
pause.play();
As a suggestion, you could also do a nice transition to slide in the new item.
Alternative solution
So far, you are using an ScrollPane combined with a VBox to add a number of items, allowing scrolling to the first item on the list but keeping the scroll position always at the bottom so the last item added is fully visible. While this works fine (with my proposal above to avoid the binding), you are adding many nodes to a non virtualized container.
I think there is a better alternative, with a ListView (or better a CharmListView that will allow headers). With the proper CellFactory you can have exactly the same visuals, and you can directly scroll to the last item. But the main advantage of this control is its virtualFlow, that will manage for you a limited number of nodes while you have many more items added to a list.
This is just a short code snippet to use a ListView control for your chat application:
ListView<String> listView = new ListView<>();
listView.setCellFactory(p -> new ListCell<String>() {
private final Label label;
{
label = new Label(null, MaterialDesignIcon.CHAT_BUBBLE.graphic());
label.setMaxWidth(Double.MAX_VALUE);
label.setPrefWidth(this.getWidth() - 60);
label.setPrefHeight(30);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null && ! empty) {
label.setText(item);
label.setAlignment(getIndex() % 2 == 0 ? Pos.CENTER_LEFT : Pos.CENTER_RIGHT);
setGraphic(label);
} else {
setGraphic(null);
}
}
});
setCenter(listView);
and to add a new item and scroll to it, you just need:
listView.getItems().add("Text " + (listView.getItems().size() + 1));
listView.scrollTo(listView.getItems().size() - 1);
Of course, with the proper styling you can remove the lines between rows, and create the same visuals as with the scrollPane.
Stacked Widgets don't have the possibility to update the size to the minimum size of the contents current shown.
It always shows the biggest size of the biggest layer and stays there as long as the QSizePolicy stays the same.
An aproach i found long time ago was to use a function like this:
void SmartUIWrapper::updateStackedWidgetSize( QStackedWidget* stacked, int index )
{
// Set to size policy ignored for the rest of pages. Set expanding to the actual one
for (int i = 0; i < stacked->count (); ++i)
{
// determine the vertical size policy
QSizePolicy::Policy policy = QSizePolicy::Ignored;
if (i == index)
policy = QSizePolicy::Expanding;
// update the size policy
stacked->widget (i)->setSizePolicy(QSizePolicy::Fixed, policy);
}
}
It sets all elements to Ignored, and then it sets the vertical policy of the selected widget which is Expanding (or whatever you want).
Later on i found out that reloading the styletheet updates the current sizes and makes the widget to resize to the minimum. But this approach is way too slow and unnecessary.
Do you know any other way to update the size to shrink to the smallest size in the current layer shown?
I'm looking for some method to change grid column's width (or row's height) by animtion defined in Storyboard. I have already found some solutions for WPF apps, but they are all useless in case of Windows Store programming, eg.:
Grid Column changing Width when animating
how to change the height of a grid row in wpf using storyboard
http://www.codeproject.com/Articles/18379/WPF-Tutorial-Part-2-Writing-a-custom-animation-cla
Is such result obtainable by creating a custom class, inheriting from Timeline? If so, which components should be overrode for proper implementation?
You should be able to use a simple DoubleAnimation. Make sure to set EnableDependentAnimation=True, as outline here.
One thing to realize when trying things out is that ColumnDefinitions are a GridLength struct. You can find more information on them here. You will need to have the animation set the Value property.
Here is a sample method to animate a Grid ColumnDefinition MaxWidth.
private void Animate(ColumnDefinition column)
{
Storyboard storyboard = new Storyboard();
Duration duration = new Duration(TimeSpan.FromMilliseconds(500));
CubicEase ease = new CubicEase { EasingMode = EasingMode.EaseOut };
DoubleAnimation animation = new DoubleAnimation();
animation.EasingFunction = ease;
animation.Duration = duration;
storyboard.Children.Add(animation);
animation.From = 1000;
animation.To = 0;
animation.EnableDependentAnimation = true;
Storyboard.SetTarget(animation, column);
Storyboard.SetTargetProperty(animation, "(ColumnDefinition.MaxWidth)");
storyboard.Begin();
}
I got a problem with the widths of the advanceddatagrid-columns.
First of all, my layout is a HDividedBox, on the left there is the navigation, on the right there is a module, containing the advanceddatagrid.
left Side: navigation
right side: module (e.g. advanceddatagrid)
Most of the columns got a fixed width, some a minWidth. Now, initially the widths of the columns are correct.
So the problem is, whenever I load a new module and later reload the advanceddatagrid, the initial width of the columns is way different, although I change nothing in the process of loading the module. Neither the fixed widths nor the minWidths are initially correct.
I recently saw there is a solution for wrong widths of colums, it looks like that:
var oldPolicy:String = advanceddatagrid.myScrollPolicy;
advanceddatagrid.myScrollPolicy = ScrollPolicy.ON;
for(var i:int = 0; i < advanceddatagrid.columns.length; i++) {
var column:AdvancedDataGridColumn = advanceddatagrid.columns[i] as AdvancedDataGridColumn;
advanceddatagrid.column.width = column.width;
}
advanceddatagrid.validateNow();
advanceddatagrid.myScrollPolicy = oldPolicy;
advanceddatagrid.validateNow();
On the whole this is just a temporary change of the ScrollPolicy, re-setting the column-widths and then changing back. But still, it doenst work.
Does anyone have a clue?
Some relevant references that might help (the first one worked for me):
http://userflex.wordpress.com/2011/04/14/force-resize-datagrid/
http://forums.adobe.com/message/4285461
To summarize the first post (credit goes to Nick Schneble):
public function resizeColumns () : void
{
grid.validateNow ();
// forces the columns to size themselves properly
for each (var column : AdvancedDataGridColumn in grid.columns)
{
column.width = column.width;
}
}
It may seem a bit ridiculous, but if you execute this method whenever the underlying data in your data grid changes, you’ll have beautifully laid out columns.
In my flex app I store the widths and visiblility of columns in an xml file. When the app loads it reads from the xml file and sets he columns values as applicable:
for(i = 0; i < columnsOrder.length; i++){
newOrder[i] = myDG.columns[Number(columnsOrder[i]) - 1];
newOrder[i].visible = (Number(columnsVisiblity[i]) == 1);
newOrder[i].width = Number(columnsWidth[i]);
}
myDG.columns = newOrder;
myDG.invalidateList();
The problem appears to be setting the visibility (it sets the visible field correctly but messes up the width)... I've tried setting it after setting the width (outside of the loop) and before the loop as well. It resizes the columns properly if I don't do anything with the visibility.
Any ideas?
Add an import statement at the top of your class file:
import mx.core.mx_internal;
Then remove using the mx_internal namespace, remove the owner of the column, change the width and then reasign the parent:
public static function resizeColumn(col:DataGridColumn, size:int):void
{
var owner:* = col.mx_internal::owner
col.mx_internal::owner = null;
col.width = size;
col.mx_internal::owner = owner;
}
This ought to do the trick (well, it did for us after a couple of days of swearing)
Is you horizontalScrollPolicy set to false on the datagrid?
"If the DataGrid's horizontalScrollPolicy property is false, all visible columns must fit in the displayable area, and the DataGrid will not always honor the width of the columns if the total width of the columns is too small or too large for the displayable area."
http://livedocs.adobe.com/flex/3/langref/mx/controls/dataGridClasses/DataGridColumn.html#width
I was able to get it to work by calling the above loop in a function twice... the first time it add the visible columns, the second time it sets the correct width. Not the best solution but I cannot spend any more time on it.