在我的评论中扩展一点(使用一些代码):如前所述,排序指示器的对齐方式(或在 fx-speak:内容显示中)是不可配置的,既不是样式也不是列/标题的任何属性 - 相反,它是硬编码在标题的布局代码中。
这意味着我们需要实现一个支持可配置显示的自定义 columnHeader。肉在一个自定义的 TableColumnHeader 中,它有:
sortIconDisplayProperty()
用于配置排序指示器的相对位置的属性
layoutChildren()
按照配置定位标签和排序指示器的覆盖
- 为了好玩:使该属性可样式化(需要一些围绕 StyleableProperty 的样板及其在 CSS 处理程序中的注册)
要使用,我们需要自定义 TableViewSkin、TableHeaderRow、NestedTableColumnHeader 的整个堆栈:所有这些都只是在其相关工厂方法中创建和返回自定义 xx 实例的样板。
下面是一个粗略的例子(阅读:布局不完美,应该有一些填充并保证不与文本重叠......但是,核心并不擅长它,也不是)支持设置图标左侧文本。为了获得完整的支持,您可能希望将其设置在顶部/底部 .. 我现在太懒了;)
/**
* https://stackoverflow.com/q/49121560/203657
* 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 进行编码的 - 将 Skins 与一系列其他更改一起移入了公共范围。使其与 fx8 一起使用
- 将导入语句调整到 com.sun.** 中的旧位置(无论如何都没有显示,您的 IDE 是您的朋友;)
- 对于所有 SomethingHeader,更改构造函数以包含 tableSkin 作为参数并在所有工厂方法中传递皮肤(可能在 fx8 中,因为
getTableViewSkin()
- 或类似 - 具有受保护的范围,因此子类可以访问)