JavaFX How to change ProgressBar color dynamically? - dynamic

I was trying to solve my problem with colored progress bars in this thread. The solution was present, but then I ran into another problem: I can't change color dynamically from my code. I want to do it right from my code, not with pre-defined .css. Generally I can do it, but I run into some difficulties when I try to do it with more than one progess bar.
public class JavaFXApplication36 extends Application {
#Override
public void start(Stage primaryStage) {
AnchorPane root = new AnchorPane();
ProgressBar pbRed = new ProgressBar(0.4);
ProgressBar pbGreen = new ProgressBar(0.6);
pbRed.setLayoutY(10);
pbGreen.setLayoutY(30);
pbRed.setStyle("-fx-accent: red;"); // line (1)
pbGreen.setStyle("-fx-accent: green;"); // line (2)
root.getChildren().addAll(pbRed, pbGreen);
Scene scene = new Scene(root, 150, 50);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
}
I always get 2 red progressbars with it! It seems that code in line (1) changes the style of ProgressBar class, not the instance.
Another strange moment is that deleting line (1) don't result in 2 green progress bars. So I can figure that line (2) is completely useless!! WHY?! That's definitely getting odd.
Is there any way to set different colors for separate progressbars?

See also the StackOverflow JavaFX ProgressBar Community Wiki.
There is a workaround you can use until a bug to fix the sample code in your question is filed and fixed.
The code in this answer does a node lookup on the ProgressBar contents, then dynamically modifies the bar colour of the progress bar to any value you like.
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ProgressBarDynamicColor extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
PickedColorBar aquaBar = new PickedColorBar(0.4, Color.AQUA);
PickedColorBar fireBar = new PickedColorBar(0.6, Color.FIREBRICK);
HBox layout = new HBox(20);
layout.getChildren().setAll(aquaBar, fireBar);
layout.setStyle("-fx-background-color: -fx-box-border, cornsilk; -fx-padding: 15;");
stage.setScene(new Scene(layout));
stage.show();
aquaBar.wasShown();
fireBar.wasShown();
}
class PickedColorBar extends VBox {
private final ProgressBar bar;
private final ColorPicker picker;
private boolean wasShownCalled = false;
final ChangeListener<Color> COLOR_LISTENER = new ChangeListener<Color>() {
#Override public void changed(ObservableValue<? extends Color> value, Color oldColor, Color newColor) {
setBarColor(bar, newColor);
}
};
public PickedColorBar(double progress, Color initColor) {
bar = new ProgressBar(progress);
picker = new ColorPicker(initColor);
setSpacing(10);
setAlignment(Pos.CENTER);
getChildren().setAll(bar, picker);
}
// invoke only after the progress bar has been shown on a stage.
public void wasShown() {
if (!wasShownCalled) {
wasShownCalled = true;
setBarColor(bar, picker.getValue());
picker.valueProperty().addListener(COLOR_LISTENER);
}
}
private void setBarColor(ProgressBar bar, Color newColor) {
bar.lookup(".bar").setStyle("-fx-background-color: -fx-box-border, " + createGradientAttributeValue(newColor));
}
private String createGradientAttributeValue(Color newColor) {
String hsbAttribute = createHsbAttributeValue(newColor);
return "linear-gradient(to bottom, derive(" + hsbAttribute+ ",30%) 5%, derive(" + hsbAttribute + ",-17%))";
}
private String createHsbAttributeValue(Color newColor) {
return
"hsb(" +
(int) newColor.getHue() + "," +
(int) (newColor.getSaturation() * 100) + "%," +
(int) (newColor.getBrightness() * 100) + "%)";
}
}
}
The code uses inlined string processing of css attributes to manipulate Region backgrounds. Future JavaFX versions (e.g. JDK8+) will include a public Java API to manipulate background attributes, making obsolete the string processing of attributes from the Java program.
Sample program output:

Related

Styling background of TornadoFX ListView

Is it possible to style a ListView component so that none of the elements have a shaded background?
I.e not like this:
But instead have them all styled like the first, third, fifth item etc.
TIA
In the default stylesheet, modena.css the background color for ListCells is governed by the following lines of code:
.list-cell,
.tree-cell {
-fx-background: -fx-control-inner-background;
-fx-background-color: -fx-background;
-fx-text-fill: -fx-text-background-color;
}
/* ... */
.list-cell:odd {
-fx-background: -fx-control-inner-background-alt;
}
So to remove the alternative color for the odd-numbered cells (note that counting is zero-indexed, so the odd-numbered cells are the 2nd, 4th, etc in the list view), you just need to include the following in your external CSS file, to revert the color for the odd-numbered cells to the same as the even-numbered cells:
.list-cell:odd {
-fx-background: -fx-control-inner-background ;
}
If you need to apply this to one specific ListView, you can set an id on that ListView:
ListView myListView ;
// ...
myListView.setId("plain-list-view");
and then the CSS selector becomes
#plain-list-view .list-cell:odd {
-fx-background: -fx-control-inner-background ;
}
You need to create a custom ListCell with a non-null Background. Once you do this, the automatic colour handling of the ListView for selected rows won't work properly any more. So you'll have to handle that yourself. The selection action will invert the colour of the text to white, but that's all. So you need to set the background colour of the cell based on the "Selected" property of the ListCell. In Java it would look like this:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new ListBackgroundWhite(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class ListBackgroundWhite extends ListView<String> {
public ListBackgroundWhite() {
super();
setCellFactory(listView -> new WhiteListCell());
setItems(FXCollections.observableArrayList("first line", "second line", "third line", "fourth line"));
}
static class WhiteListCell extends ListCell<String> {
WhiteListCell() {
Background unselectedBackground =
new Background(new BackgroundFill(Color.WHITESMOKE, CornerRadii.EMPTY, Insets.EMPTY));
Background selectedBackground = new Background(new BackgroundFill(Color.BROWN, CornerRadii.EMPTY,
Insets.EMPTY));
backgroundProperty().bind(Bindings.createObjectBinding(() -> isSelected() ? selectedBackground :
unselectedBackground, selectedProperty()));
}
#Override
public void updateItem(String item, boolean isEmpty) {
super.updateItem(item, isEmpty);
if (!isEmpty) {
setText(item);
} else {
setText(null);
}
}
}
}
Once you do this, the cell background is no longer transparent, and the stripe pattern of the ListView itself won't show through.
EDIT:
As pointed out, this IS heavy-handed, except that in most cases a ListView isn't going to be a simple Label with a string in it. It's going to have some sort of layout in the ListCell that is going to require that you create a custom ListCell in any event.
However, messing with the Background directly via a binding to the "Selected" property is clumsy. You can create a new StyleClass, and then just define the modified PseudoClasses in the css. Then add this new StyleClass to the custom ListCell, and then it will handle it automatically as it applies the "EVEN" and "ODD" pseudoclasses.
One thing I found was that since the new "odd" definition gets applied after all of the other definitions in the default Modena css, that the "SELECTED" pseudoclass for ODD lines is suppressed. This means that the odd and even lines look different when they are selected, so a duplicate "SELECTED" definition needed to be added to the new css after the "ODD" definition. Then everything works properly. So the new code looks like this:
The CSS:
.custom-list-cell:odd {
-fx-background: -fx-control-inner-background;
}
.custom-list-cell:selected {
-fx-background: -fx-selection-bar;
-fx-table-cell-border-color: derive(-fx-selection-bar, 20%);
}
The main, now loads the new stylesheet into the Scene:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new ListBackgroundWhite(), 300, 200);
scene.getStylesheets().add(getClass().getResource("/css/samples.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
}
And then the only customization to the ListCell is to add the new StyleClass:
public class ListBackgroundWhite extends ListView<String> {
public ListBackgroundWhite() {
super();
setCellFactory(listView -> new WhiteListCell());
setItems(FXCollections.observableArrayList("first line", "second line", "third line", "fourth line"));
}
static class WhiteListCell extends ListCell<String> {
WhiteListCell() {
getStyleClass().add("custom-list-cell");
}
#Override
public void updateItem(String item, boolean isEmpty) {
super.updateItem(item, isEmpty);
if (!isEmpty) {
setText(item);
} else {
setText(null);
}
}
}
}
But if you really are just wanting to have a ListView with just simple labels in it, you can just have the cell factory add the StyleClass on to the standard TextFieldListCell:
public ListBackgroundWhite() {
super();
setCellFactory(listView -> {
ListCell<String> cell = new TextFieldListCell<>();
cell.getStyleClass().add("custom-list-cell");
return cell;
});
setItems(FXCollections.observableArrayList("first line", "second line", "third line", "fourth line"));
}

Java OOP using Translate Transition and JavaFX

The Translate Transition does not output. It uses a method in the main class. I believe it is not working because it is used as an object. There has to be a different code to implement. It uses a method in a main and then puts it into a tester. However, I do not know how to use it because it uses a constructor/object as well. Then, the object turns and changes into a node which I changed it. I do not know how the Translate Transition method is attached to the object and displays it into the javafx console. Please help solve the problem for positive feedback as it shows.
import javafx.animation.TranslateTransition;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.util.Duration;
public class AnxiousShapes {
private Shape shape;
private double delay ;
private int howFarToMoveX;
private int howFarToMoveY;
public AnxiousShapes(int type, int x, int y, int size, double delay, Color color, int hftmX, int hftmY) {
if (type == 0) {shape = new Circle(x, y, size, color);}
//these are the only lines you can change
//in this main class
//else if (type == 1){shape = new Rectangle(x,y,color);}
//else if (type == 2){shape = new Polygon();}
//else if (type == 3) {shape = new Circle(x, y, size, color);}
//else { System.out.println("Error in type");shape = new
//Circle(???????);}
this.delay = delay;
this.howFarToMoveX = hftmX;
this.howFarToMoveY = hftmY;
}
// getter and setters
public TranslateTransition calculateTt() {
TranslateTransition tt = new TranslateTransition(Duration.seconds(this.delay), this.shape);
tt.setToX(this.shape.getLayoutX() + howFarToMoveX);
tt.setToY(shape.getLayoutY() + howFarToMoveY);
// Let the animation run forever -- if the shape
// tries to move "off-screen" it will return to the beginning
tt.setCycleCount(TranslateTransition.INDEFINITE);
return tt;
}
#Override
public String toString() {
return "AnxiousShape [shape=" + shape + ", delay=" + delay + ", howFarToMoveX=" + howFarToMoveX
+ ", howFarToMoveY=" + howFarToMoveY + "]";
}
}
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.scene.Group;
import javafx.scene.Node;
import java.util.Random;
public class AnxiousShapesTester extends Application {
#Override
public void start(Stage stage) {
// adding the new things
Group root = new Group();
stage.setTitle("Welcome to JavaFX!");
// create the shape circle
AnxiousShapes circle1 = new AnxiousShapes(0, 200, 200, 50, 15,
Color.GREEN, 10 ,35);
root.getChildren().add(circle1.getShape());
// this does not work
// TranslateTransition trans = circle1.calculateTt();
// trans.setNode(root);
// trans.play();
// and I tried this and I already have the movement in constructor for
// delay and x and y but TranslateTransition
// asks for duration.millis(500)
TranslateTransition tt = new
TranslateTransition(Duration.millis(500), root);
tt.play();
Scene scene = new Scene(root, 600, 600, Color.WHITE);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
In a comment, you say, "this does not work." The problem is trans.setNode(root), which attempts to make root "The target node of this TranslateTransition." Your implementation of calculateTt() already specifies this.shape as the target node. Instead, add a suitable accessor to AnxiousShapes and use the transition as constructed; the following changes are illustrated below:
public Shape getShape() { return this.shape; }
…
AnxiousShapes circle1 = new AnxiousShapes(0, 100, 100, 100, 3, Color.GREEN, 400, 400);
root.getChildren().add(circle1.getShape());
TranslateTransition trans = circle1.calculateTt();
trans.play();

Eclipse plugin - ColumnLabelProvider display only image

So I am developing an Eclipse plug-in and using ColumnLabelProvider to provide label for the columns of my tree viewer.
However, in one of the columns, I only intend to display an image and no text. However, in the final display, Eclipse reserves blank space for the text element even if I return a null.
Is there any way to make it display only image and in the full space provided?
Here is the code snippet:
column4.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
return null;
}
#Override
public Image getImage(Object element) {
/* Code to Display an image follows */
.....
}
});
ColumnLabelProvider will always leave space for the text.
You can use a class derived from OwnerDrawLabelProvider to draw the column yourself.
Something like:
public abstract class CentredImageCellLabelProvider extends OwnerDrawLabelProvider
{
protected CentredImageCellLabelProvider()
{
}
#Override
protected void measure(Event event, Object element)
{
}
#Override
protected void erase(final Event event, final Object element)
{
// Don't call super.erase() to suppress non-standard selection draw
}
#Override
protected void paint(final Event event, final Object element)
{
TableItem item = (TableItem)event.item;
Rectangle itemBounds = item.getBounds(event.index);
GC gc = event.gc;
Image image = getImage(element);
Rectangle imageBounds = image.getBounds();
int x = event.x + Math.max(0, (itemBounds.width - imageBounds.width) / 2);
int y = event.y + Math.max(0, (itemBounds.height - imageBounds.height) / 2);
gc.drawImage(image, x, y);
}
protected abstract Image getImage(Object element);
}

