2015-04-29 3 views
1

Я пытаюсь использовать TextField в javafx. Сценарий: у меня есть список, заполненный конкретными объектами, и кнопка редактирования, чтобы отредактировать объект, связанный со списком со списком списка. Когда я нажимаю кнопку «Редактировать», она перенаправляет меня на панель с функцией редактирования, где я могу редактировать имя этого объекта и сохранять его с помощью кнопки сохранения. Поэтому я должен поставить валидацию на кнопку сохранения, чтобы включить ее и отключить. Если я редактирую имя в текстовом поле, тогда он должен включить кнопку сохранения, иначе он должен оставаться отключенным. Я попытался использовать различные методы в текстовых полях, как показано ниже.TextField onEdit прослушиватель

textField.textPorperty.addListener(listener -> { 
     //Logic to enable disable save button 
}); 

Поскольку я пользуюсь списком, этот слушатель дает мне старое значение как отредактированный ранее объект, который не удовлетворяет моему состоянию. я не могу использовать

textField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {}); 

, как это не дает мне ожидаемое поведение.

Может ли кто-нибудь помочь мне решить эту проблему?

+0

Рассмотрим сценарий, если начальный текст «выдан», который изменяется на «выпуск», а затем обратно на «выпущенный», , Будет ли кнопка оставаться включенной? – ItachiUchiha

+0

Если текст выпущен изменен на кнопку выпуска - кнопка включена, если она снова была изменена на выданную без сохранения ранее измененного текста, она должна оставаться отключенной. –

ответ

1

Вам необходимо реализовать дополнительную логику, которая решает, должно ли изменение textProperty изменить состояние включения кнопки. Для этого необходимо:

  • ссылка на начальное значение (по настройке текста на вход, фантастический об изменениях выбора в списке)
  • булева свойство, которое сохраняет состояние ВОЗМОЖНОСТЕЙ (ниже это называется буферизация)
  • слушателя к TextField, который обновляет состояние Enablement при необходимости

Ниже приведен очень упрощенный пример - просто, чтобы вы начали - что извлекает эти основы в специальный класс с именем BufferedTextInput. Буферизация изменяется внутри на:

  • значение ЛОЖЬ, если значение «субъект» устанавливается или изменение совершено/выброшенного
  • значение ИСТИНА один раз на уведомления о первом изменении TextField

Более сложная логика (например, не буферизация при обнаружении изменения обратно к исходному значению) может быть реализована по мере необходимости.

/** 
* Bind disable property of commit/cancel button to actual change. 
* http://stackoverflow.com/q/29935643/203657 
*/ 
public class ManualBufferingDemo extends Application { 

    private Parent getContent() { 
     ObservableList<Person> persons = FXCollections.observableList(Person.persons(), 
       person -> new Observable[] {person.lastNameProperty()}); 
     ListView<Person> listView = new ListView<>(persons); 

     TextField lastName = new TextField(); 
     Consumer<String> committer = text -> System.out.println("committing: " + text); 
     BufferedTextInput buffer = new BufferedTextInput(lastName, committer); 
     Button save = new Button("Save"); 
     save.setOnAction(e -> { 
      buffer.commit(); 
     }); 
     save.disableProperty().bind(Bindings.not(buffer.bufferingProperty())); 
     Button cancel = new Button("Cancel"); 
     cancel.setOnAction(e -> { 
      buffer.flush(); 
     }); 
     listView.getSelectionModel().selectedItemProperty().addListener((source, old, current) -> { 
      buffer.setSubject(current.lastNameProperty()); 
     }); 
     cancel.disableProperty().bind(Bindings.not(buffer.bufferingProperty())); 
     VBox content = new VBox(listView, lastName, save, cancel); 
     return content; 
    } 


    public static class BufferedTextInput { 

     private ReadOnlyBooleanWrapper buffering; 
     private StringProperty value; 
     private TextField input; 
     private Consumer<String> committer; 

     public BufferedTextInput(TextField input, Consumer<String> committer) { 
      buffering = new ReadOnlyBooleanWrapper(this, "buffering", false); 
      value = new SimpleStringProperty(this, ""); 
      this.input = input; 
      this.committer = committer; 
      input.textProperty().addListener((source, old, current) -> { 
       updateState(old, current); 
      }); 
      input.setOnAction(e -> commit()); 
     } 

     private void updateState(String old, String current) { 
      if (isBuffering()) return; 
      if (value.get().equals(current)) return; 
      setBuffering(true); 
     } 

     public void setSubject(StringProperty value) { 
      this.value = value; 
      input.setText(value.get()); 
      setBuffering(false); 
     } 

     public void commit() { 
      committer.accept(input.getText()); 
      this.value.set(input.getText()); 
      setBuffering(false); 
     } 

     public void flush() { 
      input.setText(value.get()); 
      setBuffering(false); 
     } 

     public boolean isBuffering() { 
      return buffering.get(); 
     } 

     public ReadOnlyBooleanProperty bufferingProperty() { 
      return buffering.getReadOnlyProperty(); 
     } 

     private void setBuffering(boolean buffer) { 
      buffering.set(buffer); 
     } 
    } 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     primaryStage.setScene(new Scene(getContent())); 
     primaryStage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

Для использования производства, такая прямая связь между видом и моделью (F.I. при необходимости буферизации для полной формы) не является достаточно хорошим, дальнейшее разделение может быть необходимо. См. BufferedObjectProperty и его использование в FX-адаптации печально известного примера AlbumManager (очень грубая)

Смежные вопросы