代码之家  ›  专栏  ›  技术社区  ›  SedJ601

在按下按钮之前,停止更改ListView所选项目

  •  2
  • SedJ601  · 技术社区  · 7 年前

    所以我在尝试创建的应用程序中遇到了问题。我创建了一个示例应用程序来演示这个问题。在应用程序中,我试图在 TableView 如果未按键盘上的enter键,则不会更改 TextField StackOverFlow 错误我理解为什么我会出错。我基本上是在创建一个无限循环,但我想不出其他方法来解决这个问题。

    如果删除这行代码:

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

    如果你选择一个表格行,然后在 文本字段 . 但是,如果选择表行,请编辑 文本字段 不要按enter键,用户可以选择新的表行,而无需更新他/她试图编辑的表行。那么我的问题是,如何阻止用户更改 selectedItem 如果他/她没有确认 文本字段 按enter键进行编辑。

    控制器

    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>
    

    人员类别

    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 回复  |  直到 7 年前
        1
  •  4
  •   kleopatra Aji kattacherry    7 年前

    基本上,“正确”的方法是自定义selectionModel,它有一个要禁用的属性,并且在该状态下不做任何事情。不幸的是,selectionModels不是为被自定义类扩展/替换而设计的。此外,在表格控件中选择的共同祖先是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);
        }
    
        ... 
    

    对您的示例进行粗略检查似乎很有效:如果未提交修改的文本字段,则不允许更改选择。在不显示处于选定状态的单元格以及动态添加/删除人员和。。。可能还有很多其他的背景。