Выбор в поле со списком исчезает при отмене редактирования таблицы

Хорошо, я начинаю сходить с ума по этому поводу. У меня есть табличное представление, в котором есть 3 ячейки таблицы со списком. Первое — это поле, в котором пользователь может выбрать задание, выбранное задание изменяет параметры следующего списка (категория задания). Выбор категории работы изменяет параметры в поле труда. Таким образом, поток вниз:

работа > категория работы > труд.

У меня очень своеобразная проблема. При редактировании таблицы вы можете щелкнуть любое поле, чтобы получить соответствующий список доступных вариантов выбора на основе других полей. Это прекрасно работает. Где это взрывается, когда выбор НЕ сделан. Чтобы сделать вещи более интересными, это влияет только на поля со списком работы и категории работы, поле труда работает безупречно.

симптом:

-- выбор категории задания исчезает при отмене редактирования через esc или при потере фокуса

-- выделение, выбранное в поле категории задания, помещается в поле задания при отмене редактирования через esc или при потере фокуса

Вот шаги, чтобы воссоздать симптомы:

1) нажмите на поле категории работы и включите режим редактирования

2) сделать новый выбор из выпадающего списка

сделан новый выбор

3) нажмите на поле задания и включите редактирование

4) щелкните поле задания и отмените редактирование, щелкнув категорию задания или поле труда в той же строке.

5) включите редактирование категории вакансий, а затем отмените редактирование категории вакансий, щелкнув поля труда / труда или нажав клавишу esc.

потерять изображение категории задания/выбора задания

вот код для инициализации графики, когда она появляется:

public void initialize(URL location, ResourceBundle resources) {

    /* this is here because the screen handler will load up the Main screen in the
    in the hashmap; no connection data will be assigned to the user at that time.
    Without this block, when the hashmap attempts to load the Main data this
    will cause the screenhandler to error and the main application
    to not load correctly.  The block below initiatializes the connection to
    prevent this from happening.*/

    if ( vUsers.getConn() == null){
        try {
            //establishes a user's connection to the database
            vUsers.ConnecrDB();

        } catch (SQLException | IOException ex) {
            //debugging catch
            System.out.println(ex);
        }
    }

    //set the job box list for the user
    cmbxJobT.setItems(cmbxPopulator.getJobComboBox());

    cmbxJobT.valueProperty().addListener(new ChangeListener<String>(){
        @Override
        //reads the user's selectino and returns the appropriate labor codes for the Employee
        public void changed(ObservableValue o, String oldValue, String newValue){
            if (newValue != null){
                cmbxJobCatT.getItems().clear();
                cmbxJobCatT.getItems().addAll(cmbxPopulator.getJobCatComboBox(newValue));
            }else {
                cmbxJobCatT.getItems().clear();
                cmbxJobCatT.getItems().add(null);
            }
        }
    });

    cmbxJobCatT.valueProperty().addListener(new ChangeListener<String>(){
        @Override
        //reads the user's selectino and returns the appropriate labor codes for the Employee
        public void changed(ObservableValue o, String oldValue, String newValue){
            if (newValue != null){
                cmbxLaborT.getItems().clear();
                cmbxLaborT.getItems().addAll(cmbxPopulator.getLaborComboBox(newValue));
            }else {
                cmbxLaborT.getItems().clear();
                cmbxLaborT.getItems().add(null);
            }
        }
    });

    tblviewTime.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSel) ->{
        if (newSel != null){

            Model_Time current = tblviewTime.getSelectionModel().getSelectedItem();

            cmbxJobCatT.getItems().clear();
            cmbxJobCatT.getItems().addAll(cmbxPopulator.getJobCatComboBox(current.getJob()));

            cmbxLaborT.getItems().clear();                
            cmbxLaborT.getItems().addAll(cmbxPopulator.getLaborComboBox(current.getJobCat()));              
        }

        if (newSel == null){

             Model_Time current = tblviewTime.getSelectionModel().getSelectedItem();

            cmbxJobCatT.getItems().clear();
            cmbxJobCatT.getItems().addAll(cmbxPopulator.getJobCatComboBox(current.getJob()));

            cmbxLaborT.getItems().clear();                
            cmbxLaborT.getItems().addAll(cmbxPopulator.getLaborComboBox(current.getJobCat()));

        }
    });
    addDragListeners(bertaTabPane);
}

