Base Article:
[https://community.oracle.com/thread/2552039?start=0&tstart=0][1]
I extend the solution from the oracle community with the event filters in the constructor for a better user ergonomics.
The JavaFX TimeTextField provides all functions needed for time handling.
Unfortunately it's missing in the JavaFX Standard in Java8, where a time handling field is a must in my point of view as in other technologies it is possible.
The thread in the oracle community is not as frequent as I expect it, please test it and try to improve it! We need it...
Features:
- only numeric keys are accepted.
- validation for a given time format
- (my improvement) when the last number of a block like hours or minutes was reached, the cursor jumps to the next block.
package test;
import java.util.regex.Pattern;
import javafx.beans.binding.Bindings;
import javafx.application.Application;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.IndexRange;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TimeTextFieldTest extends Application {
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setPadding(new javafx.geometry.Insets(5));
Label hrLabel = new Label();
Label minLabel = new Label();
Label secLabel = new Label();
TimeTextField timeTextField = new TimeTextField();
hrLabel.textProperty().bind(Bindings.format("Hours: %d", timeTextField.hoursProperty()));
minLabel.textProperty().bind(Bindings.format("Minutes: %d", timeTextField.minutesProperty()));
secLabel.textProperty().bind(Bindings.format("Seconds: %d", timeTextField.secondsProperty()));
root.getChildren().addAll(timeTextField, hrLabel, minLabel, secLabel);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public static class TimeTextField extends TextField {
enum Unit {
HOURS, MINUTES, SECONDS
};
private final Pattern timePattern;
private final ReadOnlyIntegerWrapper hours;
private final ReadOnlyIntegerWrapper minutes;
private final ReadOnlyIntegerWrapper seconds;
public TimeTextField() {
this("00:00:00");
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent inputevent) {
int c = TimeTextField.this.getCaretPosition();
if (c <= 7) {
if (!"1234567890:".contains(inputevent.getCharacter().toLowerCase())) {
inputevent.consume();
}
} else {
inputevent.consume();
}
}
});
this.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent inputevent) {
boolean withMinutes = false;
if (TimeTextField.this.getText() != null && TimeTextField.this.getText().length() >= 5
&& TimeTextField.this.getText().indexOf(":") == 2) {
withMinutes = true;
}
boolean withSeconds = false;
if (TimeTextField.this.getText() != null && TimeTextField.this.getText().length() == 8
&& TimeTextField.this.getText().lastIndexOf(":") == 5) {
withSeconds = true;
}
int c = TimeTextField.this.getCaretPosition();
if (((c == 2 && withMinutes) || (c == 5 && withSeconds))
&& (inputevent.getCode() != KeyCode.LEFT && inputevent.getCode() != KeyCode.BACK_SPACE)) {
TimeTextField.this.forward();
inputevent.consume();
}
}
});
}
public TimeTextField(String time) {
super(time);
// timePattern = Pattern.compile("\\d\\d:\\d\\d:\\d\\d");
timePattern = Pattern.compile("([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]");
if (!validate(time)) {
throw new IllegalArgumentException("Invalid time: " + time);
}
hours = new ReadOnlyIntegerWrapper(this, "hours");
minutes = new ReadOnlyIntegerWrapper(this, "minutes");
seconds = new ReadOnlyIntegerWrapper(this, "seconds");
hours.bind(new TimeTextField.TimeUnitBinding(Unit.HOURS));
minutes.bind(new TimeTextField.TimeUnitBinding(Unit.MINUTES));
seconds.bind(new TimeTextField.TimeUnitBinding(Unit.SECONDS));
}
public ReadOnlyIntegerProperty hoursProperty() {
return hours.getReadOnlyProperty();
}
public int getHours() {
return hours.get();
}
public ReadOnlyIntegerProperty minutesProperty() {
return minutes.getReadOnlyProperty();
}
public int getMinutes() {
return minutes.get();
}
public ReadOnlyIntegerProperty secondsProperty() {
return seconds.getReadOnlyProperty();
}
public int getSeconds() {
return seconds.get();
}
#Override
public void appendText(String text) {
// Ignore this. Our text is always 8 characters long, we cannot
// append anything
}
#Override
public boolean deleteNextChar() {
boolean success = false;
// If there's a selection, delete it:
final IndexRange selection = getSelection();
if (selection.getLength() > 0) {
int selectionEnd = selection.getEnd();
this.deleteText(selection);
this.positionCaret(selectionEnd);
success = true;
} else {
// If the caret preceeds a digit, replace that digit with a zero
// and move the caret forward. Else just move the caret forward.
int caret = this.getCaretPosition();
if (caret % 3 != 2) { // not preceeding a colon
String currentText = this.getText();
setText(currentText.substring(0, caret) + "0" + currentText.substring(caret + 1));
success = true;
}
this.positionCaret(Math.min(caret + 1, this.getText().length()));
}
return success;
}
#Override
public boolean deletePreviousChar() {
boolean success = false;
// If there's a selection, delete it:
final IndexRange selection = getSelection();
if (selection.getLength() > 0) {
int selectionStart = selection.getStart();
this.deleteText(selection);
this.positionCaret(selectionStart);
success = true;
} else {
// If the caret is after a digit, replace that digit with a zero
// and move the caret backward. Else just move the caret back.
int caret = this.getCaretPosition();
if (caret % 3 != 0) { // not following a colon
String currentText = this.getText();
setText(currentText.substring(0, caret - 1) + "0" + currentText.substring(caret));
success = true;
}
this.positionCaret(Math.max(caret - 1, 0));
}
return success;
}
#Override
public void deleteText(IndexRange range) {
this.deleteText(range.getStart(), range.getEnd());
}
#Override
public void deleteText(int begin, int end) {
// Replace all digits in the given range with zero:
StringBuilder builder = new StringBuilder(this.getText());
for (int c = begin; c < end; c++) {
if (c % 3 != 2) { // Not at a colon:
builder.replace(c, c + 1, "0");
}
}
this.setText(builder.toString());
}
#Override
public void insertText(int index, String text) {
// Handle an insert by replacing the range from index to
// index+text.length() with text, if that results in a valid string:
StringBuilder builder = new StringBuilder(this.getText());
builder.replace(index, index + text.length(), text);
final String testText = builder.toString();
if (validate(testText)) {
this.setText(testText);
}
this.positionCaret(index + text.length());
}
#Override
public void replaceSelection(String replacement) {
final IndexRange selection = this.getSelection();
if (selection.getLength() == 0) {
this.insertText(selection.getStart(), replacement);
} else {
this.replaceText(selection.getStart(), selection.getEnd(), replacement);
}
}
#Override
public void replaceText(IndexRange range, String text) {
this.replaceText(range.getStart(), range.getEnd(), text);
}
#Override
public void replaceText(int begin, int end, String text) {
if (begin == end) {
this.insertText(begin, text);
} else {
// only handle this if text.length() is equal to the number of
// characters being replaced, and if the replacement results in
// a valid string:
if (text.length() == end - begin) {
StringBuilder builder = new StringBuilder(this.getText());
builder.replace(begin, end, text);
String testText = builder.toString();
if (validate(testText)) {
this.setText(testText);
}
this.positionCaret(end);
}
}
}
private boolean validate(String time) {
if (!timePattern.matcher(time).matches()) {
return false;
}
String[] tokens = time.split(":");
assert tokens.length == 3;
try {
int hours = Integer.parseInt(tokens[0]);
int mins = Integer.parseInt(tokens[1]);
int secs = Integer.parseInt(tokens[2]);
if (hours < 0 || hours > 23) {
return false;
}
if (mins < 0 || mins > 59) {
return false;
}
if (secs < 0 || secs > 59) {
return false;
}
return true;
} catch (NumberFormatException nfe) {
// regex matching should assure we never reach this catch block
assert false;
return false;
}
}
private final class TimeUnitBinding extends IntegerBinding {
final Unit unit;
TimeUnitBinding(Unit unit) {
this.bind(textProperty());
this.unit = unit;
}
#Override
protected int computeValue() {
// Crazy enum magic
String token = getText().split(":")[unit.ordinal()];
return Integer.parseInt(token);
}
}
}
}
Related
I am using the below thread for setting the time in multiples of 5 in the time picker. Using the custom renderers I am able to select the time in multiples of 5. But after selecting a time, when the clock-Pop up closed, a different time is showing on the UI. The issue is only in the android platform, for the ios everything is working as excepted.
My code:
public class CustomTimePickerRenderer : TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.TimePicker> e)
{
base.OnElementChanged(e);
TimePickerDialogIntervals timePickerDlg = new TimePickerDialogIntervals(this.Context, new EventHandler<TimePickerDialogIntervals.TimeSetEventArgs>(UpdateDuration),
Element.Time.Hours, Element.Time.Minutes, true);
var control = new EditText(this.Context);
control.Focusable = false;
control.FocusableInTouchMode = false;
control.Clickable = false;
control.Click += (sender, ea) => timePickerDlg.Show();
control.Text = Element.Time.Hours.ToString("00") + ":" + Element.Time.Minutes.ToString("00");
SetNativeControl(control);
}
void UpdateDuration(object sender, Android.App.TimePickerDialog.TimeSetEventArgs e)
{
Element.Time = new TimeSpan(e.HourOfDay, e.Minute, 0);
Control.Text = Element.Time.Hours.ToString("00") + ":" + Element.Time.Minutes.ToString("00");
}
}
public class TimePickerDialogIntervals : TimePickerDialog
{
public const int TimePickerInterval = 05;
private bool _ignoreEvent = false;
public TimePickerDialogIntervals(Context context, EventHandler<TimePickerDialog.TimeSetEventArgs> callBack, int hourOfDay, int minute, bool is24HourView)
: base(context, (sender, e) =>
{
callBack(sender, new TimePickerDialog.TimeSetEventArgs(e.HourOfDay, e.Minute * TimePickerInterval));
}, hourOfDay, minute / TimePickerInterval, is24HourView)
{
}
public override void OnTimeChanged(Android.Widget.TimePicker view, int hourOfDay, int minute)
{
base.OnTimeChanged(view, hourOfDay, minute);
if (_ignoreEvent) return;
if (minute % TimePickerInterval != 0)
{
int minuteFloor = minute - (minute % TimePickerInterval);
minute = minuteFloor + (minute == minuteFloor + 1 ? TimePickerInterval : 0);
if (minute == 60)
minute = 0;
_ignoreEvent = true;
view.CurrentMinute = (Java.Lang.Integer)minute;
_ignoreEvent = false;
}
}
}
Why a different time is showing on the UI when the clock pop-up closed?
The problem is because the minute has been multiplied by 'TimePickerInterval' (05). Changing the parameter to 'e.Minute' will work as expected.
Code:
public class TimePickerDialogIntervals : TimePickerDialog
{
public const int TimePickerInterval = 15;
private bool _ignoreEvent = false;
public TimePickerDialogIntervals(Context context, EventHandler<TimePickerDialog.TimeSetEventArgs> callBack, int hourOfDay, int minute, bool is24HourView) :
base(context, (sender, e) =>
{
callBack(sender, new TimePickerDialog.TimeSetEventArgs(e.HourOfDay, e.Minute));//remove '* TimePickerInterval'
}, hourOfDay, minute / TimePickerInterval, is24HourView)
{
}
...
}
This question already has answers here:
What is the recommended way to make a numeric TextField in JavaFX?
(24 answers)
Restricting a TextField input to hexadecimal values in Java FX
(3 answers)
Closed 3 years ago.
I have a credit card page in my java fx program. I am trying to make it so that the inputs only allow numbers. At the moment it only gives an error if the fields are empty. But no error occurs if text is included?
I have tried changing it from String to integer, but that doesn't work.
public void thankyoupage(ActionEvent actionEvent) throws IOException {
String cardno = cardtf.getText();
String expdate1 = expirytf1.getText();
String expdate2 = expirytf2.getText();
String cvvnum = cvvtf.getText();
if (cardno.equals("") || expdate1.equals("") ||
expdate2.equals("") || cvvnum.equals("")) {
Alert alert = new Alert(Alert.AlertType.WARNING, "Enter Full Details", ButtonType.OK);
alert.showAndWait();
} else{
Window mainWindow = confirmbut.getScene().getWindow();
Parent newRoot = FXMLLoader.load(getClass().getResource("Thankyou.fxml"));
mainWindow.getScene().setRoot(newRoot);
}
}
Any links or changes would be nice.
You should attach a TextFormatter to your TextField. I have attached a sample on using Decimals - since you are using money, this might make the most sense.
On your text field you simply add the TextFormatter - this will prevent entry of anything other than what you allow.
//For Example
moneyTextField.setTextFormatter(new DecimalTextFormatter(0, 2));
--Below is the control code.
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.function.UnaryOperator;
import javafx.scene.control.TextFormatter;
import javafx.util.StringConverter;
public class DecimalTextFormatter extends TextFormatter<Number> {
private static DecimalFormat format = new DecimalFormat("#.0;-#.0");
public DecimalTextFormatter(int minDecimals, int maxDecimals) {
super(getStringConverter(minDecimals, maxDecimals), 0, getUnaryOperator(maxDecimals, true,-1));
}
public DecimalTextFormatter(int minDecimals, int maxDecimals, boolean allowsNegative) {
super(getStringConverter(minDecimals, maxDecimals), 0, getUnaryOperator(maxDecimals, allowsNegative,-1));
}
public DecimalTextFormatter(int minDecimals, int maxDecimals, boolean allowsNegative , int maxNoOfDigitsBeforeDecimal) {
super(getStringConverter(minDecimals, maxDecimals), 0, getUnaryOperator(maxDecimals, allowsNegative, maxNoOfDigitsBeforeDecimal));
}
private static StringConverter<Number> getStringConverter(int minDecimals, int maxDecimals) {
return new StringConverter<Number>() {
#Override
public String toString(Number object) {
if (object == null) {
return "";
}
String format = "0.";
for (int i = 0; i < maxDecimals; i++) {
if (i < minDecimals) {
format = format + "0";
} else {
format = format + "#";
}
}
format = format + ";-" + format;
DecimalFormat df = new DecimalFormat(format);
String formatted = df.format(object);
return formatted;
}
#Override
public Number fromString(String string) {
try {
if (string == null) {
return null;
}
return format.parse(string);
} catch (ParseException e) {
return null;
}
}
};
}
private static UnaryOperator<javafx.scene.control.TextFormatter.Change> getUnaryOperator(int maxDecimals,
boolean allowsNegative, int noOfDigitsBeforeDecimal) {
return new UnaryOperator<TextFormatter.Change>() {
#Override
public TextFormatter.Change apply(TextFormatter.Change change) {
if (!allowsNegative && change.getControlNewText().startsWith("-")) {
return null;
}
if (change.getControlNewText().isEmpty()) {
return change;
}
ParsePosition parsePosition = new ParsePosition(0);
Object object = format.parse(change.getControlNewText(), parsePosition);
if (change.getCaretPosition() == 1) {
if (change.getControlNewText().equals(".")) {
return change;
}
}
if (object == null || parsePosition.getIndex() < change.getControlNewText().length()) {
return null;
} else {
if(noOfDigitsBeforeDecimal != -1)
{
int signum = new BigDecimal(change.getControlNewText()).signum();
int precision = new BigDecimal(change.getControlNewText()).precision();
int scale = new BigDecimal(change.getControlNewText()).scale();
int val = signum == 0 ? 1 : precision - scale;
if (val > noOfDigitsBeforeDecimal) {
return null;
}
}
int decPos = change.getControlNewText().indexOf(".");
if (decPos > 0) {
int numberOfDecimals = change.getControlNewText().substring(decPos + 1).length();
if (numberOfDecimals > maxDecimals) {
return null;
}
}
return change;
}
}
};
}
}
You have to use regular expressions to validate fields. You can learn more about regular expression here https://regexr.com/
String cardno = cardtf.getText();
if (cardno.equals("") || expdate1.equals("") || expdate2.equals("") || cvvnum.equals("")) {
Alert alert = new Alert(Alert.AlertType.WARNING, "Enter Full Details", ButtonType.OK);
alert.showAndWait();
}else if (cardno.matches("/^[A-Za-z ]+$/")){
Alert alert = new Alert(Alert.AlertType.WARNING, "It Can not contain letters", ButtonType.OK);
alert.showAndWait();
}else{
//Else Part
}
Here is a piece of code that should help you doing the trick by checking at every input if the text contains only numbers an a maximum of one "," as the decimal separator.
There is already a post showing how to do this.
Post
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;
public class NumberField extends TextField {
public NumberField () {
initSpellListener();
}
public final void initSpellListener() {
this.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
if (!newValue.matches("\\d*")) {
this.setText(newValue.replaceAll("[^\\d,]", ""));/*The comma here "[^\\d,]" can be changed with the dot*/
StringBuilder aus = new StringBuilder();
aus.append(this.getText());
boolean firstPointFound = false;
for (int i = 0; i < aus.length(); i++) {
if (aus.charAt(i) == ',') {/*Change the , with . if you want the . to be the decimal separator*/
if (!firstPointFound) {
firstPointFound = true;
} else {
aus.deleteCharAt(i);
}
}
}
newValue = aus.toString();
this.setText(newValue);
} else {
this.setText(newValue);
}
});
}}
[As soon as I find the post I will credit this code.]
if (!newValue.matches("\\d*"))
this part of the code checks with a regex expression if the new string value doesn't contain only numbers, and then with this code
this.setText(newValue.replaceAll("[^\\d,]", ""));
it replaces all the non-digit or comma chars.
Finally the for-loop checks if only exists one comma ad if other are found they are deleted.
To help you with regex writing here is a very useful site : Online regex
Then you can use this object as a normal TextField:
#FMXL
private NumberField nf;
I am looking for the solution for light color background gradient animation. I have looked into other examples with KeyFrames and was unable to do it by myself.
So I made a primitive version of what I need:
boolean rbottomforward = true;
boolean gbottomforward = true;
boolean bbottomforward = true;
int redbottom = 171;
int greenbottom = 186;
int bluebottom = 171;
boolean rtopforward = true;
boolean gtopforward = true;
boolean btopforward = true;
int redtop = 255;
int greentop = 255;
int bluetop = 255;
#Override
protected Void call() throws Exception {
Timeline backgroundAnimator = new Timeline(new KeyFrame(Duration.millis(200), event -> {
backgroundPane.getStylesheets().clear();
backgroundPane.setStyle("-fx-background-color: linear-gradient(to top, "+ randomBottomColor()+", "+randomTopColor()+");");
}));
backgroundAnimator.setCycleCount(Timeline.INDEFINITE);
backgroundAnimator.play();
return null;
}
private String randomBottomColor() {
greenbottom = newBottomGreen();
redbottom = newBottomRed();
bluebottom = newBottomBlue();
return String.format("rgb(%d,%d,%d)", redbottom, greenbottom, bluebottom);
}
private int newBottomGreen() {
if(greenbottom <255 && greenbottom >155){
if(gbottomforward)
greenbottom +=getRandomNumber();
else
greenbottom -=getRandomNumber();
} else if(greenbottom >=255) {
gbottomforward=false;
greenbottom -=getRandomNumber();
} else {
gbottomforward=true;
greenbottom +=getRandomNumber();
}
return greenbottom;
}
private int getRandomNumber() {
int r = (int) (Math.random() * (3 - 1)) + 1;
return r;
}
private int newBottomRed() {
if(redbottom <255 && redbottom >155){
if(rbottomforward)
redbottom +=getRandomNumber();
else
redbottom -=getRandomNumber();
} else if(redbottom >=255) {
rbottomforward=false;
redbottom -=getRandomNumber();
} else {
rbottomforward=true;
redbottom +=getRandomNumber();
}
return redbottom;
}
private int newBottomBlue() {
if(bluebottom <255 && bluebottom >155){
if(bbottomforward)
bluebottom +=getRandomNumber();
else
bluebottom -=getRandomNumber();
} else if(bluebottom >=255) {
bbottomforward=false;
bluebottom -=getRandomNumber();
} else{
bbottomforward=true;
bluebottom +=getRandomNumber();
}
return bluebottom;
}
private String randomTopColor() {
greentop = newTopGreen();
redtop = newTopRed();
bluetop = newTopBlue();
return String.format("rgb(%d,%d,%d)", redtop, greentop, bluetop);
}
private int newTopGreen() {
if(greentop <255 && greentop >155){
if(gtopforward)
greentop +=getRandomNumber();
else
greentop -=getRandomNumber();
} else if(greentop >=255) {
gtopforward=false;
greentop -=getRandomNumber();
} else {
gtopforward=true;
greentop +=getRandomNumber();
}
return greentop;
}
private int newTopRed() {
if(redtop <255 && redtop >155){
if(rtopforward)
redtop +=getRandomNumber();
else
redtop -=getRandomNumber();
} else if(redtop >=255) {
rtopforward=false;
redtop -=getRandomNumber();
} else {
rtopforward=true;
redtop +=getRandomNumber();
}
return redtop;
}
private int newTopBlue() {
if(bluetop <255 && bluetop >155){
if(btopforward)
bluetop +=getRandomNumber();
else
bluetop -=getRandomNumber();
} else if(bluetop >=255) {
btopforward=false;
bluetop -=getRandomNumber();
} else{
btopforward=true;
bluetop +=getRandomNumber();
}
return bluetop;
}
This is a bit overkill. Can anyone help me convert this to a more advanced level code. Thanks :)
There's really not much you can do about this but reduce the repetition in code. (You won't be able to interpolate anything, since on every update the next color is chosen at random.)
You should use the same code for all the color channels:
/**
* Class containing logic for a single color channel.
*/
private class AnimatedChannel {
private boolean increment = true;
private int value;
AnimatedChannel(int value) {
this.value = value;
}
/**
* Changes channel by random amount in [1, 3] keeping it in range [155, 255]
*/
private void update() {
int delta = getRandomNumber();
if (increment) {
value += delta;
if (value > 0xFF) {
value = (2 * 0xFF) - value; // v = max - (v - max)
increment = false;
}
} else {
value -= delta;
if (value < 155) {
value = (2 * 155) - value; // v = min + (min - v)
increment = true;
}
}
}
}
private final Random random = new Random();
private int getRandomNumber() {
return random.nextInt(3 - 1) + 1;
}
private final AnimatedChannel bottomRed = new AnimatedChannel(171);
private final AnimatedChannel bottomGreen = new AnimatedChannel(186);
private final AnimatedChannel bottomBlue = new AnimatedChannel(171);
private final AnimatedChannel topRed = new AnimatedChannel(0xFF);
private final AnimatedChannel topGreen = new AnimatedChannel(0xFF);
private final AnimatedChannel topBlue = new AnimatedChannel(0xFF);
private final AnimatedChannel[] channels = new AnimatedChannel[] {
bottomRed,
bottomGreen,
bottomBlue,
topRed,
topGreen,
topBlue
};
#Override
protected Void call() throws Exception {
Timeline backgroundAnimator = new Timeline(new KeyFrame(Duration.millis(200), event -> {
for (AnimatedChannel channel : channels) {
channel.update();
}
backgroundPane.setBackground(new Background(new BackgroundFill(
new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE,
new Stop(0, channelsToColor(topRed, topGreen, topBlue)),
new Stop(1, channelsToColor(bottomRed, bottomGreen, bottomBlue))),
CornerRadii.EMPTY,
Insets.EMPTY)));
}));
backgroundAnimator.setCycleCount(Timeline.INDEFINITE);
backgroundAnimator.play();
return null;
}
private static Color channelsToColor(AnimatedChannel red, AnimatedChannel green, AnimatedChannel blue) {
return Color.rgb(red.value, green.value, blue.value);
}
This code is not that much shorter than your code, but it's easier to maintain. If you want to modify the logic for updating the channels, you only need to adjust 1 method instead of 6 methods. Also it relieves JavaFX of the burden of parsing the inline CSS on every update.
Note:
You seem to be extending Task<Void> or some similar concurrency-related class. This is not really necessary, since Timeline.play is not a long running operation but simply triggers repeated execution of logic (in this case the EventHandler<ActionEvent>) on the application thread at a later time.
I want to use updateMessage("...") in a Class that is not an Task.
How can I solve this Problem?
Is it possible to give a Task Handle which can perform a updateMessage("...") to a Function?
import javafx.concurrent.Task;
public class TestTask extends Task<Void> {
private final static int COUNT = 1000;
#Override
protected Void call() {
doTaskStuff("Found ");
return null;
}
public void doTaskStuff(String s) {
for (int i = 0; i < COUNT; i++) {
System.out.println(i);
this.updateProgress(i, COUNT);
this.updateMessage(s + (i + 1) + "!");
Dummy.dummy(this, i);
try {
Thread.sleep(1);
} catch (InterruptedException interruptedException) {
}
}
}
}
class Dummy {
public static void dummy(TestTask testTask, int i) {
String s = "";
if (i == 0) {
s = "i == 0";
} else {
s = "i != 0";
}
testTask.updateMessage(s);// does not work
}
}
The issue here is the method being protected. Of course you could override the method and increase the visibility, but this would imho be a bad approach:
#Override
public void updateMessage(String message) {
super.updateMessage(message);
}
Note that the method sets a single message, therefore you could just return the value:
updateMessage(Dummy.dummy(i));
public static String dummy(int i) {
return (i == 0) ? "i == 0" : "i != 0";
}
For multiple updates you could also provide access to this kind of functionality using an interface:
Dummy.dummy(this::updateMessage, i);
public static void dummy(Consumer<String> updater, int i) {
updater.accept((i == 0) ? "i == 0" : "i != 0");
}
I have written the following code and keep running into this error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at yournamep3.Yournamep3test.main(Yournamep3test.java:23)
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
public class Yournamep3test {
public static void main(String[] args) {
// Check if target file exists
File targetFile = new File(args[0]);
try {
PrintWriter out = new PrintWriter(targetFile);
out.write("\r\nStringed musical Instrument program");
for (int arrayIndex = 0; arrayIndex < 10; arrayIndex++) {
out.write("\r\n\r\n");
out.write("\r\nCreating new Stringed Musical Instrument object now..............");
Yournamep3 violinInstrument = new Yournamep3();
violinInstrument.setNameOfInstrument("Violin # " + (arrayIndex+1));
out.write("\r\nCreated instrument with name - "
+ violinInstrument.getNameOfInstrument());
int num = violinInstrument.getNumberOfStrings();
out.write("\r\nNumber of strings in instrument is " + num);
out.write("\r\nNames of String are ");
String strings[] = violinInstrument.getStringNames();
for (int counter = 0; counter < num; counter++) {
out.write("\r\n" + strings[counter]);
}
out.write("\r\nIs the Instrument playing - "
+ violinInstrument.isPlaying());
out.write("\r\nIs the Instrument tuned - "
+ violinInstrument.isTuned());
out.write("\r\nTuning now.........");
violinInstrument.setTuned(true);
out.write("\r\nIs the Instrument tuned - "
+ violinInstrument.isTuned());
out.write("\r\nCalling the Instrument play method now..");
violinInstrument.startPlayInstrument();
out.write("\r\nIs the Instrument playing - "
+ violinInstrument.isPlaying());
out.write("\r\nStopping playing of instrument..............");
violinInstrument.stopPlayInstrument();
out.write("\r\nIs the Instrument playing - "
+ violinInstrument.isPlaying());
}
out.close();
} catch (IOException e) {
}
}
}
I think the issue is with line 23. Any advice would be appreciated, thanks.
This is the other part of the code yournamep3
public class Yournamep3 {
//fields to determine if the instrument is isTuned,
private boolean isTuned;
//and if the instrument is currently isPlaying.
private boolean isPlaying;
private String name;
private int numberOfStrings = 4; // number of strings
private String nameofStringsInInstrument[] = {"E", "C", "D", "A"}; //an array of string names
//A constructor method that set the isTuned and currently isPlaying fields to false.
public Yournamep3() {
this.isTuned = false;
this.isPlaying = false;
}
/**
* #return the name
*/
public String getNameOfInstrument() {
return name;
}
/**
* #param name the name to set
*/
public void setNameOfInstrument(String nameOfInstrument) {
this.name = nameOfInstrument;
}
// Other methods
public boolean isPlaying() {
return isPlaying;
}
public void setPlaying(boolean playing) {
this.isPlaying = playing;
}
public boolean isTuned() {
return isTuned;
}
public void setTuned(boolean isTuned) {
this.isTuned = isTuned;
}
public void startPlayInstrument() {
System.out.println("The Instrument is now Playing.");
isPlaying = true;
}
public void stopPlayInstrument() {
System.out.println("The Instrument is not Playing anymore.");
isPlaying = false;
}
public void startTuneInstrument() {
System.out.println("The Instrument is Tuned.");
isTuned = true;
}
public void stopTuneInstrument() {
System.out.println("The Instrument is not Tuned.");
isTuned = false;
}
public int getNumberOfStrings() {
return this.numberOfStrings ;
}
public String[] getStringNames() {
return nameofStringsInInstrument;
}
}
I would look at your getStringNames() method for your violinInstrument. It seems to me that it isn't populating your String array properly, or the getNumberOfStrings() method does not give the right number of strings. If you put the code for that up, I can help a bit more.
Line 23 appears to be
Yournamep3 violinInstrument = new Yournamep3();
If that's the case you should check the constructor for Yournamemp3
Since Line 23 is
File targetFile = new File(args [0]);
It indicates that your args object is empty. ArrayIndexOutOfBoundException is thrown to indicate that an array has been accessed with an illegal index. 0 is an illegal index.