2017-10-12 63 views
2

所以我在我嘗試創建的應用程序有問題。我已經創建了一個示例應用程序來演示問題。在應用程序中,如果輸入未在TextField上按下,我試圖停止TableView中的選定項目更改。在我的實現中,我遇到了StackOverFlow錯誤。我明白爲什麼我得到錯誤。我基本上創造了一個無限循環,但我想不出解決這個問題的另一種方法。停止ListView選擇的項目從更改,直到按下按鈕

如果去掉這行代碼:

if(!validateTextFields()) 
{ 
    tvPerson.getSelectionModel().select(oldPerson); 
    return; 
} 

該應用程序的工作原理是,如果你選擇一個錶行,然後在TextField編輯的文字,然後按上TextField進入它的設計。但是,如果選擇一個表格行,編輯TextField並且不要按下回車鍵,用戶可以選擇一個新的表格行,而不更新他/她試圖編輯的表格行。因此,我的問題是,如果用戶沒有通過按回車確認TextField編輯,我該如何阻止用戶更改selectedItem

控制器

import java.net.URL; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
import java.util.ResourceBundle; 
import javafx.collections.FXCollections; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.control.TextField; 
import javafx.scene.control.cell.PropertyValueFactory; 

/** 
* 
* @author Sedrick 
*/ 
public class FXMLDocumentController implements Initializable { 

    @FXML TextField tfFirstName, tfLastName; 
    @FXML TableView<Person> tvPerson; 
    @FXML TableColumn<Person, String> tcFirstName, tcLastName; 

    final String firstNames = "Darryl  \n" + 
           "Enriqueta  \n" + 
           "Katherine  \n" + 
           "Harley  \n" + 
           "Arlean  \n" + 
           "Jacquelynn  \n" + 
           "Yuko  \n" + 
           "Dion  \n" + 
           "Vivan  \n" + 
           "Carly  \n" + 
           "Eldon  \n" + 
           "Joe  \n" + 
           "Klara  \n" + 
           "Shona  \n" + 
           "Delores  \n" + 
           "Sabra  \n" + 
           "Vi  \n" + 
           "Gearldine  \n" + 
           "Laine  \n" + 
           "Lila  "; 

    final String lastNames = "Ollie  \n" + 
           "Donnette  \n" + 
           "Audra  \n" + 
           "Angelica  \n" + 
           "Janna  \n" + 
           "Lekisha  \n" + 
           "Michael  \n" + 
           "Tomi  \n" + 
           "Cheryl  \n" + 
           "Roni  \n" + 
           "Aurelio  \n" + 
           "Mayola  \n" + 
           "Kelsie  \n" + 
           "Britteny  \n" + 
           "Dannielle  \n" + 
           "Kym  \n" + 
           "Scotty  \n" + 
           "Deloris  \n" + 
           "Lavenia  \n" + 
           "Sun  \n"; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
     // TODO 
     tcFirstName.setCellValueFactory(new PropertyValueFactory("firstName")); 
     tcLastName.setCellValueFactory(new PropertyValueFactory("lastName")); 

     tvPerson.setItems(FXCollections.observableArrayList(getPersons())); 
     tvPerson.getSelectionModel().selectedItemProperty().addListener((obs, oldPerson, newPerson)->{ 
      if(!validateTextFields()) 
      { 
       tvPerson.getSelectionModel().select(oldPerson); 
       return; 
      } 

      if(newPerson != null) 
      { 
       tfFirstName.setText(newPerson.getFirstName()); 
       tfLastName.setText(newPerson.getLastName()); 
      } 
     }); 

     tfFirstName.setOnKeyReleased(keyEvent ->{ 
      Person tempPerson = tvPerson.getSelectionModel().getSelectedItem(); 
      if(!tfFirstName.getText().trim().equals(tempPerson.getFirstName().trim())) 
      { 
       tfFirstName.setStyle("-fx-control-inner-background: red;"); 
      } 
     }); 

     tfFirstName.setOnAction(actionEvent ->{ 
      Person tempPerson = tvPerson.getSelectionModel().getSelectedItem(); 
      tempPerson.setFirstName(tfFirstName.getText().trim()); 

      tfFirstName.setStyle(null); 
     });  

     tfLastName.setOnKeyReleased(keyEvent ->{ 
      Person tempPerson = tvPerson.getSelectionModel().getSelectedItem(); 
      if(tfLastName.getText().trim().equals(tempPerson.getLastName().trim())) 
      { 
       tfLastName.setStyle("-fx-control-inner-background: red;"); 
      } 
     }); 