вот код, который настраивает табличное представление:

public void btnTimeSearch(ActionEvent event){


   //makes an instance of the toolkit needed to query user time.
   Database_RetrievesTime userData = new Database_RetrievesTime();

   //grabs data from the userinput fields to set the toolkit
   userData.setDateSelect(lblPickDateT.getValue());  
   userData.setJobBoxSelect(jobTbl.getIdByDesc(cmbxJobT.getValue()));
   userData.setLaborBoxSelect(laborTbl.getIdByDesc(cmbxLaborT.getValue()));
   userData.setJobCatSelect(jobCatTbl.getIdByDesc(cmbxJobCatT.getValue()));

    /*creates cell factories in each column and maps the cell values to the
    observable array list's IDs. The section also sets the columns up for user
    editing to be available and the methods to execute upon an editted cell
    being committed to entry.

    **NOTE:  The values are retrieved by the model class's getter methods. 
    Changing a name in the model class requires the user to update the getters.  
    Naming convention does apply.  So for example: a variable
    named cscHelp is added, it would need to have a getter called getCscHelp otherwise
    the corresponding column will return blanks.*/

    //setup ID column
    IDcol.setCellValueFactory(new PropertyValueFactory<>("ID"));

    //setup Datecol
    Datecol.setCellValueFactory(new PropertyValueFactory<>("userDate"));
    Datecol.setCellFactory(DatePickerTableCell.forTableColumn());
    //created a custom datepicker callback that can be reused throughout the code's interfaces
    Datecol.setOnEditCommit((CellEditEvent<Model_Time,LocalDate> t) -> {

            //generate a temporary variable to convert the LocalDate returned into a SimpleObjectProperty
            ObjectProperty<LocalDate> temp = new SimpleObjectProperty(t.getNewValue());
            //store the new value to the object's model
            t.getRowValue().setUserDate(temp);

            //store row's object to the change list
            Helper_TimShArrGen.addToEditedMatrix(t.getRowValue());
            //signaling to the program that a change had been made
            isChanged = true;
    });

    //job column setup
    Jobcol.setCellValueFactory(new PropertyValueFactory<>("Job"));
    Jobcol.setCellFactory(ComboBoxTableCell.forTableColumn(cmbxPopulator.getJobComboBox()));
    /*creates a combobox filled with the populated items found at initialization of the screen
    user inputs are automatically commited
    */

    Jobcol.setOnEditCommit((CellEditEvent<Model_Time,String> t) -> {

            SimpleStringProperty ssp = new SimpleStringProperty(t.getNewValue());
            //store selection to the object's model (unprocessed so values will show something like '6002: Kobota'
            t.getRowValue().setJob(ssp);
            //store row's object to the change list
            Helper_TimShArrGen.addToEditedMatrix(t.getRowValue());
            cmbxJobCatT.getItems().clear();
            cmbxJobCatT.getItems().addAll(cmbxPopulator.getJobCatComboBox(t.getRowValue().getJob()));
            //signaling a change has been made
            isChanged = true;
    });


    jobCatCol.setCellValueFactory(new PropertyValueFactory<>("JobCat"));
    jobCatCol.setCellFactory(ComboBoxTableCell.forTableColumn(cmbxPopulator.getJobCatComboBox(cmbxJobT.getValue())));
    jobCatCol.setOnEditCommit((CellEditEvent<Model_Time,String> t)->{

             SimpleStringProperty ssp = new SimpleStringProperty(t.getNewValue());
            //store selection to the object's model (unprocessed so values will show something like '6002: Kobota'
            t.getRowValue().setJob(ssp);
            //store row's object to the change list
            Helper_TimShArrGen.addToEditedMatrix(t.getRowValue());
            cmbxLaborT.getItems().clear();
            cmbxLaborT.getItems().addAll(cmbxPopulator.getLaborComboBox(t.getRowValue().getJob()));
            //signaling a change has been made
            isChanged = true;

    });

    //labor column setup works just like the job column
    Laborcol.setCellValueFactory(new PropertyValueFactory<>("Labor"));
    Laborcol.setCellFactory(ComboBoxTableCell.forTableColumn(cmbxPopulator.getLaborComboBox(cmbxJobCatT.getValue())));   
    Laborcol.setOnEditCommit((CellEditEvent<Model_Time,String> t) -> {

        SimpleStringProperty ssp = new SimpleStringProperty(t.getNewValue());
        t.getRowValue().setLabor(ssp);    
        Helper_TimShArrGen.addToEditedMatrix(t.getRowValue());
        t.getTableView().getItems().get(t.getTablePosition().getRow()).setLabor(ssp);
        isChanged = true;
    });

    //time column setup.  
    Timecol.setCellValueFactory(new PropertyValueFactory<>("Time"));
    Timecol.setCellFactory(TextFieldTableCell.<Model_Time, Float>forTableColumn(new FloatStringConverter()));
    Timecol.setOnEditCommit((CellEditEvent<Model_Time,Float> t) -> {

        //temp variable initialiation
        float token = t.getNewValue();
        //generate a temporary variable to convert the float return to a SimpleFloatProperty
        SimpleFloatProperty temp = new SimpleFloatProperty(token);
        //now update the row's object
        t.getRowValue().setTime(temp);
        //store row's object to the change list
        Helper_TimShArrGen.addToEditedMatrix(t.getRowValue());
        isChanged = true;
    });

    //set tableView editable
    tblviewTime.setEditable(true);

    //sets tableView to allow multiline selection
    TableViewSelectionModel<Model_Time> tvt = tblviewTime.getSelectionModel();
    tvt.setSelectionMode(SelectionMode.MULTIPLE); 

    /* checks if edits have been made.  If there are edits, it commits to the 
    database before wiping the arraylists and updating the table*/
    if (isChanged == true){
        /*makes sure there are no duplicate entries in the arraylist.  Throws out
        previous edits and takes the most recent*/
        Helper_TimShArrGen.validateMatrixEntries();
        //commits changes and resets the "isChanged" value.
        isChanged = Helper_TimShArrGen.confirmChanges(vUsers, jobTbl, jobCatTbl, laborTbl, isChanged);
    }

    /*tells the kit to run the querytime Method which uses the user input data
    and user data to search the timesheet tables and returns the user's time.*/
    if (isChanged == false){
        try {
            rs = userData.queryTime(vUsers.getConn(), vUsers.getLogin_ID());
        } catch (SQLException ex) {
            System.out.println(ex);
        }
    } else {
        return;
    }

    //sets up the observablelist for the tableview
    renderTable.setTableView(tblviewTime);
    ObservableList n = renderTable.generateTable(jobTbl, jobCatTbl, laborTbl, rs, vUsers.getConn());

    //renders the data on the screen`
    tblviewTime.setItems(n);


    Helper_TimeBreakDown breakdown = new Helper_TimeBreakDown();
    breakdown.setTblArr(n);
    breakdown.BreakDwnTim();
    lblMonTim.setText(String.valueOf(breakdown.getMon()));
    lblTuesTim.setText(String.valueOf(breakdown.getTues()));
    lblWedsTim.setText(String.valueOf(breakdown.getWeds()));
    lblThursTim.setText(String.valueOf(breakdown.getThurs()));
    lblFriTim.setText(String.valueOf(breakdown.getFri()));
    lblSatTim.setText(String.valueOf(breakdown.getSat()));
    lblSunTim.setText(String.valueOf(breakdown.getSun()));
    lblWkTim.setText(String.valueOf(breakdown.getWeek()));
}

