【问题标题】:JavaFX - horizontal marquee textJavaFX - 水平选框文本
【发布时间】:2014-05-28 13:13:21
【问题描述】:

我正在尝试实现类似于marquee 的效果 - 在水平轴上移动的长(在我的情况下)文本行。我设法让它工作,但我不能称之为令人满意。

我的Controller 类如下所示:

@FXML
private Text newsFeedText;

(...)
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    TranslateTransition transition = TranslateTransitionBuilder.create()
            .duration(new Duration(7500))
            .node(newsFeedText)
            .interpolator(Interpolator.LINEAR)
            .cycleCount(Timeline.INDEFINITE)
            .build();   

    GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
    int width = gd.getDisplayMode().getWidth();

    transition.setFromX(width);
    transition.setToX(-width);
    transition.play();
}

newsFeedText 绑定到一些动态更新的文本源,因此它包含不同数量的文本。

我的代码至少有两个缺点:

  • 转换从-width+widthwidth 是显示器的分辨率宽度

如果窗口未全屏显示,有时文本将完全不可见。 如果文本会更长并且newsFeedText 宽度将大于显示器的分辨率宽度,则过渡将“一半”消失(仍然在屏幕上)。

  • 目前Duration 不依赖于newsFeedText 的宽度。

现在,它没有任何磨损,但是如果动态计算过渡的fromXtoX,那么它将导致各种速度的选取框。

如何摆脱这些弊端?

【问题讨论】:

    标签: java javafx javafx-2 marquee


    【解决方案1】:

    您应该可以通过收听场景的widthProperty 来做到这一点。您可以通过newsFeedText.getScene().widthProperty() 访问它,或者从主类中获取引用并从那里公开它,或者将其传递给方法或构造函数以在声明newsFeedText 的类中访问。

    这种方法的好处是,现在您的逻辑取决于场景的宽度(动态依赖)而不是监视器的宽度(静态依赖)。请注意,我没有测试过这种方法,但目前没有理由(也许是天真地)它不应该工作。

    至于您的持续时间依赖性,您可以通过根据newsFeedText 中文本的长度执行某种计算来解决这个问题。 Duration.seconds(newsFeedText.get Text().length()/denominator) 之类的东西,其中 denominator 是您指定的某个值(例如 7500,如您的代码中所示)。这将使您的持续时间根据文本的长度动态计算。

    如果您想使用newsFeedText 本身的宽度而不是其文本的长度进行操作,那么只需将newsFeedText.getText().length() 替换为newsFeedText.getWidth()。确保在布置 newsFeedText 之后执行此计算,以便获取其宽度的调用返回实际宽度。您还可以将呼叫替换为 getPrefWidth()getMinWidth()getMaxWidth() 中的任何一个。

    【讨论】:

    • 这是另一个问题出现的地方,如果你绑定TranslateTransition#toX,调用TranslateTransition#play并改变窗口的大小,过渡不会改变它的toX,直到它停止并再次播放。在我的例子中,代码是在Initializable#initialize 方法中调用的,所以我的场景的大小在那一刻等于零
    • 使用在宽度属性上注册的ChangeListener。在那里,将您的过渡值设置为ChangeListener 报告的新值。
    【解决方案2】:

    我已经设法让它工作,任何重新计算只能在转换停止后发生,所以我们不能将它的 cycleCount 设置为 Timeline.INDEFINITE。我的要求是我可以更改组件内的文本,以便有 fxml 接线:

    @FXML
    private Text node; // text to marquee
    
    @FXML
    private Pane parentPane; // pane on which text is placed
    

    有效的代码是:

    transition = TranslateTransitionBuilder.create()
            .duration(new Duration(10))
            .node(node)
            .interpolator(Interpolator.LINEAR)
            .cycleCount(1)
            .build();
    
    transition.setOnFinished(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            rerunAnimation();
        }
    });
    
    rerunAnimation();
    

    rerunAnimation() 在哪里:

    private void rerunAnimation() {
        transition.stop();
        // if needed set different text on "node"
        recalculateTransition();
        transition.playFromStart();
    }
    

    recalculateTransition() 是:

    private void recalculateTransition() {
        transition.setToX(node.getBoundsInLocal().getMaxX() * -1 - 100);
        transition.setFromX(parentPane.widthProperty().get() + 100);
    
        double distance = parentPane.widthProperty().get() + 2 * node.getBoundsInLocal().getMaxX();
        transition.setDuration(new Duration(distance / SPEED_FACTOR));
    }
    

    【讨论】:

    • 考虑将选取框控件捐赠给ControlsFX
    • 它适用于所有窗口宽度,但如果我需要内部控制的自动收报机怎么办?我无法特别隐藏文字
    • @jewelsea 我觉得我的“变通”解决方案效率太低了,但有空我一定会改进它并捐赠解决方案。
    • 还可以考虑在marquee Text对象下放置一个矩形,其大小大于可见屏幕区域,以实现流畅的动画效果。您的文本形状必须在该矩形的所有路径上运行。并且不要忘记设置形状的缓存。
    猜你喜欢
    • 2011-03-29
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多