     tfLastName.setOnAction(actionEvent ->{ 
      Person tempPerson = tvPerson.getSelectionModel().getSelectedItem(); 
      tempPerson.setLastName(tfLastName.getText().trim()); 

      tfLastName.setStyle(null); 
     }); 

    }  

    private boolean validateTextFields() 
    { 
     if(!tfFirstName.getStyle().isEmpty()){return false;} 
     if(!tfLastName.getStyle().isEmpty()){return false;} 

     return true; 
    } 

    List<Person> getPersons() 
    { 
     List<Person> tempPerson = new ArrayList(); 

     List<String> tempFirstName = Arrays.asList(firstNames.split("\n")); 
     List<String> tempLastName = Arrays.asList(lastNames.split("\n")); 

     for(int i = 0; i < tempFirstName.size(); i++) 
     { 
      tempPerson.add(new Person(tempFirstName.get(i).trim(), tempLastName.get(i).trim())); 
     } 

     return tempPerson; 
    } 

} 

FXML

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.geometry.Insets?> 
<?import javafx.scene.control.Label?> 
<?import javafx.scene.control.TableColumn?> 
<?import javafx.scene.control.TableView?> 
<?import javafx.scene.control.TextField?> 
<?import javafx.scene.layout.AnchorPane?> 
<?import javafx.scene.layout.HBox?> 
<?import javafx.scene.layout.VBox?> 

<AnchorPane id="AnchorPane" prefHeight="575.0" prefWidth="836.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication17.FXMLDocumentController"> 
    <children> 
     <VBox layoutX="7.0" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> 
     <children> 
      <VBox prefHeight="200.0" prefWidth="100.0" spacing="5.0" VBox.vgrow="ALWAYS"> 
       <children> 
        <HBox spacing="5.0"> 
        <children> 
         <Label prefHeight="31.0" prefWidth="72.0" text="First Name" /> 
         <TextField fx:id="tfFirstName" /> 
        </children> 
        </HBox> 
        <HBox spacing="5.0"> 
        <children> 
         <Label prefHeight="31.0" prefWidth="72.0" text="Last Name" /> 
         <TextField fx:id="tfLastName" /> 
        </children> 
        </HBox> 
       </children> 
       <padding> 
        <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> 
       </padding> 
      </VBox> 
      <TableView fx:id="tvPerson" prefHeight="200.0" prefWidth="200.0"> 
       <columns> 
       <TableColumn fx:id="tcFirstName" prefWidth="108.0" text="First Name" /> 
       <TableColumn fx:id="tcLastName" prefWidth="110.0" text="Last Name" /> 
       </columns> 
       <VBox.margin> 
        <Insets /> 
       </VBox.margin> 
       <padding> 
        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> 
       </padding> 
      </TableView> 
     </children> 
     </VBox> 
    </children> 
</AnchorPane> 

Person類

import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

/** 
* 
* @author Sedrick 
*/ 
public class Person { 
    StringProperty firstName = new SimpleStringProperty(); 
    StringProperty lastName = new SimpleStringProperty(); 

    public Person(String firstName, String lastName) 
    { 
     this.firstName.set(firstName); 
     this.lastName.set(lastName); 
    } 

    public StringProperty firstNameProperty() 
    { 
     return firstName; 
    } 

    public String getFirstName() 
    { 
     return firstName.get(); 
    } 

    public void setFirstName(String firstName) 
    { 
     this.firstName.set(firstName); 
    } 

    public StringProperty lastNameProperty() 
    { 
     return lastName; 
    } 

    public String getLastName() 
    { 
     return lastName.get(); 
    } 

    public void setLastName(String firstName) 
    { 
     this.lastName.set(firstName); 
    } 

} 

異常