это сегмент из класса, который обрабатывает данные, поступающие в списки.

public ObservableList<String> getJobComboBox(){

    //clear the jobList to clean out junk data between calls
    jobList.clear();

    //looks at the job table to determine if the job is active. If active, it reads the entry
    for (count=0; count<= jobTbl.getTblArray().size()-1; count++){
       if(jobTbl.getTblArray().get(count).isActive()){ 

           /*here we scroll through the JobIDList and match the IDs to the jobtbl
           data.  When a match is hit, we grab up the number on the job and the description
           this is added to another array that will become the combobox's list
           */
           for(inCount = 0; inCount <= jobIDList.size()-1; inCount++){
               if(jobIDList.get(inCount).getCol2ID() == jobTbl.getTblArray().get(count).getID()){
                   jobList.add(jobTbl.getNumById(jobIDList.get(inCount).getCol2ID()) 
                           + ": " + jobTbl.getDescById(jobIDList.get(inCount).getCol2ID()));
               }
           }
       }
    }

    /* there's probably a better dataset to use that won't allow duplicates
    due to my lack of knowledge at this time, I elected to create a hashset, pass the arrayList
    to the hashset to wipe out duplicates, and then pass it back to the arrayList to be used in the combobox
    */
   Set<String> tmp = new HashSet();

   tmp.addAll(jobList);
   jobList.clear();
   jobList.addAll(tmp);
   jobList.add(null);

    return jobList;
}

