Skip to content

Instantly share code, notes, and snippets.

@NetzwergX
Last active November 12, 2020 10:58
Show Gist options
  • Save NetzwergX/9f77750a1a50ad14d21dcfadbc02d69d to your computer and use it in GitHub Desktop.
Save NetzwergX/9f77750a1a50ad14d21dcfadbc02d69d to your computer and use it in GitHub Desktop.
Various JavaFX Table/ListCell implementations with support for custom formatters

This Gist contains a few of the TableCell and ListCells with factories I have written for JavaFX.

DatePickerTableCell

column.setCellFactory(
  column -> new DatePickerTableCell<Person>(
    DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));

FormattedListCell

personList.setCellFactory(
 FormattedListCell.getListCellFactory("%s", Person::getName));
/*
* Copyright (c) 2017 Sebastian Teumert (<http://teumert.net>)
* Some rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.teumert.javafx.scene.control.cell;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;
/**
* <p>DatePicker implementation for use inside JavaFX table cells.</p>
*
* <p>Allows setting of a formatter to allow localized display of dates.</p>
*
* <p>
* <b>Example</b>
* </p>
* <pre>
* {@code
* column.setCellFactory(
* column -> new DatePickerTableCell<Person>(
* DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
* }
* </pre>
*
* @author Sebastian Teumert
*/
public class DatePickerTableCell<S> extends TableCell<S, LocalDate> {
private DatePicker picker;
private DateTimeFormatter formatter;
public DatePickerTableCell () {
this (null);
}
/**
* Creates a new table cell with date picker
* @param formatter A {@link DateTimeFormatter} that can convert the selected
* LocalDate (from what the user typed in) into an string with the given format.
*/
public DatePickerTableCell (DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public void startEdit () {
if (!isEditable()
|| !getTableView().isEditable()
|| !getTableColumn().isEditable()) { return; }
super.startEdit();
if (isEditing()) {
if (picker == null) {
picker = new DatePicker();
// ATTENTION: Value MUST be set before the change listener(!)
picker.setValue(this.getItem());
// stop editing when loosing focus
// (e.g. when another cell is clicked)
picker.focusedProperty().addListener(
(observable, oldValue, newValue) -> {
if (!newValue)
cancelEdit();
}
);
// make sure events propagate back through to the data model
picker.valueProperty().addListener(
(observable, oldValue, newValue) -> commitEdit(newValue));
}
this.setText(null);
this.setGraphic(picker);
picker.requestFocus();
}
}
@Override
protected void updateItem (LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (isEmpty()) {
setText(null);
setGraphic(null);
}
else {
if (isEditing()) {
setText(null);
setGraphic(picker);
}
else {
setGraphic(null);
setText(formatText(item));
}
}
}
private String formatText (LocalDate item) {
if (item == null)
return null;
else if (formatter != null)
return formatter.format(item);
else
return item.toString();
}
@Override
public void cancelEdit () {
super.cancelEdit();
setText(formatText(getItem()));
setGraphic(null);
}
}
/*
* Copyright (c) 2019 Sebastian Teumert (<http://teumert.net>)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.teumert.javafx.scene.control;
import java.util.Arrays;
import java.util.function.Function;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
/**
* <p>A {@link ListCell} with custom string formats.</p>
* <p>The </code>FormattedListCell</code> provides support for user-defined string
* representations (not relying on {@link Object#toString()} for formatting)
* to provide human-readable representations of its items. This is also a useful
* feature for <i>Localization</i>.</p>
* <p>Therefore, this class expects a string in {@link java.util.Formatter Format string syntax},
* as well as a variable amunt of arguments in the form of <code>Function&lt;E,?&gt;</code></p>
* <p><b>Usage Example</b><br/><pre>personList.setCellFactory(
* FormattedListCell.getListCellFactory("%s", Person::getName));</pre></p>
* @author Sebastian Teumert
*
*/
public class FormattedListCell<E> extends ListCell<E> {
private String format;
private Function<E, ?> [] extractors;
@SafeVarargs
public FormattedListCell (String format, Function<E, ?>... extractors) {
this.format = format;
this.extractors = extractors;
}
@SuppressWarnings ("rawtypes")
@Override
protected void updateItem (E item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null)
setText(null);
else {
try {
setText(String.format(format,
Arrays
.stream(extractors)
.map(extractor -> extractor.apply(item))
.map(value -> value instanceof ObservableValue ? ((ObservableValue) value).getValue() : value)
.toArray()));
} catch (Exception e){
setText(e.getClass().getName() + ": " + e.getMessage());
}
}
}
/**
*
* @param format
* @param extractors
* @return
*/
@SafeVarargs
public static <E> Callback<ListView<E>, ListCell<E>> getListCellFactory
(String format, Function<E, ?>... extractors) {
return view -> new FormattedListCell<E>(format, extractors);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment