JavaFX具有对所有元素(scoll窗格,文本区域等)透明的滚动条

时间:2019-05-22 23:41:55

标签: css javafx scrollbar

我要查找的是类似于IntelliJ IDE的scoll-bar:

  • 完全透明地跟踪
  • 没有按钮
  • 拇指具有部分透明度
  • 滚动向上/向下滚动直到曲目的尽头

IntelliJ example

到目前为止,我唯一可以做的方法就是拥有这段代码,但是这并没有修饰我的舞台-我不想拥有它,它看起来更像是一种解决方法,而不是对滚动条的实际控制。采用此解决方案将需要进行巨大的更改,并将其应用到坚实的背景中,仅因为根已经更改。到目前为止的代码:

主要

primaryStage.initStyle(StageStyle.TRANSPARENT);
root.getScene().setFill(Color.TRANSPARENT);

CSS

* {
    -fx-background: transparent;
}

.scroll-pane {
    -fx-background-color: transparent;
}

代码产生(蓝色背景是桌面墙纸,我只希望它在场景边界内是透明的,而不是下图所示)

Current code output

2 个答案:

答案 0 :(得分:1)

您可以很容易地使滚动条轨道透明:

.scroll-bar .track {
  -fx-fill: transparent;
}

除非您使用自己的皮肤,否则无法删除这些按钮:

.scroll-bar {
   -fx-skin: "hs.mediasystem.util.javafx.control.MinimalScrollBarSkin";
}

最后,IntelliJ屏幕快照具有通过滚动条显示的滚动窗格的内容。现在有点棘手,因为滚动条仍将保留空间,背景将是其父级,而不是ScrollPane的内容。

您只能通过编写自己的ScrollPane(将其子类化,并在其layoutChildren中自定义内容的位置并使透明滚动条与内容重叠)来实现。

我没有一个例子,但是应该可以实现。

无论如何,这是MinimalScrollBarSkin的代码(可能有更简单的方法实现,但这对我有用):

package hs.mediasystem.util.javafx.control;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.Skin;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;

/**
 * Scrollbar skin without increment/decrement buttons.
 */
public class MinimalScrollBarSkin implements Skin<ScrollBar> {

  private ScrollBar scrollBar;
  private Region group;
  private Rectangle track = new Rectangle();
  private Rectangle thumb = new Rectangle();

  public MinimalScrollBarSkin(final ScrollBar scrollBar) {
    this.scrollBar = scrollBar;

    this.group = new Region() {
      NumberBinding range = Bindings.subtract(scrollBar.maxProperty(), scrollBar.minProperty());
      NumberBinding position = Bindings.divide(Bindings.subtract(scrollBar.valueProperty(), scrollBar.minProperty()), range);

      {
        // Children are added unmanaged because for some reason the height of the bar keeps changing
        // if they're managed in certain situations... not sure about the cause.
        getChildren().addAll(track, thumb);

        track.setManaged(false);
        track.getStyleClass().add("track");

        thumb.setManaged(false);
        thumb.getStyleClass().add("thumb");

        scrollBar.orientationProperty().addListener(obs -> setup());

        setup();
      }

      private void setup() {
        track.widthProperty().unbind();
        track.heightProperty().unbind();

        if(scrollBar.getOrientation() == Orientation.HORIZONTAL) {
          track.relocate(0, -16);
          track.widthProperty().bind(scrollBar.widthProperty());
          track.setHeight(16);
        }
        else {
          track.relocate(-16, 0);
          track.setWidth(16);
          track.heightProperty().bind(scrollBar.heightProperty());
        }

        thumb.xProperty().unbind();
        thumb.yProperty().unbind();
        thumb.widthProperty().unbind();
        thumb.heightProperty().unbind();

        if(scrollBar.getOrientation() == Orientation.HORIZONTAL) {
          thumb.relocate(0, -16);
          thumb.widthProperty().bind(Bindings.max(16, scrollBar.visibleAmountProperty().divide(range).multiply(scrollBar.widthProperty())));
          thumb.setHeight(16);
          thumb.xProperty().bind(Bindings.subtract(scrollBar.widthProperty(), thumb.widthProperty()).multiply(position));
        }
        else {
          thumb.relocate(-16, 0);
          thumb.setWidth(16);
          thumb.heightProperty().bind(Bindings.max(16, scrollBar.visibleAmountProperty().divide(range).multiply(scrollBar.heightProperty())));
          thumb.yProperty().bind(Bindings.subtract(scrollBar.heightProperty(), thumb.heightProperty()).multiply(position));
        }
      }

      @Override
      protected double computeMaxWidth(double height) {
        if(scrollBar.getOrientation() == Orientation.HORIZONTAL) {
          return Double.MAX_VALUE;
        }

        return 16;
      }

      @Override
      protected double computeMaxHeight(double width) {
        if(scrollBar.getOrientation() == Orientation.VERTICAL) {
          return Double.MAX_VALUE;
        }

        return 16;
      }
    };
  }

  @Override
  public void dispose() {
    scrollBar = null;
    group = null;
  }

  @Override
  public Node getNode() {
    return group;
  }

  @Override
  public ScrollBar getSkinnable() {
    return scrollBar;
  }
}

答案 1 :(得分:0)