/**
 * 
 * @param job Argument for the selected Job String
 * @return returns the ObservableList of strings for the Job Category ComboBox 
 */
public ObservableList<String> getJobCatComboBox(String job){

    //clearing out old artifact data from the previous selection
    jobCatList.clear();

    //splitting the user's string selection apart (number as string, description as string)
    int jID=0;
    if (job != null){
        if (job.contains(": ")){
            String[] tmp = job.split(": ");
            job = tmp[1];
        }
    }

    // here we comb the job Table for a matching description and vacuum up the associated ID number
    for(count=0; count<=jobTbl.getTblArray().size()-1; count++){
        if(jobTbl.getTblArray().get(count).getDesc().equals(job)){
            jID = jobTbl.getTblArray().get(count).getID();
        }
    }

    /*using that jobID number to examine the fKey in the category table.
    once we match the JobID to the fKey ID in the jobCat table, we scoop up the
    the job Category code and description to create a list for the combobox
    */
    for (count=0; count<= jobCTbl.getTblArray().size()-1; count++){  
        for(inCount = 0; inCount <= jobCatIDList.size()-1; inCount++){

            if(jobCatIDList.get(inCount).getID() == jobCTbl.getTblArray().get(count).getID()){
                if(jobCTbl.getTblArray().get(count).getfKeyId() == jID){
                    jobCatList.add(jobCTbl.getNumById(jobCatIDList.get(inCount).getID()) 
                        + ": " + jobCTbl.getDescById(jobCatIDList.get(inCount).getID()));
                }
            }
        }
    }

    //same house keeping to remove duplicates as described above
    Set<String> tmp = new HashSet();
    tmp.addAll(jobCatList);
    jobCatList.clear();
    jobCatList.addAll(tmp);
    jobCatList.add(null);

    return jobCatList;
}

/**
 * 
 * @param jobCat Argument for the selected Job Category String
 * @return ObservableList of strings for the Labor ComboBox
 */
public ObservableList<String> getLaborComboBox(String jobCat){

    //temp arrays I needed to decode the affiliated connections
    ArrayList<Integer> jCID = new ArrayList();
    ArrayList<Integer> laborID = new ArrayList();

    //house keeping to remove artifact data from previous selections
    if(jCID != null){
    jCID.clear();
    }

    if(laborID != null){
    laborID.clear();
    }

    if(laborList !=null){
        laborList.clear();
    }

    //split user's string selection for the job category (numerical code as sting, description as string)
    if(jobCat != null){
        if (jobCat.contains(": ")){
            String[] tmp = jobCat.split(": ");
            jobCat = tmp[1];
        }
    }
    //use the description to find the affiliated job category ID
    for(count=0; count<=jobCTbl.getTblArray().size()-1; count++){
        if(jobCTbl.getTblArray().get(count).getDesc().equals(jobCat)){
            jCID.add(jobCTbl.getTblArray().get(count).getID());
        }
    }

    //use the job category ID to find the associated labor IDs from the associate entity table 
    for(count=0; count<=jCLTbl.getTblArray().size()-1; count++){
        for(inCount=0; inCount<=jCID.size()-1; inCount++){
            if(jCLTbl.getTblArray().get(count).getCol1ID() == jCID.get(inCount)){
                laborID.add(jCLTbl.getTblArray().get(count).getCol2ID());
            }     
        }
    }

    //use the labor ID to look up the needed data from the labor table.
    for (count=0; count<= laborTbl.getTblArray().size()-1; count++){
        for(inCount = 0; inCount <= laborID.size()-1; inCount++){
            if(laborID.get(inCount) == laborTbl.getTblArray().get(count).getID()){
                    laborList.add(laborTbl.getNumById(laborID.get(inCount)) 
                        + ": " + laborTbl.getDescById(laborID.get(inCount)));
                }
            }
        }

    //more housekeeping to remove duplicate entries.
    Set<String> tmp = new HashSet();
    tmp.addAll(laborList);
    laborList.clear();
    laborList.addAll(tmp);
    laborList.add(null);

    return laborList;
}   

