我不认为 Slider 控件是为此目的而实现的,这可能是可能的,但需要大量的黑客攻击,为什么你必须这样限制自己?
我建议实现一个简单的自定义控件并将其样式设置为看起来像滑块,或者更好的是您可以在拇指内添加一个图标,以指示状态(播放/暂停),您基本上可以用它做任何您想做的事情.
我编写了以下示例,它使用 StackPane 作为父级,然后将 StackPanes 用于子级(全宽栏和进度条,也是拇指):
我将类命名为 MediaSlider,它扩展了 StackPane
import java.util.function.Consumer;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
public class MediaSlider extends StackPane {
private String main = "#6200ee";
private String sec = "#c6aee6";
private DoubleProperty value;
private StackPane thumb;
private ImageView icon;
private Image playImage, pauseImage;
private Runnable onThumbClick;
private boolean dragged = false;
private Consumer<Double> onSlide;
private double seek;
public MediaSlider() {
setAlignment(Pos.CENTER_LEFT);
setMaxHeight(50);
value = new SimpleDoubleProperty(0);
StackPane bar = createRect(sec);
bar.maxWidthProperty().bind(widthProperty());
StackPane fill = createRect(main);
fill.maxWidthProperty()
.bind(widthProperty().subtract(heightProperty()).multiply(value).add(heightProperty().divide(2)));
thumb = new StackPane();
thumb.setMaxWidth(Region.USE_PREF_SIZE);
thumb.setMinWidth(Region.USE_PREF_SIZE);
thumb.prefWidthProperty().bind(heightProperty());
thumb.setCursor(Cursor.HAND);
Circle circle = new Circle();
circle.radiusProperty().bind(heightProperty().divide(2));
circle.setFill(Color.web(main));
playImage = new Image(getClass().getResourceAsStream("/play.png"));
pauseImage = new Image(getClass().getResourceAsStream("/pause.png"));
icon = new ImageView(playImage);
icon.fitWidthProperty().bind(thumb.widthProperty().divide(2));
icon.fitHeightProperty().bind(thumb.heightProperty().divide(2));
thumb.getChildren().addAll(circle, icon);
thumb.setEffect(new DropShadow(10, Color.gray(.0, .4)));
DoubleBinding thumbPos = widthProperty().subtract(thumb.widthProperty()).multiply(value);
thumb.translateXProperty().bind(thumbPos);
thumb.setOnMousePressed(e -> {
dragged = false;
thumb.translateXProperty().unbind();
});
thumb.setOnMouseDragged(e -> {
double preX = thumb.getTranslateX();
double x = e.getX() - getHeight() / 2 + preX;
seek = x / (getWidth() - getHeight());
seek = seek > 1 ? 1 : seek < 0 ? 0 : seek;
thumb.setTranslateX(Math.max(0, Math.min(getWidth() - getHeight(), x)));
dragged = true;
});
thumb.setOnMouseReleased(e -> {
if(dragged) {
if(onSlide != null) {
onSlide.accept(seek);
}
}else if(onThumbClick != null) {
onThumbClick.run();
}
thumb.translateXProperty().bind(thumbPos);
});
getChildren().addAll(bar, fill, thumb);
}
public void setOnSlide(Consumer<Double> onSlide) {
this.onSlide = onSlide;
}
public void setOnThumbClick(Runnable onThumbClick) {
this.onThumbClick = onThumbClick;
}
public void pause() {
icon.setImage(playImage);
}
public void play() {
icon.setImage(pauseImage);
}
public void setValue(double value) {
this.value.set(value);
}
public double getValue() {
return value.get();
}
public DoubleProperty valueProperty() {
return value;
}
private StackPane createRect(String color) {
StackPane res = new StackPane();
res.maxHeightProperty().bind(heightProperty().divide(5));
res.setStyle("-fx-background-color: " + color + ";-fx-background-radius: 5;");
return res;
}
}
然后在app中使用如下方式
import java.io.IOException;
import java.net.URISyntaxException;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
@Override
public void start(Stage stage) throws IOException, URISyntaxException {
StackPane root = new StackPane();
root.setPadding(new Insets(20));
root.setStyle("-fx-background-color: #e5e5e5");
// load media
MediaPlayer player = new MediaPlayer(new Media(getClass().getResource("/song.mp3").toURI().toString()));
player.setOnReady(() -> {
player.play();
player.pause();
});
// instantiate your custom slider class
MediaSlider s = new MediaSlider();
// bind slider position to media position
s.valueProperty().bind(Bindings.createDoubleBinding(() -> {
double val = player.getCurrentTime().toSeconds() / player.getTotalDuration().toSeconds();
return Double.isNaN(val) ? .0 : val;
}, player.currentTimeProperty()));
// handle click events
s.setOnThumbClick(() -> {
if (player.getStatus().equals(Status.PLAYING)) {
s.pause();
player.pause();
player.seek(player.getCurrentTime());
} else {
s.play();
player.play();
}
});
//handle mouse release after sliding by setting the current time
s.setOnSlide(v -> {
player.seek(Duration.seconds(player.getTotalDuration().toSeconds() * v));
});
// replay on end of media
player.setOnEndOfMedia(() -> player.seek(Duration.ZERO));
root.getChildren().add(s);
Scene scene = new Scene(root, 600, 300);
stage.setScene(scene);
stage.show();
}
}
这是它的外观: