Статьи

JavaFX Совет 30: панель прокрутки с DropShadow

В одном из моих проектов я недавно заметил, что пользователю было трудно увидеть, прокручивается ли содержимое экземпляра ScrollPane в настоящее время или нет. Один из способов сделать это более понятным — добавить тень на верхнюю часть панели прокрутки.

Это также то, что предложено Google Material Design. Я попробовал. В своем решении я просто добавил область к ScrollPane, и при ее разметке я перемещаю ее за пределы области просмотра ScrollPane, чтобы в нее по-прежнему проникал только теневой эффект, примененный к области.

Чтобы действительно убедиться, что регион не виден, мне также пришлось установить клип на ScrollPane. Это работает довольно хорошо, хотя я должен признать, что не уверен на 100%, что это лучший способ сделать это. Так что, если у кого-то есть какие-либо предложения / альтернативные подходы, пожалуйста, оставьте комментарий.

Ниже вы видите скриншоты до и после прокрутки одного из экранов нашего приложения.

Перед прокруткой

Панель прокрутки перед прокруткой

После прокрутки

Панель прокрутки с тенями

Кстати, я реализовал это таким образом, что тень не просто появляется внезапно, но она перемещается в окно просмотра шаг за шагом, в зависимости от того, как далеко прокрутил пользователь. Чтобы увидеть это, вам нужно очень медленно прокрутить вниз.

Код для ShadowScrollPane можно найти в этой статье на GitHub:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package uk.co.senapt.desktop.shell;
 
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
 
/**
 * Created by lemmi on 23.08.17.
 */
public class ShadowScrollPane extends ScrollPane {
 
    private Region shadow = new Region();
 
    public ShadowScrollPane() {
        super();
 
        init();
    }
 
    public ShadowScrollPane(Node content) {
        super(content);
        init();
    }
 
    private void init() {
        skinProperty().addListener(it -> {
            getChildren().addAll(shadow);
        });
 
        setFitToWidth(true);
        setVbarPolicy(ScrollBarPolicy.NEVER);
        setHbarPolicy(ScrollBarPolicy.NEVER);
 
        shadow.setManaged(false);
        shadow.setStyle("-fx-pref-height: 10;" +
                "-fx-background-color: black;" +
                "-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, .75), 20, 0.19, 0, 6);");
        shadow.getStyleClass().add("shadow");
        shadow.visibleProperty().bind(showShadowProperty());
        shadow.setMouseTransparent(true);
        shadow.visibleProperty().bind(vvalueProperty().greaterThan(0));
 
        Rectangle clip = new Rectangle();
        clip.widthProperty().bind(widthProperty());
        clip.heightProperty().bind(heightProperty());
        setClip(clip);
 
        vvalueProperty().addListener(it -> {
            if (lastOffset != computeOffset()) {
                requestLayout();
            }
        });
        showShadowProperty().addListener(it -> requestLayout());
    }
 
    private final BooleanProperty showShadow = new SimpleBooleanProperty(this, "showShadow", true);
 
    public final BooleanProperty showShadowProperty() {
        return showShadow;
    }
 
    public final boolean isShowShadow() {
        return showShadow.get();
    }
 
    public final void setShowShadow(boolean show) {
        showShadow.set(show);
    }
 
    private final int SHADOW_HEIGHT = 30;
 
    @Override
    protected void layoutChildren() {
        super.layoutChildren();
 
        if (isShowShadow()) {
            Insets insets = getInsets();
            double w = getWidth();
            double offset = computeOffset();
            shadow.resizeRelocate(-10, insets.getTop() - shadow.prefHeight(-1) - SHADOW_HEIGHT + offset, w + 20, shadow.prefHeight(-1) - 1);
            lastOffset = offset;
        }
    }
 
    private double lastOffset = 0;
 
    private double computeOffset() {
        if (getContent() != null) {
            return Math.min(getVvalue() * getContent().prefHeight(-1), SHADOW_HEIGHT);
        }
 
        return 0;
    }
}
Опубликовано на Java Code Geeks с разрешения Дирка Леммермана, партнера нашей программы JCG. Смотрите оригинальную статью здесь: JavaFX Совет 30: Панель прокрутки с DropShadow

Мнения, высказанные участниками Java Code Geeks, являются их собственными.