Я знаю, что, вероятно, здесь есть лучшие способы сделать что-то. Я все еще новичок в JAVA и кодировании. Я работаю в небольшой компании, поэтому я единственный кодер, который у них работает. К сожалению, это ограничивает меня в том, чему я могу научить себя, поэтому, вероятно, здесь есть несколько более эффективных способов выполнить ту же миссию. В любом случае, если кто-нибудь может помочь мне понять, что происходит, это будет оценено.


person Tom L'esperance    schedule 17.02.2016    source источник
comment
Вы видите какое-либо сообщение об ошибке, когда он взрывается?   -  person lrnzcig    schedule 17.02.2016
comment
вообще ничего, что делает его еще более двусмысленным. Программа не видит в этом проблем.   -  person Tom L'esperance    schedule 17.02.2016


Ответы (1)


На самом деле ответ на эту проблему можно найти в части SetOnEdit файла jobcatCol.

при ближайшем рассмотрении и многократных отладках я обнаружил, что эта строка

 t.getRowValue().setJob(ssp);

на самом деле должно быть

 t.getRowValue().setJobCat(ssp);

Я также решил отказаться от ComboBoxTableCell API и написать свои собственные реализации выпадающего списка для каждой ячейки. Это очень простая версия, найденная здесь:

public class JobComboBoxCell extends TableCell<Model_Time, String>
{

    private ComboBox<String> comboBox;
    private Helper_UserSpecificTimDat cmbxPopulator = new Helper_UserSpecificTimDat();

    public JobComboBoxCell(Helper_UserSpecificTimDat cmbxPopulator)
    {
        comboBox = new ComboBox<>();
        this.cmbxPopulator = cmbxPopulator;
    }


    @Override
    public void startEdit()
    {
        if ( !isEmpty() )
        {
            super.startEdit();


            comboBox.setItems( cmbxPopulator.getJobComboBox() );
            comboBox.getSelectionModel().select( this.getItem() );

            comboBox.focusedProperty().addListener( new ChangeListener<Boolean>()
            {
                @Override
                public void changed( ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue )
                {
                    if ( !newValue )
                    {
                        commitEdit( comboBox.getSelectionModel().getSelectedItem() );
                    }
                }
            } );

            setText( null );
            setGraphic( comboBox );
        }
    }


    @Override
    public void cancelEdit()
    {
        super.cancelEdit();

        setText( ( String ) this.getItem() );
        setGraphic( null );
    }


    @Override
    public void updateItem( String item, boolean empty )
    {
        super.updateItem( item, empty );

        if ( empty )
        {
            setText( null );
            setGraphic( null );
        }
        else
        {
            if ( isEditing() )
            {
                setText( null );
                setGraphic( comboBox );
            }
            else
            {
                setText( this.getItem() );
                setGraphic( null );
            }
        }
    }

}

Этот код был предоставлен ответом Улука Бия в ветке: Динамическое заполнение списка полей со списком для каждой строки в табличном представлении javaFx

Я немного адаптировал его код, чтобы он соответствовал моим потребностям. Это решило проблему с исчезновением значений из поля категории должностей и переносом данных из поля категории должностей в поле Должность.

Это был один из самых сложных фрагментов кода, которые мне приходилось устранять. Это была комбинация того, как работает ComboBoxTableCell, динамические данные моих полей со списком и ошибка в том, какой сеттер я вызывал. Речь шла об идеальном шторме ляпов, для которых в результате программа не сделала исключений.

Спасибо вам за помощь

person Tom L'esperance    schedule 18.02.2016