john16384的答案是完美的,除了不能通过鼠标或触摸移动滚动拇指之外,因此我在其中添加了此功能。 PS:我还将滚动条宽度16更改为有价值的值,该值可以通过CSS定义,但超出主题范围

        this.group = new Region() {
            private double preDragThumbPos;
            private Point2D dragStart;

            final NumberBinding range = Bindings.subtract(scrollBar.maxProperty(), scrollBar.minProperty());
            final NumberBinding position = Bindings.divide(Bindings.subtract(scrollBar.valueProperty(), scrollBar.minProperty()), this.range);

            {
                // Children are added unmanaged because for some reason the height of the bar keeps changing
                // if they're managed in certain situations... not sure about the cause.
                this.getChildren().addAll(MinimalScrollBarSkin.this.track, MinimalScrollBarSkin.this.thumb);

                MinimalScrollBarSkin.this.track.setManaged(false);
                MinimalScrollBarSkin.this.track.getStyleClass().add("track");

                MinimalScrollBarSkin.this.thumb.setManaged(false);
                MinimalScrollBarSkin.this.thumb.getStyleClass().add("thumb");

                scrollBar.orientationProperty().addListener(obs -> this.setup());

                MinimalScrollBarSkin.this.thumb.setOnMousePressed((mouseEvent) -> {
                    if (mouseEvent.isSynthesized()) {
                        mouseEvent.consume();
                    } else {
                        if (MinimalScrollBarSkin.this.getSkinnable().getMax() > MinimalScrollBarSkin.this.getSkinnable().getMin()) {
                            dragStart = MinimalScrollBarSkin.this.thumb.localToParent(mouseEvent.getX(), mouseEvent.getY());
                            double value = Utils.clamp(MinimalScrollBarSkin.this.getSkinnable().getMin(), MinimalScrollBarSkin.this.getSkinnable().getValue(), MinimalScrollBarSkin.this.getSkinnable().getMax());
                            preDragThumbPos = (value - MinimalScrollBarSkin.this.getSkinnable().getMin()) / (MinimalScrollBarSkin.this.getSkinnable().getMax() - MinimalScrollBarSkin.this.getSkinnable().getMin());
                            mouseEvent.consume();
                        }

                    }
                });

                MinimalScrollBarSkin.this.thumb.setOnMouseDragged((mouseEvent) -> {
                    if (mouseEvent.isSynthesized()) {
                        mouseEvent.consume();
                    } else {
                        if (MinimalScrollBarSkin.this.getSkinnable().getMax() > MinimalScrollBarSkin.this.getSkinnable().getMin()) {
                            if (this.trackLength() > this.thumbLength()) {
                                Point2D pos = MinimalScrollBarSkin.this.thumb.localToParent(mouseEvent.getX(), mouseEvent.getY());
                                if (dragStart == null) {
                                    dragStart = MinimalScrollBarSkin.this.thumb.localToParent(mouseEvent.getX(), mouseEvent.getY());
                                }

                                double value = MinimalScrollBarSkin.this.getSkinnable().getOrientation() == Orientation.VERTICAL ? pos.getY() - dragStart.getY() : pos.getX() - this.dragStart.getX();
                                double change = preDragThumbPos + value / (this.trackLength() - this.thumbLength());
                                double newValue = change * (MinimalScrollBarSkin.this.getSkinnable().getMax() - MinimalScrollBarSkin.this.getSkinnable().getMin()) + MinimalScrollBarSkin.this.getSkinnable().getMin();
                                if (!Double.isNaN(newValue)) {
                                    MinimalScrollBarSkin.this.getSkinnable().setValue(boundedSize(MinimalScrollBarSkin.this.getSkinnable().getMin(), newValue, MinimalScrollBarSkin.this.getSkinnable().getMax()));
                                }
                            }
                            mouseEvent.consume();
                        }
                    }
                });

                this.setup();
            }

            private double trackLength() {
                return MinimalScrollBarSkin.this.getSkinnable().getOrientation() == Orientation.VERTICAL ? MinimalScrollBarSkin.this.track.getHeight() : MinimalScrollBarSkin.this.track.getWidth();
            }

            private double thumbLength() {
                return MinimalScrollBarSkin.this.getSkinnable().getOrientation() == Orientation.VERTICAL ? MinimalScrollBarSkin.this.thumb.getHeight() : MinimalScrollBarSkin.this.thumb.getWidth();
            }

            private double boundedSize(double min, double value, double max) {
                return Math.min(Math.max(value, min), Math.max(min, max));
            }

            private void setup() { ... } 
      }

@ 4673_j的更新

scrollpane在放置内容时将获得滚动条(而不是皮肤)的首选宽度/高度,因此在我们的皮肤中将首选宽度/高度设置为1或0(取决于您的跟踪样式)将解决此问题。 例如,将设置功能修改为:

        if (this.scrollBar.getOrientation() == Orientation.HORIZONTAL) {
            this.track.relocate(0, -size);
            this.track.widthProperty().bind(this.scrollBar.widthProperty());
            this.track.setHeight(size);
            this.getSkinnable().setPrefHeight(track.getStrokeWidth());
        } else {
            this.track.relocate(-size, 0);
            this.track.setWidth(size);
            this.track.heightProperty().bind(this.scrollBar.heightProperty());
            this.getSkinnable().setPrefWidth(track.getStrokeWidth());
        }