如何设置 TableColumn 的排序箭头对齐方式?
How does one set the TableColumn's sort arrow alignment?
要点
在 JavaFX TableColumn
中,右侧有一个排序箭头。
如何设置此箭头的对齐方式?
我的用例
我问是因为我正在尝试将 Material Design 应用于 JavaFX,并且箭头需要在左侧 — 否则箭头似乎属于相邻的列。
我做知道的
我知道你可以像这样到达 TableColumnHeader
:
for (final Node headerNode : tableView.lookupAll(".column-header")) {
TableColumnHeader tableColumnHeader = (TableColumnHeader) headerNode;
我知道 TableColumnHeader
有一个 Label label
和一个 GridPane sortArrowGrid
作为它的 children。
如何将 sortArrowGrid
移到 children 的前面? .toFront()
只适用于 z-order 对吗?
Node arrow = tableColumnHeader.lookup(".arrow");
if (arrow != null) {
GridPane sortArrowGrid = (GridPane) arrow.getParent();
// TODO: Move the sortArrowGrid to the front of the tableColumnHeader's children
}
我觉得我 可能 做错了——我很乐意 CSS。
对我的评论进行一些扩展(使用一些代码):如前所述,排序指示器的对齐方式(或在 fx-speak 中:内容显示)是不可配置的,既不是风格也不是任何 [= column/header 的 57=] - 相反,它是页眉布局代码中的 hard-coded。
意味着我们需要实现一个支持可配置显示的自定义columnHeader。肉在自定义 TableColumnHeader 中,它具有:
- a 属性
sortIconDisplayProperty()
配置排序指标的相对位置
- 重写
layoutChildren()
按配置放置标签和排序指示器
- 为了好玩:使 属性 具有样式(需要一些围绕 StyleableProperty 的样板及其在 CSS 处理程序中的注册)
要使用,我们需要自定义 TableViewSkin、TableHeaderRow、NestedTableColumnHeader 的整个堆栈:所有这些都只是 boiler-plate 以在其相关工厂方法中创建和 return 自定义 xx 实例。
下面是一个粗略的示例(阅读:布局不完美,应该有一些填充并保证不与文本重叠......但是,核心不是那么擅长,也不是)支持设置文本左侧的图标。为了获得完整的支持,您可能需要在 top/bottom 上实现设置 .. 我现在太懒了 ;)
/**
*
* position sort indicator at leading edge of column header
*
* @author Jeanette Winzenburg, Berlin
*/
public class TableHeaderLeadingSortArrow extends Application {
/**
* Custom TableColumnHeader that lays out the sort icon at its leading edge.
*/
public static class MyTableColumnHeader extends TableColumnHeader {
public MyTableColumnHeader(TableColumnBase column) {
super(column);
}
@Override
protected void layoutChildren() {
// call super to ensure that all children are created and installed
super.layoutChildren();
Node sortArrow = getSortArrow();
// no sort indicator, nothing to do
if (sortArrow == null || !sortArrow.isVisible()) return;
if (getSortIconDisplay() == ContentDisplay.RIGHT) return;
// re-arrange label and sort indicator
double sortWidth = sortArrow.prefWidth(-1);
double headerWidth = snapSizeX(getWidth()) - (snappedLeftInset() + snappedRightInset());
double headerHeight = getHeight() - (snappedTopInset() + snappedBottomInset());
// position sort indicator at leading edge
sortArrow.resize(sortWidth, sortArrow.prefHeight(-1));
positionInArea(sortArrow, snappedLeftInset(), snappedTopInset(),
sortWidth, headerHeight, 0, HPos.CENTER, VPos.CENTER);
// resize label to fill remaining space
getLabel().resizeRelocate(sortWidth, 0, headerWidth - sortWidth, getHeight());
}
// --------------- make sort icon location styleable
// use StyleablePropertyFactory to simplify styling-related code
private static final StyleablePropertyFactory<MyTableColumnHeader> FACTORY =
new StyleablePropertyFactory<>(TableColumnHeader.getClassCssMetaData());
// default value (strictly speaking: an implementation detail)
// PENDING: what about RtoL orientation? Is it handled correctly in
// core?
private static final ContentDisplay DEFAULT_SORT_ICON_DISPLAY = ContentDisplay.RIGHT;
private static CssMetaData<MyTableColumnHeader, ContentDisplay> CSS_SORT_ICON_DISPLAY =
FACTORY.createEnumCssMetaData(ContentDisplay.class,
"-fx-sort-icon-display",
header -> header.sortIconDisplayProperty(),
DEFAULT_SORT_ICON_DISPLAY);
// property with lazy instantiation
private StyleableObjectProperty<ContentDisplay> sortIconDisplay;
protected StyleableObjectProperty<ContentDisplay> sortIconDisplayProperty() {
if (sortIconDisplay == null) {
sortIconDisplay = new SimpleStyleableObjectProperty<>(
CSS_SORT_ICON_DISPLAY, this, "sortIconDisplay",
DEFAULT_SORT_ICON_DISPLAY);
}
return sortIconDisplay;
}
protected ContentDisplay getSortIconDisplay() {
return sortIconDisplay != null ? sortIconDisplay.get()
: DEFAULT_SORT_ICON_DISPLAY;
}
protected void setSortIconDisplay(ContentDisplay display) {
sortIconDisplayProperty().set(display);
}
/**
* Returnst the CssMetaData associated with this class, which may
* include the CssMetaData of its superclasses.
*
* @return the CssMetaData associated with this class, which may include
* the CssMetaData of its superclasses
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return FACTORY.getCssMetaData();
}
/** {@inheritDoc} */
@Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return getClassCssMetaData();
}
//-------- reflection acrobatics .. might use lookup and/or keeping aliases around
private Node getSortArrow() {
return (Node) FXUtils.invokeGetFieldValue(TableColumnHeader.class, this, "sortArrow");
}
private Label getLabel() {
return (Label) FXUtils.invokeGetFieldValue(TableColumnHeader.class, this, "label");
}
}
private Parent createContent() {
// instantiate the tableView with the custom default skin
TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(
Locale.getAvailableLocales())) {
@Override
protected Skin<?> createDefaultSkin() {
return new MyTableViewSkin<>(this);
}
};
TableColumn<Locale, String> countryCode = new TableColumn<>("CountryCode");
countryCode.setCellValueFactory(new PropertyValueFactory<>("country"));
TableColumn<Locale, String> language = new TableColumn<>("Language");
language.setCellValueFactory(new PropertyValueFactory<>("language"));
TableColumn<Locale, String> variant = new TableColumn<>("Variant");
variant.setCellValueFactory(new PropertyValueFactory<>("variant"));
table.getColumns().addAll(countryCode, language, variant);
BorderPane pane = new BorderPane(table);
return pane;
}
/**
* Custom nested columnHeader, headerRow und skin only needed to
* inject the custom columnHeader in their factory methods.
*/
public static class MyNestedTableColumnHeader extends NestedTableColumnHeader {
public MyNestedTableColumnHeader(TableColumnBase column) {
super(column);
}
@Override
protected TableColumnHeader createTableColumnHeader(
TableColumnBase col) {
return col == null || col.getColumns().isEmpty() || col == getTableColumn() ?
new MyTableColumnHeader(col) :
new MyNestedTableColumnHeader(col);
}
}
public static class MyTableHeaderRow extends TableHeaderRow {
public MyTableHeaderRow(TableViewSkinBase tableSkin) {
super(tableSkin);
}
@Override
protected NestedTableColumnHeader createRootHeader() {
return new MyNestedTableColumnHeader(null);
}
}
public static class MyTableViewSkin<T> extends TableViewSkin<T> {
public MyTableViewSkin(TableView<T> table) {
super(table);
}
@Override
protected TableHeaderRow createTableHeaderRow() {
return new MyTableHeaderRow(this);
}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
URL uri = getClass().getResource("columnheader.css");
stage.getScene().getStylesheets().add(uri.toExternalForm());
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableHeaderLeadingSortArrow.class.getName());
}
columnheader.css配置:
.column-header {
-fx-sort-icon-display: LEFT;
}
版本说明:
该示例是针对 fx9 编码的 - 它将皮肤移入 public 范围以及一系列其他更改。使其与 fx8
一起使用
- 将导入语句调整到 com.sun 中的旧位置**(无论如何都不会显示,您的 IDE 是您的朋友;)
- 对于所有 SomethingHeader,更改构造函数以包含 tableSkin 作为参数并在所有工厂方法中传递皮肤(可能在 fx8 中,因为
getTableViewSkin()
- 或类似的 - 具有受保护的范围,因此子类可以访问)
要点
在 JavaFX TableColumn
中,右侧有一个排序箭头。
如何设置此箭头的对齐方式?
我的用例
我问是因为我正在尝试将 Material Design 应用于 JavaFX,并且箭头需要在左侧 — 否则箭头似乎属于相邻的列。
我做知道的
我知道你可以像这样到达 TableColumnHeader
:
for (final Node headerNode : tableView.lookupAll(".column-header")) {
TableColumnHeader tableColumnHeader = (TableColumnHeader) headerNode;
我知道 TableColumnHeader
有一个 Label label
和一个 GridPane sortArrowGrid
作为它的 children。
如何将 sortArrowGrid
移到 children 的前面? .toFront()
只适用于 z-order 对吗?
Node arrow = tableColumnHeader.lookup(".arrow");
if (arrow != null) {
GridPane sortArrowGrid = (GridPane) arrow.getParent();
// TODO: Move the sortArrowGrid to the front of the tableColumnHeader's children
}
我觉得我 可能 做错了——我很乐意 CSS。
对我的评论进行一些扩展(使用一些代码):如前所述,排序指示器的对齐方式(或在 fx-speak 中:内容显示)是不可配置的,既不是风格也不是任何 [= column/header 的 57=] - 相反,它是页眉布局代码中的 hard-coded。
意味着我们需要实现一个支持可配置显示的自定义columnHeader。肉在自定义 TableColumnHeader 中,它具有:
- a 属性
sortIconDisplayProperty()
配置排序指标的相对位置 - 重写
layoutChildren()
按配置放置标签和排序指示器 - 为了好玩:使 属性 具有样式(需要一些围绕 StyleableProperty 的样板及其在 CSS 处理程序中的注册)
要使用,我们需要自定义 TableViewSkin、TableHeaderRow、NestedTableColumnHeader 的整个堆栈:所有这些都只是 boiler-plate 以在其相关工厂方法中创建和 return 自定义 xx 实例。
下面是一个粗略的示例(阅读:布局不完美,应该有一些填充并保证不与文本重叠......但是,核心不是那么擅长,也不是)支持设置文本左侧的图标。为了获得完整的支持,您可能需要在 top/bottom 上实现设置 .. 我现在太懒了 ;)
/**
*
* position sort indicator at leading edge of column header
*
* @author Jeanette Winzenburg, Berlin
*/
public class TableHeaderLeadingSortArrow extends Application {
/**
* Custom TableColumnHeader that lays out the sort icon at its leading edge.
*/
public static class MyTableColumnHeader extends TableColumnHeader {
public MyTableColumnHeader(TableColumnBase column) {
super(column);
}
@Override
protected void layoutChildren() {
// call super to ensure that all children are created and installed
super.layoutChildren();
Node sortArrow = getSortArrow();
// no sort indicator, nothing to do
if (sortArrow == null || !sortArrow.isVisible()) return;
if (getSortIconDisplay() == ContentDisplay.RIGHT) return;
// re-arrange label and sort indicator
double sortWidth = sortArrow.prefWidth(-1);
double headerWidth = snapSizeX(getWidth()) - (snappedLeftInset() + snappedRightInset());
double headerHeight = getHeight() - (snappedTopInset() + snappedBottomInset());
// position sort indicator at leading edge
sortArrow.resize(sortWidth, sortArrow.prefHeight(-1));
positionInArea(sortArrow, snappedLeftInset(), snappedTopInset(),
sortWidth, headerHeight, 0, HPos.CENTER, VPos.CENTER);
// resize label to fill remaining space
getLabel().resizeRelocate(sortWidth, 0, headerWidth - sortWidth, getHeight());
}
// --------------- make sort icon location styleable
// use StyleablePropertyFactory to simplify styling-related code
private static final StyleablePropertyFactory<MyTableColumnHeader> FACTORY =
new StyleablePropertyFactory<>(TableColumnHeader.getClassCssMetaData());
// default value (strictly speaking: an implementation detail)
// PENDING: what about RtoL orientation? Is it handled correctly in
// core?
private static final ContentDisplay DEFAULT_SORT_ICON_DISPLAY = ContentDisplay.RIGHT;
private static CssMetaData<MyTableColumnHeader, ContentDisplay> CSS_SORT_ICON_DISPLAY =
FACTORY.createEnumCssMetaData(ContentDisplay.class,
"-fx-sort-icon-display",
header -> header.sortIconDisplayProperty(),
DEFAULT_SORT_ICON_DISPLAY);
// property with lazy instantiation
private StyleableObjectProperty<ContentDisplay> sortIconDisplay;
protected StyleableObjectProperty<ContentDisplay> sortIconDisplayProperty() {
if (sortIconDisplay == null) {
sortIconDisplay = new SimpleStyleableObjectProperty<>(
CSS_SORT_ICON_DISPLAY, this, "sortIconDisplay",
DEFAULT_SORT_ICON_DISPLAY);
}
return sortIconDisplay;
}
protected ContentDisplay getSortIconDisplay() {
return sortIconDisplay != null ? sortIconDisplay.get()
: DEFAULT_SORT_ICON_DISPLAY;
}
protected void setSortIconDisplay(ContentDisplay display) {
sortIconDisplayProperty().set(display);
}
/**
* Returnst the CssMetaData associated with this class, which may
* include the CssMetaData of its superclasses.
*
* @return the CssMetaData associated with this class, which may include
* the CssMetaData of its superclasses
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return FACTORY.getCssMetaData();
}
/** {@inheritDoc} */
@Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return getClassCssMetaData();
}
//-------- reflection acrobatics .. might use lookup and/or keeping aliases around
private Node getSortArrow() {
return (Node) FXUtils.invokeGetFieldValue(TableColumnHeader.class, this, "sortArrow");
}
private Label getLabel() {
return (Label) FXUtils.invokeGetFieldValue(TableColumnHeader.class, this, "label");
}
}
private Parent createContent() {
// instantiate the tableView with the custom default skin
TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(
Locale.getAvailableLocales())) {
@Override
protected Skin<?> createDefaultSkin() {
return new MyTableViewSkin<>(this);
}
};
TableColumn<Locale, String> countryCode = new TableColumn<>("CountryCode");
countryCode.setCellValueFactory(new PropertyValueFactory<>("country"));
TableColumn<Locale, String> language = new TableColumn<>("Language");
language.setCellValueFactory(new PropertyValueFactory<>("language"));
TableColumn<Locale, String> variant = new TableColumn<>("Variant");
variant.setCellValueFactory(new PropertyValueFactory<>("variant"));
table.getColumns().addAll(countryCode, language, variant);
BorderPane pane = new BorderPane(table);
return pane;
}
/**
* Custom nested columnHeader, headerRow und skin only needed to
* inject the custom columnHeader in their factory methods.
*/
public static class MyNestedTableColumnHeader extends NestedTableColumnHeader {
public MyNestedTableColumnHeader(TableColumnBase column) {
super(column);
}
@Override
protected TableColumnHeader createTableColumnHeader(
TableColumnBase col) {
return col == null || col.getColumns().isEmpty() || col == getTableColumn() ?
new MyTableColumnHeader(col) :
new MyNestedTableColumnHeader(col);
}
}
public static class MyTableHeaderRow extends TableHeaderRow {
public MyTableHeaderRow(TableViewSkinBase tableSkin) {
super(tableSkin);
}
@Override
protected NestedTableColumnHeader createRootHeader() {
return new MyNestedTableColumnHeader(null);
}
}
public static class MyTableViewSkin<T> extends TableViewSkin<T> {
public MyTableViewSkin(TableView<T> table) {
super(table);
}
@Override
protected TableHeaderRow createTableHeaderRow() {
return new MyTableHeaderRow(this);
}
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
URL uri = getClass().getResource("columnheader.css");
stage.getScene().getStylesheets().add(uri.toExternalForm());
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableHeaderLeadingSortArrow.class.getName());
}
columnheader.css配置:
.column-header {
-fx-sort-icon-display: LEFT;
}
版本说明:
该示例是针对 fx9 编码的 - 它将皮肤移入 public 范围以及一系列其他更改。使其与 fx8
一起使用- 将导入语句调整到 com.sun 中的旧位置**(无论如何都不会显示,您的 IDE 是您的朋友;)
- 对于所有 SomethingHeader,更改构造函数以包含 tableSkin 作为参数并在所有工厂方法中传递皮肤(可能在 fx8 中,因为
getTableViewSkin()
- 或类似的 - 具有受保护的范围,因此子类可以访问)