停止ListView所选项目的更改,直到按下按钮 [英] Stop ListView selected item from changing until button pressed

查看:135
本文介绍了停止ListView所选项目的更改,直到按下按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在我想要创建的应用程序中遇到问题。我创建了一个示例应用程序来演示此问题。在应用程序中,我试图停止 TableView 中的所选项目,如果未在 TextField 。在我的实现中,我得到一个 StackOverFlow 错误。我理解为什么我收到错误。我基本上创建了一个无限循环,但我想不出另一种方法来解决这个问题。

So I am having a problem in an app I am trying to create. I have created a sample app to demonstrate the problem. In the app, I am trying to stop the selected item in the TableView from changing if enter is not pressed on a TextField. In my implementation, I am getting a StackOverFlow error. I understand why I am getting the error. I am basically creating an infinite loop, but I can't think of another way to approach this problem.

如果删除这行代码:

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

如果选择表格行然后编辑 TextField 中的文本,然后按 TextField 上的Enter键。但是,如果您选择一个表行,编辑 TextField 并且不按Enter键,则用户可以选择一个新表行而不更新他/她正在尝试的表行编辑。所以我的问题是,如果他/她没有确认 TextField ,如何阻止用户更改 selectedItem 按Enter键进行编辑。

The app works like it's designed if you select a table row and then edit the text in the TextField and press enter on the TextField. Though, if you select a table row, edit the TextField and don't press enter, the user can select a new table row without updating the table row he/she was trying to edit. So my questions, how do I stop the user from changing the selectedItem if he/she hasn't confirmed the TextField edit by pressing enter.


控制器

Controller



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

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 Class

Person Class



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



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)

异常很长,所以我发布了一个简短的版本。

The exception is really long so I on posted a short version.

推荐答案

基本上,正确的方法是自定义selectionModel,它具有要禁用的属性,并且在该状态下不执行任何操作。不幸的是,selectionModels不是为自定义类扩展/替换而设计的。此外,在表格控件中选择的共同祖先是MultipleSelectionModelBase,它既完全隐藏又极其错误。因此,制作自定义模型的机会是......不是很好。

Basically, the "correct" approach would be a custom selectionModel that has a property to be disabled and does nothing in that state. Unfortunately, selectionModels are not designed for being extended/replaced by custom classes. Plus the common ancestor for selection in tabular controls is MultipleSelectionModelBase which is both entirely hidden and extremely buggy. So the chances to make a custom model behave are ... not very good.

尽管如此,它可能(并且投资足够的能量和资源甚至可以可靠地工作;):实现一个自定义的TableViewSelectionModel,它委托默认的实现TableViewBitSelectionModel(一个)它从TableView获取,保持与它同步并将自己安装到表中。

Nevertheless, it might be possible (and with investing enough enery and resources might even work reliably ;): implement a custom TableViewSelectionModel that delegates to the default implementation TableViewBitSelectionModel (the one it grabs from the TableView), keeps itself in synch with that and installs itself to the table.

类似于:

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);
    }

    ... 

与您的粗略支票示例似乎正在工作,有点:如果未提交修改后的textFields,则不允许更改选择。有些问题没有显示处于选定状态的单元格以及动态添加/删除人员......可能还有很多其他情境。

A crude check with your example seems to be working, kind of: it does not allow the selection to change if the modified textFields are not committed. There are problems in not showing the cells in selected state and with dynamic add/remove persons and ... probably with a whole bunch of other contexts.

这篇关于停止ListView所选项目的更改,直到按下按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