在JavaFX TableColumn
中,右侧有一个排序箭头。
如何设置此箭头的对齐?
我问,因为我正在尝试将Material Design应用于JavaFX,箭头需要位于左侧 - 否则箭头似乎属于相邻列。
我知道你可以这样TableColumnHeader
:
for (final Node headerNode : tableView.lookupAll(".column-header")) {
TableColumnHeader tableColumnHeader = (TableColumnHeader) headerNode;
我知道TableColumnHeader
有一个Label label
和一个GridPane sortArrowGrid
作为其子女。
如何将sortArrowGrid
移到孩子的前面? .toFront()
只适用于z顺序吗?
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来做。
答案 0 :(得分:1)
在我的评论中扩展一点(带有一些代码):如前所述,排序指示器的对齐(或在fx-speak:内容显示中)是不可配置的,不是样式,也不是列/标题的任何属性 - 相反,它在标题的布局代码中进行了硬编码。
这意味着我们需要实现支持可配置显示的自定义columnHeader。肉是一个自定义的TableColumnHeader,它有:
sortIconDisplayProperty()
,用于配置排序指示符的相对位置layoutChildren()
,用于将标签和排序指标定位为已配置要使用,我们需要自定义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
getTableViewSkin()
- 或类似 - 具有受保护的范围,因此可供子类访问)< / LI>