package com.taocares.naoms.client.model.common.custom.control;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import javafx.beans.NamedArg;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.SpinnerValueFactory;
import javafx.util.StringConverter;
public class LocalDateTimeSpinnerValueFactory extends SpinnerValueFactory{
/**
* Creates a new instance of the LocalDateSpinnerValueFactory, using the
* value returned by calling {@code LocalDate#now()} as the initial value,
* and using a stepping amount of one day.
*/
public LocalDateTimeSpinnerValueFactory() {
this(LocalDateTime.now());
}
/**
* Creates a new instance of the LocalDateSpinnerValueFactory, using the
* provided initial value, and a stepping amount of one day.
*
* @param initialValue The value of the Spinner when first instantiated.
*/
public LocalDateTimeSpinnerValueFactory(@NamedArg("initialValue") LocalDateTime initialValue) {
this(LocalDateTime.MIN, LocalDateTime.MAX, initialValue);
}
/**
* Creates a new instance of the LocalDateSpinnerValueFactory, using the
* provided initial value, and a stepping amount of one day.
*
* @param min The minimum allowed double value for the Spinner.
* @param max The maximum allowed double value for the Spinner.
* @param initialValue The value of the Spinner when first instantiated.
*/
public LocalDateTimeSpinnerValueFactory(@NamedArg("min") LocalDateTime min,
@NamedArg("min") LocalDateTime max,
@NamedArg("initialValue") LocalDateTime initialValue) {
this(min, max, initialValue, 1, ChronoUnit.DAYS,"yyyy-MM-dd HH:mm:ss");
}
/**
* Creates a new instance of the LocalDateSpinnerValueFactory, using the
* provided min, max, and initial values, as well as the amount to step
* by and {@link java.time.temporal.TemporalUnit}.
*
* To better understand, here are a few examples:
*
*
* - To step by one day from today: {@code new LocalDateSpinnerValueFactory(LocalDate.MIN, LocalDate.MAX, LocalDate.now(), 1, ChronoUnit.DAYS)}
* - To step by one month from today: {@code new LocalDateSpinnerValueFactory(LocalDate.MIN, LocalDate.MAX, LocalDate.now(), 1, ChronoUnit.MONTHS)}
* - To step by one year from today: {@code new LocalDateSpinnerValueFactory(LocalDate.MIN, LocalDate.MAX, LocalDate.now(), 1, ChronoUnit.YEARS)}
*
*
* @param min The minimum allowed double value for the Spinner.
* @param max The maximum allowed double value for the Spinner.
* @param initialValue The value of the Spinner when first instantiated.
* @param amountToStepBy The amount to increment or decrement by, per step.
* @param temporalUnit The size of each step (e.g. day, week, month, year, etc)
*/
public LocalDateTimeSpinnerValueFactory(@NamedArg("min") LocalDateTime min,
@NamedArg("min") LocalDateTime max,
@NamedArg("initialValue") LocalDateTime initialValue,
@NamedArg("amountToStepBy") long amountToStepBy,
@NamedArg("temporalUnit") TemporalUnit temporalUnit,
@NamedArg("pattern") String pattern) {
setMin(min);
setMax(max);
setAmountToStepBy(amountToStepBy);
setTemporalUnit(temporalUnit);
setPattern(pattern);
setConverter(new StringConverter() {
@Override public String toString(LocalDateTime object) {
if (object == null) {
return "";
}
DateTimeFormatter stf = DateTimeFormatter.ofPattern(getPattern());
return object.format(stf);
}
@Override public LocalDateTime fromString(String string) {
return LocalDateTime.parse(string);
}
});
valueProperty().addListener((o, oldValue, newValue) -> {
// when the value is set, we need to react to ensure it is a
// valid value (and if not, blow up appropriately)
if(newValue!=null){
if (getMin() != null && newValue.isBefore(getMin())) {
setValue(getMin());
} else if (getMax() != null && newValue.isAfter(getMax())) {
setValue(getMax());
}
}
});
setValue(initialValue != null ? initialValue : LocalDateTime.now());
}
/***********************************************************************
* *
* Properties *
* *
**********************************************************************/
// --- min
private ObjectProperty min = new SimpleObjectProperty(this, "min") {
@Override protected void invalidated() {
LocalDateTime currentValue = LocalDateTimeSpinnerValueFactory.this.getValue();
if (currentValue == null) {
return;
}
final LocalDateTime newMin = get();
if (newMin.isAfter(getMax())) {
setMin(getMax());
return;
}
if (currentValue.isBefore(newMin)) {
LocalDateTimeSpinnerValueFactory.this.setValue(newMin);
}
}
};
public final void setMin(LocalDateTime value) {
min.set(value);
}
public final LocalDateTime getMin() {
return min.get();
}
/**
* Sets the minimum allowable value for this value factory
*/
public final ObjectProperty minProperty() {
return min;
}
// --- max
private ObjectProperty max = new SimpleObjectProperty(this, "max") {
@Override protected void invalidated() {
LocalDateTime currentValue = LocalDateTimeSpinnerValueFactory.this.getValue();
if (currentValue == null) {
return;
}
final LocalDateTime newMax = get();
if (newMax.isBefore(getMin())) {
setMax(getMin());
return;
}
if (currentValue.isAfter(newMax)) {
LocalDateTimeSpinnerValueFactory.this.setValue(newMax);
}
}
};
public final void setMax(LocalDateTime value) {
max.set(value);
}
public final LocalDateTime getMax() {
return max.get();
}
/**
* Sets the maximum allowable value for this value factory
*/
public final ObjectProperty maxProperty() {
return max;
}
// --- temporalUnit
private ObjectProperty temporalUnit = new SimpleObjectProperty<>(this, "temporalUnit");
public final void setTemporalUnit(TemporalUnit value) {
temporalUnit.set(value);
}
public final TemporalUnit getTemporalUnit() {
return temporalUnit.get();
}
/**
* The size of each step (e.g. day, week, month, year, etc).
*/
public final ObjectProperty temporalUnitProperty() {
return temporalUnit;
}
// --- amountToStepBy
private LongProperty amountToStepBy = new SimpleLongProperty(this, "amountToStepBy");
public final void setAmountToStepBy(long value) {
amountToStepBy.set(value);
}
public final long getAmountToStepBy() {
return amountToStepBy.get();
}
/**
* Sets the amount to increment or decrement by, per step.
*/
public final LongProperty amountToStepByProperty() {
return amountToStepBy;
}
//---- pattern
private StringProperty pattern=new SimpleStringProperty(this, "pattern");
public final void setPattern(String value){
pattern.setValue(value);
}
public final String getPattern(){
return pattern.get();
}
public final StringProperty patternProperty(){
return pattern;
}
/***********************************************************************
* *
* Overridden methods *
* *
**********************************************************************/
/** {@inheritDoc} */
@Override public void decrement(int steps) {
final LocalDateTime currentValue = getValue();
final LocalDateTime min = getMin();
if(currentValue==null){
setValue(LocalDateTime.now());
return;
}
LocalDateTime newValue = currentValue.minus(getAmountToStepBy() * steps, getTemporalUnit());
if (min != null && isWrapAround() && newValue.isBefore(min)) {
// we need to wrap around
newValue = getMax();
}
setValue(newValue);
}
/** {@inheritDoc} */
@Override public void increment(int steps) {
final LocalDateTime currentValue = getValue();
final LocalDateTime max = getMax();
if(currentValue==null){
setValue(LocalDateTime.now());
return;
}
LocalDateTime newValue = currentValue.plus(getAmountToStepBy() * steps, getTemporalUnit());
if (max != null && isWrapAround() && newValue.isAfter(max)) {
// we need to wrap around
newValue = getMin();
}
setValue(newValue);
}
}
package com.taocares.naoms.client.model.common.custom.control;
import java.time.temporal.ChronoUnit;
/**
* 时间控件 ENUM
*
* @author wxx
*
*/
public enum LocalDateTimeSpinnerCode {
YEARS(0, 4, 1, ChronoUnit.YEARS),
MONTHS(5, 7, 2, ChronoUnit.MONTHS),
DAYS(8, 10, 3, ChronoUnit.DAYS),
HOURS(11, 13, 4, ChronoUnit.HOURS),
MINUTES(14, 16, 5, ChronoUnit.MINUTES),
SECONDS(17, 19, 6, ChronoUnit.SECONDS);
private Integer start;
private Integer end;
private Integer index;
private ChronoUnit chronoUnit;
LocalDateTimeSpinnerCode(Integer start, Integer end, Integer index, ChronoUnit chronoUnit) {
this.start = start;
this.end = end;
this.index = index;
this.chronoUnit = chronoUnit;
}
/**
* 根据Start获取对象
*
* @param start
* @return
*/
public static LocalDateTimeSpinnerCode getCodeByStart(Integer start) {
for (LocalDateTimeSpinnerCode code : LocalDateTimeSpinnerCode.values()) {
if (code.getStart() <= start && code.getEnd() >= start) {
return code;
}
}
return null;
}
/**
* 根据Index 获取对象
* @param index
* @return
*/
public static LocalDateTimeSpinnerCode getCodeByIndex(Integer index){
for (LocalDateTimeSpinnerCode code : LocalDateTimeSpinnerCode.values()) {
if (code.getIndex()==index) {
return code;
}
}
return null;
}
public Integer getStart() {
return start;
}
public Integer getEnd() {
return end;
}
public Integer getIndex() {
return index;
}
public ChronoUnit getChronoUnit() {
return chronoUnit;
}
}
package com.taocares.naoms.client.model.common.custom.control;
import java.time.LocalDateTime;
import java.util.Date;
import com.taocares.naoms.client.model.common.util.LocalDateConvert;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.control.IndexRange;
import javafx.scene.control.Spinner;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
/**
* 时间控件 目前适用于yyyy-MM-dd HH:mm 格式 使用方式: new LocalDateTimeSpinner()||new
* LocalDateTimeSpinner(Date date) 存取值方式: setDate(Date date),Date
* getDate()||LocalDateTime getValue()
*
* @author wxx
* @author stone 添加监听,左右切换以及键入验证
*
*/
public class LocalDateTimeSpinner extends Spinner {
private static Integer CURRENT_INDEX = -1;
private static Integer CURRENT = 0;
private boolean isMinute = false;
LocalDateTimeSpinnerValueFactory ldtsvf = new LocalDateTimeSpinnerValueFactory();
public LocalDateTimeSpinner() {
init();
}
public LocalDateTimeSpinner(Date date) {
init();
setDate(date);
}
public void setDate(Date date) {
if (date != null) {
this.getValueFactory().setValue(LocalDateConvert.UDateToLocalDateTime(date));
} else {
this.getValueFactory().setValue(null);
}
}
public Date getDate() {
if (this.getValueFactory().getValue() != null) {
return LocalDateConvert.LocalDateTimeToUdate(this.getValueFactory().getValue());
}
return null;
}
/**
* 切换调整元素
*/
private void switchTemporalUnit() {
TextField tf = this.getEditor();
LocalDateTimeSpinnerCode code = LocalDateTimeSpinnerCode.getCodeByIndex(CURRENT_INDEX);
if (code == null) {
IndexRange range = tf.getSelection();
code = LocalDateTimeSpinnerCode.getCodeByStart(range.getStart());
}
tf.selectRange(code.getStart(), code.getEnd());
CURRENT_INDEX = code.getIndex();
ldtsvf.setTemporalUnit(code.getChronoUnit());
}
/**
* 双击
*/
private void switchTemporalUnitByDoubleClick() {
TextField tf = this.getEditor();
IndexRange range = tf.getSelection();
LocalDateTimeSpinnerCode code = LocalDateTimeSpinnerCode.getCodeByStart(range.getStart());
tf.selectRange(code.getStart(), code.getEnd());
CURRENT_INDEX = code.getIndex();
ldtsvf.setTemporalUnit(code.getChronoUnit());
}
/**
* 若没有通过之前验证,字符串替换为0+arg0.getCharacter()
*
* @param code
* @param arg0
*/
private void keyBoardingElse(LocalDateTimeSpinnerCode code, KeyEvent arg0) {
CURRENT++;
getEditor().replaceText(code.getStart(), code.getEnd(), 0 + arg0.getCharacter());
}
/**
* 键入验证
*
* @param code
* @param regex
* @param arg0
* @return
*/
private boolean keyBoardingRegex(LocalDateTimeSpinnerCode code, String regex, KeyEvent arg0) {
if (getEditor().getText(code.getEnd() - 1, code.getEnd()).matches(regex)) {
CURRENT++;
getEditor().replaceText(code.getStart(), code.getEnd(),
getEditor().getText(code.getEnd() - 1, code.getEnd()) + arg0.getCharacter());
return true;
}
return false;
}
private void init() {
ldtsvf.setPattern("yyyy-MM-dd HH:mm");
setValueFactory(ldtsvf);
// 初始化,值为空
getValueFactory().setValue(null);
setOnKeyPressed(new EventHandler() {
@Override
public void handle(KeyEvent event) {
switch (event.getCode()) {
case LEFT:
focusLeft();
// 此处可能被其他捕获
event.consume();
break;
case RIGHT:
focusRight();
// 此处可能被其他捕获
event.consume();
break;
case DELETE:
getEditor().setText("");
setDate(null);
break;
case BACK_SPACE:
getEditor().setText("");
setDate(null);
break;
default:
break;
}
}
});
this.getEditor().setOnMouseClicked(new EventHandler() {
@Override
public void handle(MouseEvent event) {
switchTemporalUnitByDoubleClick();
}
});
valueProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue extends LocalDateTime> observable, LocalDateTime oldValue,
LocalDateTime newValue) {
if (CURRENT == 2) {
if (!isMinute) {
focusRight();
CURRENT = 0;
} else {
focusLeft();
isMinute = false;
CURRENT = 0;
}
} else {
switchTemporalUnit();
}
}
});
/**
* 控制键入
*/
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler() {
@Override
public void handle(KeyEvent event) {
IndexRange range = getEditor().getSelection();
LocalDateTimeSpinnerCode code = LocalDateTimeSpinnerCode.getCodeByStart(range.getStart());
if(getEditor().getText()==null){
return;
}
if (event.getCharacter().matches("[0-9]")) {
switch (code.getStart()) {
case 0:
if(code.getEnd()<0||code.getEnd()>getEditor().getText().length()){
break;
}
if (getEditor().getText(code.getEnd() - 1, code.getEnd()).matches("[0-9]")) {
getEditor().replaceText(code.getStart() + 2, code.getEnd(),
getEditor().getText(code.getEnd() - 1, code.getEnd()) + event.getCharacter());
}
break;
case 5:
if (!keyBoardingRegex(code, "1", event)) {
keyBoardingElse(code, event);
}
break;
case 8:
if (!keyBoardingRegex(code, "[1-3]", event)) {
keyBoardingElse(code, event);
}
break;
case 11:
if (!keyBoardingRegex(code, "[1-2]", event)) {
keyBoardingElse(code, event);
}
break;
case 14:
isMinute = true;
if (!keyBoardingRegex(code, "[1-5]", event)) {
keyBoardingElse(code, event);
}
break;
}
}
setDate(LocalDateConvert.getDate(getEditor().getText(), "yyyy-MM-dd HH:mm"));
}
});
setPrefWidth(180);
}
public void focusLeft() {
int count = ldtsvf.getPattern().split("\\-| |:").length;
CURRENT_INDEX--;
if (CURRENT_INDEX == 0) {
CURRENT_INDEX = count;
}
switchTemporalUnit();
}
public void focusRight() {
int count = ldtsvf.getPattern().split("\\-| |:").length;
if (CURRENT_INDEX == count) {
CURRENT_INDEX = 0;
}
CURRENT_INDEX++;
switchTemporalUnit();
}
}