listview - javafx Tableview cell index/editing bug with duplicated entries -


i've been messing around javafx bit. , came across cannot realy explain. trying create editable tableview string values. , worked, till started biger tests. before tested small lists, didn't exceed list's visible limit. though big test, list far more bigger show in window.

the problem give amount of cells @ begin of tableview collour, works perfectly, till start scrolling down. seems re-used cells aren't updated fast enough, since have been given wrong colour. when scrolling down, , updating more cells, patern of 'wrong coloured cells' changes well. interesting part is, these 'fails' consistent!

(btw, im checking cells index using this.getindex() after call this.setstyle() proper colour syntax.)

the second thing noticed, when start editing cell, though dont stop editing , scroll down. there multiple cells being edited @ same time. (also in consistent patern) , not visual glitch!, have debugged it, , seems editing property isn't updated enough either.

one last side note, update tablecells' graphics in cell#updateitem() method. (and editing part in cell#startedit() , cell#canceledit() well.)

my question is, bug? , if so, there workaround? please leave comment if have idea's/suggestions/solutions.

thanks in advance!


edit 01:

first need appoligize, since problem ocuring tableview, not listview. being said, made example class displays exactually problem tried describe. if there suggestions/solutions, please let me know!

package testpacket;  import java.util.arrays;  import javafx.application.application; import javafx.collections.fxcollections; import javafx.collections.observablelist; import javafx.scene.scene; import javafx.scene.control.tablecell; import javafx.scene.control.tablecolumn; import javafx.scene.control.tableview; import javafx.scene.layout.anchorpane; import javafx.stage.stage;   public class tableviewtest extends application {     private static observablelist<string> examplelist = fxcollections.observablearraylist();     private static string[] testarray = new string[50];      public static void main(string[] args)     {         launch(args);     }      @override     public void start(stage primarystage) throws exception     {         // basic ui setup         anchorpane parent = new anchorpane();         scene scene = new scene(parent);         primarystage.setscene(scene);          //fill backinglist data         arrays.fill(testarray, 0, testarray.length, "test string");         examplelist.setall(arrays.aslist(testarray));          //create basic tableview         tableview<string> listview = new tableview<string>();         tablecolumn<string, string> column = new tablecolumn<string, string>();         column.setcellfactory(e -> new tablecelltest<string, string>());         column.setcellvaluefactory(e -> new simplestringproperty(e.getvalue()));          // set listviews' backing list         listview.getitems().addall(examplelist);         // listview.setitems(examplelist); doesnt rely on backing list, either way showing bug.           listview.getcolumns().clear();         listview.getcolumns().add(column);         parent.getchildren().add(listview);          primarystage.show();     }      public static class tablecelltest<s, t> extends tablecell<s, t>     {          @override         protected void updateitem(t item, boolean empty)         {             super.updateitem(item, empty);              // dipslays cells' value             this.settext((string)this.getitem());              // checks cells index , set color.             if(this.getindex() < 12)                 this.setstyle("-fx-background-color: rgba(253, 255, 150, 0.4);");             else this.setstyle("");         }     } } 

edit 02:

@james_d

many answer, indeed duplicated items problem. somehow javafx checking whether content has changed or not. unfortunatly solution:
testarray[i] = new string("test string");
not work, quit unfortunate because system depends heavily on duplicated entries. trying make them unique impossible handle perfectly.

so hope there possibility overwrite 'is equal check' somehow. have been diging bit, , seems methods/fields private. changing kinda impossible do, unless recreate bunch of javafx classes.

so please if there still suggestion/solutions one, feel free post them!

onto next problem though, editing property isnt updated enough either. in onupdateitem() method check if current cell editing , keep updating cell stays in editing mode, if scroll down (and scrolling editing index out of window). made example can see yourself.

note when remove onupdateitem() part of editing system, cell exit edit mode after has been scrolled outside viewwindow.

package testpacket;  import java.util.arrays;  import javafx.application.application; import javafx.beans.property.simplestringproperty; import javafx.collections.fxcollections; import javafx.collections.observablelist; import javafx.event.eventhandler; import javafx.scene.scene; import javafx.scene.control.tablecell; import javafx.scene.control.tablecolumn; import javafx.scene.control.tableview; import javafx.scene.control.textfield; import javafx.scene.input.keycode; import javafx.scene.input.keyevent; import javafx.scene.layout.anchorpane; import javafx.stage.stage;   public class tableviewtest extends application {     private static observablelist<string> examplelist = fxcollections.observablearraylist();     private static string[] testarray = new string[50];      public static void main(string[] args)     {         launch(args);     }      @override     public void start(stage primarystage) throws exception     {         // basic ui setup         anchorpane parent = new anchorpane();         scene scene = new scene(parent);         primarystage.setscene(scene);          //fill backinglist data         for(int = 0 ; < testarray.length; i++)             testarray[i] = "test string" + i;  //      arrays.fill(testarray, 0, testarray.length, new string("test string"));         examplelist.setall(arrays.aslist(testarray));          //create basic tableview         tableview<string> listview = new tableview<string>();         tablecolumn<string, string> column = new tablecolumn<string, string>();         column.setcellfactory(e -> new tablecelltest<string, string>());         column.setcellvaluefactory(e -> new simplestringproperty(e.getvalue()));         column.seteditable(true);          // set listviews' backing list         listview.getitems().addall(examplelist);         listview.seteditable(true); //      listview.setitems(examplelist); doesnt rely on backing list, either way showing bug.           listview.getcolumns().clear();         listview.getcolumns().add(column);         parent.getchildren().add(listview);          primarystage.show();     }      public static class tablecelltest<s, t> extends tablecell<s, t>     {          protected textfield textfield;          @override         public void startedit()         {             super.startedit();              if (!iseditable()                     || (this.gettableview() != null && !this.gettableview().iseditable())                     || (this.gettablecolumn() != null && !this.gettablecolumn().iseditable()))                 return;              if(this.textfield == null)                 this.createtextfield();              this.textfield.settext((string)this.getitem());             this.setgraphic(this.textfield);             this.textfield.selectall();             this.settext(null);         }          @override         public void canceledit()         {             super.canceledit();             if (!isediting())                 return;              this.settext((string)this.getitem());             this.setgraphic(null);         }          @override         protected void updateitem(t item, boolean empty)         {             super.updateitem(item, empty);              if(empty || item == null)             {                 this.settext(null);                 this.setgraphic(null);             }             else             {                 // tried option check whether cell represents correct item, though doesnt work                 if(this.isediting())// && this.gettableview().geteditingcell() != null && this.gettableview().geteditingcell().equals(this))                 {                     if(this.textfield != null)                         this.textfield.settext((string)this.getitem());                     this.settext(null);                     this.setgraphic(this.textfield);                 }                 else                 {                     this.settext((string)this.getitem());                     this.setgraphic(null);                 }             }         }          @suppresswarnings("unchecked")         public void createtextfield()         {             this.textfield = new textfield();             this.textfield.setonkeyreleased((eventhandler<? super keyevent>)e -> {                 if(e.getcode() == keycode.enter)                 {                     this.setitem((t)this.textfield.gettext());                     this.commitedit(this.getitem());                 }                 else if(e.getcode() == keycode.escape)                     this.canceledit();             });         }     } } 

edit 03:

unlike mentioned in class, there workaround one. note works 'single cell editing mode'! (as mode supported tableview) solution follows:

if(this.isediting() && this.gettableview().geteditingcell() != null      && this.gettableview().geteditingcell().gettablecolumn().getcelldata(this.gettableview().geteditingcell().getrow()).equals(this.getitem())) {     ... stuff } 

though unfortunatly doesn't solve index problem. thread still open suggestions/solutions.

the highlighting problem arises because have identical elements in table: i.e. have table.getitems().get(0)==table.getitems().get(1) etc. if change table initialization items not identical (but still equal):

// arrays.fill(testarray, 0, testarray.length, "test string"); (int = 0 ; < testarray.length; i++)      testarray[i] = new string("test string"); 

then highlighting works expected.

to persist editing state during scrolling (including case editing cell scrolled out of view), need careful distinguish between cells , items. want persist item being edited: since cell may reused during scrolling means need way of keeping track of item being edited during scrolling; in updateitem(...) method need check , "go editing mode" if necessary. easiest way track item being edited using row index.

the following example shows way this:

import java.util.arrays;  import javafx.application.application; import javafx.beans.property.simplestringproperty; import javafx.collections.fxcollections; import javafx.collections.observablelist; import javafx.scene.scene; import javafx.scene.control.contentdisplay; import javafx.scene.control.tablecell; import javafx.scene.control.tablecolumn; import javafx.scene.control.tableview; import javafx.scene.control.textfield; import javafx.scene.input.keycode; import javafx.scene.input.keyevent; import javafx.scene.layout.anchorpane; import javafx.stage.stage; import javafx.util.callback;   public class tableviewtest extends application {      public static void main(string[] args)     {         launch(args);     }      @override     public void start(stage primarystage) throws exception     {         // basic ui setup         anchorpane parent = new anchorpane();         scene scene = new scene(parent);         primarystage.setscene(scene);            //create basic tableview         tableview<string> table = new tableview<string>();         table.seteditable(true);         tablecolumn<string, string> column = new tablecolumn<string, string>();         column.setcellfactory(new cellfactory());         column.setcellvaluefactory(celldata -> new simplestringproperty(celldata.getvalue()));          column.setoneditcommit(e -> {             int row = e.gettableposition().getrow() ;             table.getitems().set(row, e.getnewvalue());         });          (int = 0 ; < 50; i++) {             table.getitems().add(new string("test"));         }          table.getcolumns().add(column);         parent.getchildren().add(table);          primarystage.show();     }      public static class cellfactory implements callback<tablecolumn<string, string>, tablecell<string, string>> {          private int editingindex = -1 ;          @override         public tablecell<string, string> call(tablecolumn<string, string> param) {             return new tablecell<string, string>() {                  private textfield textfield = new textfield() ;                  {                     textfield.setonaction(e -> commitedit(textfield.gettext()));                     textfield.addeventfilter(keyevent.key_pressed, e -> {                         if (e.getcode() == keycode.escape) {                             canceledit();                         }                     });                     setgraphic(textfield);                     setcontentdisplay(contentdisplay.text_only);                 }                  @override                 protected void updateitem(string item, boolean empty)                 {                     super.updateitem(item, empty);                      if (getindex() != -1 && getindex() == editingindex) {                         setcontentdisplay(contentdisplay.graphic_only);                     } else {                         settext(empty ? null : item);                         setcontentdisplay(contentdisplay.text_only);                     }                      // checks cells index , set color.                     if(this.getindex() >= 0 && getindex() < 12)                         this.setstyle("-fx-background-color: rgba(253, 255, 150, 0.4);");                     else this.setstyle("");                 }                  @override                 public void startedit() {                     editingindex = getindex();                     super.startedit();                     textfield.settext(getitem());                     setcontentdisplay(contentdisplay.graphic_only);                 }                  @override                 public void commitedit(string newvalue) {                     editingindex = -1 ;                     super.commitedit(newvalue);                     setcontentdisplay(contentdisplay.text_only);                 }                  @override                 public void canceledit() {                     settext(gettableview().getitems().get(editingindex));                     editingindex = -1 ;                     super.canceledit();                     setcontentdisplay(contentdisplay.text_only);                 }              };         }      }  } 

Comments