Exception in thread "JavaFX Application Thread" java.lang.StackOverflowError 
    at javafx.collections.ListChangeBuilder.findSubChange(ListChangeBuilder.java:62) 
    at javafx.collections.ListChangeBuilder.insertAdd(ListChangeBuilder.java:127) 
    at javafx.collections.ListChangeBuilder.nextAdd(ListChangeBuilder.java:254) 
    at javafx.collections.ObservableListBase.nextAdd(ObservableListBase.java:179) 
    at javafx.collections.transformation.SortedList.setAllToMapping(SortedList.java:354) 
    at javafx.collections.transformation.SortedList.addRemove(SortedList.java:397) 
    at javafx.collections.transformation.SortedList.sourceChanged(SortedList.java:108) 
    at javafx.collections.transformation.TransformationList.lambda$getListener$23(TransformationList.java:106) 
    at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88) 
    at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164) 
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73) 
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233) 
    at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482) 
    at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541) 
    at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205) 
    at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:155) 
    at java.util.AbstractList.add(AbstractList.java:108) 
    at com.sun.javafx.scene.control.SelectedCellsMap.add(SelectedCellsMap.java:118) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2456) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2427) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2485) 
    at javafxapplication17.FXMLDocumentController.lambda$initialize$0(FXMLDocumentController.java:83) 
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182) 
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
    at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74) 
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102) 
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112) 
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146) 
    at javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:102) 
    at javafx.scene.control.MultipleSelectionModelBase.lambda$new$34(MultipleSelectionModelBase.java:67) 
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137) 
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
    at javafx.beans.property.ReadOnlyIntegerPropertyBase.fireValueChangedEvent(ReadOnlyIntegerPropertyBase.java:72) 
    at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:102) 
    at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:113) 
    at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:147) 
    at javafx.scene.control.SelectionModel.setSelectedIndex(SelectionModel.java:68) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.updateSelectedIndex(TableView.java:2945) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2458) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2427) 
    at javafx.scene.control.TableView$TableViewArrayListSelectionModel.select(TableView.java:2485) 
    at javafxapplication17.FXMLDocumentController.lambda$initialize$0(FXMLDocumentController.java:83) 
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182) 
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 
    at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74) 
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102) 
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112) 
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146) 
    at javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:102) 
    at javafx.scene.control.MultipleSelectionModelBase.lambda$new$34(MultipleSelectionModelBase.java:67) 
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137) 
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) 

唯一的例外是很長,所以我就貼了短版。

+1

在這種情況下模態對話框會工作嗎? – trashgod

+1

在昨天晚上睡覺之後,我想出了在任何「TextFields」中進行更改並在提交所有更改後啓用「TableView」後禁用「TableView」。 – Sedrick

+0

你可以[回答你自己的問題](http://meta.stackoverflow.com/q/17463/163188)。 – trashgod

回答

3

基本上,「正確的」方法將是一個自定義的selectionModel,它有一個屬性被禁用,並在該狀態下不執行任何操作。不幸的是,selectionModel不是被設計爲被自定義類擴展/替換的。此外,表格控件中用於選擇的共同祖先是MultipleSelectionModelBase,它既是完全隱藏的,也是極其錯誤的。因此,製作自定義模型的機會是......不太好。然而,它可能是可能的(並且投資足夠的能源和資源甚至可以可靠地工作):實現一個委託給默認實現的自定義TableViewSelectionModel TableViewBitSelectionModel(它從TableView獲取的一個)保持自身同步與此並安裝到桌子上。

喜歡的東西:

public static class VetoableSelection<T> extends TableViewSelectionModel<T> { 

    private boolean disabled; 
    private TableViewSelectionModel<T> delegate; 

    public VetoableSelection(TableView<T> table) { 
     super(table); 
     delegate = table.getSelectionModel(); 
     table.setSelectionModel(this); 
     new VetoableFocusModel<>(table); 
     delegate.selectedIndexProperty().addListener(c -> indexInvalidated()); 
    } 

    /** 
    * keep selectedIndex in sync 
    */ 
    private void indexInvalidated() { 
     setSelectedIndex(delegate.getSelectedIndex()); 
    } 

    /** 
    * Does nothing if disabled. 
    */ 
    public void setDisabled(boolean disabled) { 
     this.disabled = disabled; 
    } 

    public boolean isDisabled() { 
     return disabled; 
    } 

    /** 
    * Override all state changing methods to delegate 
    * if not disabled, do nothing if disabled. 
    * Here: row selection. 
    */ 
    @Override 
    public void clearAndSelect(int row) { 
     if (isDisabled()) return; 
     delegate.clearAndSelect(row); 
    } 

    @Override 
    public void select(int row) { 
     if (isDisabled()) return; 
     delegate.select(row); 
    } 

    /** 
    * Here: methods with columns 
    */ 
    @Override 
    public void clearAndSelect(int row, TableColumn<T, ?> column) { 
     if (isDisabled()) return; 
     delegate.clearAndSelect(row, column); 
    } 
    @Override 
    public void select(int row, TableColumn<T, ?> column) { 
     if (isDisabled()) return; 
     delegate.select(row, column); 
    } 

    ... 

你的榜樣粗檢查似乎是工作的那種,:它不允許選擇改變,如果修改後的文本框都不敢承諾。沒有顯示處於選定狀態的單元格以及動態添加/刪除人員以及...可能具有大量其他上下文的問題。

+0

很好的答案。投票 –