JavaFX2 : can not mix some "setStyle" on Pane

DESCRIPTION
On windows7 and with JDK1.8.0_20, I simply try to display some panes with a black border and with a given background color. I am using "setStyle" method for that, with the following documentation http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html#region. The problem code is within the TestPane class.
See below the full running code :
package pane;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class BorderPaneApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("BorderPane");
MainPane mainPane = new MainPane();
Scene scene = new Scene( mainPane, 400, 300, Color.ORANGE);
// do it for the layout
mainPane.prefHeightProperty().bind(scene.heightProperty());
mainPane.prefWidthProperty().bind(scene.widthProperty());
primaryStage.setScene(scene);
primaryStage.show();
}
public class MainPane extends BorderPane{
public TestPane topPane = new TestPane("top", Color.LIGHTSKYBLUE);
public TestPane leftPane = new TestPane("left", Color.AQUA);
public TestPane bottomPane = new TestPane("bottom", Color.AZURE);
public TestPane centerPane = new TestPane("center", Color.LIGHTBLUE);
public MainPane(){
this.setTop(this.topPane);
this.setLeft(this.leftPane);
this.setCenter(this.centerPane);
this.setBottom(this.bottomPane);
}
}
public class TestPane extends BorderPane {
public TestPane(String name, Color color){
// first style part - start
this.setStyle("-fx-border-color: #FFFFFF;");
this.setStyle("-fx-border-width: 1px;");
this.setStyle("-fx-border-style: solid;");
// first style part - end
// second style part - start
this.setStyle("-fx-background-color: " + color.toString().replace("0x", "#") + ";");
// second style part - end
this.setCenter(new Text(name));
}
}
}
After some tries, I can not mix this code :
// first style part - start
this.setStyle("-fx-border-color: #FFFFFF;");
this.setStyle("-fx-border-width: 1px;");
this.setStyle("-fx-border-style: solid;");
// first style part - end
with this one :
// second style part - start
this.setStyle("-fx-background-color: " + color.toString().replace("0x", "#") + ";");
// second style part - end
The last one seems to take over the first and do not display it. The first picture shows the border line sets before the background and the second shows the background sets before the border line.
QUESTION
How to display both style together?
Cheers,
Jakez
setStyle() is a setter method, thus it does not append..
You want to combine all styles into one String:
setStyle("-fx-border-color: #FFFFFF;-fx-border-width: 1px;-fx-border-style: solid;-fx-background-color: " + color.toString().replace("0x", "#") + ";");

