JavaFX TableView: copy text as rendered in cell -


i want implement copy functionality in tableview. text copied should actual text rendered in cell, not .tostring version of data model rendered, is, should .gettext of cell.

there several ways of getting data cell. rendered cell text contents, procedure seems this:

  • get cell data.
  • get cell factory.
  • use factory create cell.
  • use cell's updateitem method render data, gettext rendered text.

the last step not possible due updateitem being protected.

how can access rendered text of given cell in tableview?

the process outline involves getting text (i.e. data) view (the cell), violates principles behind mvc/mvp design. practical perspective, involves creating ui elements (which expensive create) manipulate data (which typically less expensive create , process). additionally, depending on you're doing, ui elements may impose additional threading constraints on code (as single-threaded).

if need use "formatting text" functionality outside of cell, should factor out elsewhere , reuse in both "copy" functionality need , in cell. @ minimum, done making "format text" functionality part of cell factory:

import java.util.function.function;  import javafx.scene.control.tablecell; import javafx.scene.control.tablecolumn; import javafx.util.callback;  public class formattingtablecellfactory<s, t> implements callback<tablecolumn<s, t>, tablecell<s, t>> {      private final function<t, string> formatter ;      public formattingtablecellfactory(function<t, string> formatter) {         this.formatter = formatter ;     }      public formattingtablecellfactory() {         this(t::tostring);     }      public final function<t, string> getformatter() {         return formatter ;     }      @override     public tablecell<s,t> call(tablecolumn<s,t> col) {         return new tablecell<s,t>() {             @override             protected void updateitem(t item, boolean empty) {                 super.updateitem(item, empty);                 settext(item == null ? null : formatter.apply(item));             }         };     } } 

(obviously extend produce more sophisticated cells graphical content, etc.)

and copy functionality can apply formatter data, without reference actual cells. here's sscce:

import java.text.numberformat; import java.util.arraylist; import java.util.list; import java.util.random; import java.util.function.function;  import javafx.application.application; import javafx.beans.binding.bindings; import javafx.beans.property.doubleproperty; import javafx.beans.property.simpledoubleproperty; import javafx.beans.property.simplestringproperty; import javafx.beans.property.stringproperty; import javafx.beans.value.observablevalue; import javafx.geometry.insets; import javafx.geometry.pos; import javafx.scene.scene; import javafx.scene.control.button; import javafx.scene.control.selectionmode; import javafx.scene.control.tablecolumn; import javafx.scene.control.tableview; import javafx.scene.layout.borderpane; import javafx.stage.stage;  public class main extends application {      private string copy(tableview<product> table) {          stringbuilder sb = new stringbuilder();         (product p : table.getselectionmodel().getselecteditems()) {             list<string> data = new arraylist<>();             (tablecolumn<product, ?> column : table.getcolumns()) {                 function<object, string> formatter = ((formattingtablecellfactory) column.getcellfactory()).getformatter();                 data.add(formatter.apply(column.getcellobservablevalue(p).getvalue()));             }             sb.append(string.join("\t", data)).append("\n");         }         return sb.tostring() ;     }      @override     public void start(stage primarystage) {         tableview<product> table = new tableview<>();         table.getselectionmodel().setselectionmode(selectionmode.multiple);          table.getcolumns().add(column("product", product::nameproperty, string::tostring));         numberformat currencyformat = numberformat.getcurrencyinstance();         table.getcolumns().add(column("price", product::priceproperty, currencyformat::format));          random rng = new random();         (int = 1; <= 100; i++) {             table.getitems().add(new product("product "+i, rng.nextdouble()*100));         }          button copy = new button("copy");         copy.setonaction(e -> system.out.println(copy(table)));         copy.disableproperty().bind(bindings.isempty(table.getselectionmodel().getselecteditems()));          borderpane root = new borderpane(table);         borderpane.setalignment(copy, pos.center);         borderpane.setmargin(copy, new insets(10));         root.setbottom(copy);         scene scene = new scene(root, 600, 600);         primarystage.setscene(scene);         primarystage.show();     }       private static <s,t> tablecolumn<s,t> column(string title, function<s,observablevalue<t>> property, function<t,string> formatter) {         tablecolumn<s,t> col = new tablecolumn<>(title);         col.setcellvaluefactory(celldata -> property.apply(celldata.getvalue()));         col.setcellfactory(new formattingtablecellfactory<>(formatter));         return col ;     }      public static class product {         private final stringproperty name = new simplestringproperty();         private final doubleproperty price = new simpledoubleproperty() ;          public product(string name, double price) {             setname(name);             setprice(price);         }          public final stringproperty nameproperty() {             return this.name;         }           public final string getname() {             return this.nameproperty().get();         }           public final void setname(final string name) {             this.nameproperty().set(name);         }           public final doubleproperty priceproperty() {             return this.price;         }           public final double getprice() {             return this.priceproperty().get();         }           public final void setprice(final double price) {             this.priceproperty().set(price);         }        }      public static void main(string[] args) {         launch(args);     } } 

you can rid of less typesafe code @ expense of less flexibility:

private final function<string, string> defaultformatter = function.identity() ; private final function<number, string> priceformatter = decimalformat.getcurrencyinstance()::format  ;  private string copy(tableview<product> table) {     return table.getselectionmodel().getselecteditems().stream().map(product ->          string.format("%s\t%s",                  defaultformatter.apply(product.getname()),                 priceformatter.apply(product.getprice()))     ).collect(collectors.joining("\n")); } 

and

    table.getcolumns().add(column("product", product::nameproperty, defaultformatter));     table.getcolumns().add(column("price", product::priceproperty, priceformatter)); 

Comments

Popular posts from this blog

javascript - jQuery: Add class depending on URL in the best way -

caching - How to check if a url path exists in the service worker cache -

Redirect to a HTTPS version using .htaccess -