Android 3.1, trouble displaying two counters side by side

Im new to programming and I am having a hard time trying to figure this one out. I'm trying to create two visible separate counters on each side of the tablet. One is the the left of the tablet, the other on the right of the tablet. When i click the left button it updates the count on the left(e.g., 1+1+1 etc) but when I click on the right counter, it adds an additional value to sum up on the left counter. (e.g., click on right (adds 1, then when i click on the left counter it acts such as add 2, instead of 1.)
here is what my code looks like so far
import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.widget.Button;
import android.widget.EditText;
import android.view.View;
import android.view.View.OnClickListener;
public class swim2 extends Activity {
// References to UI views
EditText txtCount;
EditText txtCount2;
Button PUp;
Button NUp;
static int Count = 0; // Initial count
static int Count2 = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setContentView(R.layout.main3);
// TODO Auto-generated method stub
Button previous = (Button) findViewById(R.id.button4);
previous.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent myIntent = new Intent(view.getContext(), swim1.class);
startActivityForResult(myIntent, 0);
}
});
// Retrieve references to UI views by their id in XML layout
NUp = (Button)findViewById(R.id.incremintationbutton2);
txtCount2 = (EditText)findViewById(R.id.ni);
txtCount2.setText(String.valueOf(Count2)); //Set initial value
NUp = (Button)findViewById(R.id.incremintationbutton2);
// Process the button on-click event
NUp.setOnClickListener(new OnClickListener() {
public void onClick(View Button) {
Count++;
txtCount2.setText(String.valueOf(Count2));
}
});
PUp = (Button)findViewById(R.id.incremintationbutton1);
txtCount = (EditText)findViewById(R.id.pi);
txtCount.setText(String.valueOf(Count)); // Set initial value
PUp = (Button)findViewById(R.id.incremintationbutton1);
PUp.setOnClickListener(new OnClickListener() {
public void onClick(View Button) {
Count++;
txtCount.setText(String.valueOf(Count));
}
});
}
}
Fixed it, had to change one of the count++, to count2++.. took me a while but i figured it out after a cup of coffee.