在 Javafx 中为新的 ListView 条目添加动画

2024-01-01

问题

Hi,

我正在尝试编写一个应用程序,其中 ListView 中的每个新条目都会动画化。这是我的代码:

public class BookCell extends ListCell<Book>
{
    private Text text;
    private HBox h;
    
    public BookCell()
    {
        this.text = new Text();
        this.h = new HBox();
        this.h.getChildren().add(text);
        super.getStyleClass().add("book-list-cell");
        super.itemProperty().addListener((obs,oldv,newv)->{
            if(newv != null )
            {
                if(getIndex() == this.getListView().getItems().size()-1 )
                {
                    //why does this get called twice for each update?
                    System.out.println("isbn = "+newv.getIsbn().get() + "   lastIndexOf=" + this.getListView().getItems().lastIndexOf(newv)+"    Index="+getIndex()+"   size="+this.getListView().getItems().size());
                    runAnimation();
                }
            }
            
        });
        this.getChildren().add(h);
    }
    @Override
    protected void updateItem(Book item, boolean empty)
    {
         super.updateItem(item, empty);
         if(!empty)
             super.setGraphic(h);
         text.setText(item == null ? "" : item.getIsbn().get());
         
    }
    
    public void runAnimation()
    {
        FadeTransition f = new FadeTransition();
        f.setFromValue(0);
        f.setToValue(1);
        f.setNode(this);
        f.play();
    }
}

I've tried to add a listener to the itemProperty, but I get some strange behavior. Firstly, the listener fires twice each time I add an entry: enter image description here which results in the animation playing twice. This can be a little awkward to watch even though it is hardly noticeable in this case.

Furthermore, after the list starts scrolling, the animation is sometimes repeated for a visible entry: enter image description here

说实话,如果某个项目我不一定介意动画重复再次可见,但就目前情况而言,哪些单元格被动画化是非常不可靠的。

我知道这些细胞实际上是受控制的,并且随着时间的推移会被重复使用。但我很难找到一种可靠的方法来确定屏幕上是否出现非空单元格。

问题

  1. 为什么侦听器会发射两次?
  2. 观察屏幕上细胞出现/消失的最佳方法是什么
  3. 有没有更好的方法向 ListView 添加动画?

完整代码

这可能会有所帮助,所以这是我的 css 和主文件。

主程序.java

public class Main extends Application 
{
    static int newBookIndex = 1;        
    public static final ObservableList<Book> data =  FXCollections.observableArrayList();
    @Override
    public void start(Stage primaryStage) 
    {
        try 
        {
            GridPane myPane = (GridPane)FXMLLoader.load(getClass().getResource("quotes.fxml"));
            ListView<Book> lv = (ListView<Book>) myPane.getChildren().get(0);   
            Button addButton = (Button) myPane.getChildren().get(1);
            addButton.setText("Add Book");
            addButton.setOnAction((event)->{
            data.add(new Book(String.valueOf(newBookIndex++),"test"));
            });
            lv.setEditable(true);
            data.addAll(
                     new Book("123","Hugo"),
                     new Book("456","Harry Potter")
                );
                  
            lv.setItems(data);
            lv.setCellFactory((param)->return new BookCell());
            Scene myScene = new Scene(myPane);
            myScene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(myScene);
            primaryStage.show();
        } 
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) 
    {
        launch(args);
    }
}

application.css(基于https://github.com/privatejava/javafx-listview-animation https://github.com/privatejava/javafx-listview-animation)

.book-list-cell:odd{

    -fx-background-image: url('./wooden2.png') ,url('./gloss.png');
    -fx-background-repeat: repeat,no-repeat;
}
.book-list-cell:empty {
     -fx-background-image: url('./gloss.png');
     -fx-background-repeat: repeat;
}

.book-list-cell {
    -fx-font-size:45px;
    -fx-font-family: 'Segoe Script';
}
.book-list-cell:selected,.book-list-cell:selected:hover{
    -fx-border-color:linear-gradient(to bottom,transparent,rgb(22,22,22,1));
    -fx-border-width:2 2 2 10px;
    -fx-effect:innershadow( three-pass-box,#764b0c,30,0.3,0,0);
}
.book-list-cell:hover{
    -fx-border-color:rgb(255,255,255,0.7);
    -fx-border-width:1 1 1 10px;
}

.book-list-cell{
    -fx-background-image: url('./wooden.png') ,url('./gloss.png');
    -fx-background-repeat: repeat,no-repeat;
    -fx-background-position:left top;
    -fx-background-size: auto,100% 40%;
}

为了找到监听器双重触发的原因,我做了一些调试,遵循layoutChildren calls.

长话短说:每次点击Add Book按钮,不是两个,而是三个变化itemProperty()。要找到答案,请在侦听器和控制台上添加一些打印updateItem method:

itemProperty().addListener((obs,oldv,newv)->{
    System.out.println("change: "+getIndex()+", newv "+(newv != null?newv.getIsbn():"null")+", oldv: "+(oldv != null?oldv.getIsbn():"null"));
}

protected void updateItem(Book item, boolean empty){
        super.updateItem(item, empty);
        System.out.println("update item: "+(item!=null?item.getIsbn():"null"));
}

对于第一本书“123”,这是跟踪:

update item: null
change: 0, newv 123, oldv: null
update item: 123
change: -1, newv null, oldv: 123
update item: null
update item: null
change: 0, newv 123, oldv: null
update item: 123

由于新单元的创建,第一个更改按预期发生。但一旦创建,就会调用VirtualFlow.releaseCell()索引为-1,最后all细胞被重新重建addLeadingCells() and addTrailingCells() in VirtualFlow.layoutChildren().

因此,根据设计,这些调用是无法避免的,这意味着如果您想确保只调用一次动画,则动画不应该位于侦听器中。

动画片

我提出了一个解决方案,意味着获取单元格并运行动画:

addButton.setOnAction((event)->{
    data.add(new Book(String.valueOf(newBookIndex++),"test"));

    VirtualFlow vf=(VirtualFlow)lv.lookup(".virtual-flow");
    BookCell cell = (BookCell)vf.getCell(lv.getItems().size()-1);
    cell.runAnimation();
});

这不是非常可取,因为使用VirtualFlow,私有API。

通过查找 CSS 选择器可以避免这种情况indexed-cells反而:

addButton.setOnAction((event)->{
    data.add(new Book(String.valueOf(newBookIndex),"test"));

    lv.lookupAll(".indexed-cell").stream()
            .map(n->(BookCell)n)
            .filter(c->c.getBook().getIsbn().equals(String.valueOf(newBookIndex)))
            .findFirst().ifPresent(BookCell::runAnimation);
    newBookIndex++;
});

请注意,由于我们使用查找,因此应在显示舞台后添加按钮的事件处理程序。

EDIT

OP 报告了滚动方面的奇怪问题。我不确定这是否是一个错误,但顶部的单元格是动画的,而不是底部的。

为了解决这个问题,我提出了这个解决方法,删除BookCell动画,并为单元格创建一个动画,使用VirtualFlow获取单元格并在必要时滚动。

首先,我们尝试查找滚动条是否不可见:我们使用淡入淡出过渡。但如果我们有滚动条,现在我们需要调用show()滚动到最后一个单元格。一个短PauseTransition在启动淡入淡出过渡之前完成这个技巧。

addButton.setOnAction((event)->{
    addButton.setDisable(true);
    data.add(new Book(String.valueOf(newBookIndex),"test"));

    VirtualFlow vf=(VirtualFlow)lv.lookup(".virtual-flow");
    if(!lv.lookup(".scroll-bar").isVisible()){
        FadeTransition f = new FadeTransition();
        f.setDuration(Duration.seconds(1));
        f.setFromValue(0);
        f.setToValue(1);
        f.setNode(vf.getCell(lv.getItems().size()-1));
        f.setOnFinished(t->{
            newBookIndex++;
            addButton.setDisable(false);                        
        });
        f.play();
    } else {
        PauseTransition p = new PauseTransition(Duration.millis(20));
        p.setOnFinished(e->{
            vf.getCell(lv.getItems().size()-1).setOpacity(0);
            vf.show(lv.getItems().size()-1);
            FadeTransition f = new FadeTransition();
            f.setDuration(Duration.seconds(1));
            f.setFromValue(0);
            f.setToValue(1);
            f.setNode(vf.getCell(lv.getItems().size()-1));
            f.setOnFinished(t->{
                newBookIndex++;
                addButton.setDisable(false);                        
            });
            f.play();
        });
        p.play();
    }
});

更新的代码

最后,这是我使用过的所有代码:

public class Main extends Application {

    private int newBookIndex = 2;        
    public final ObservableList<Book> data =  FXCollections.observableArrayList(
            new Book("123","Hugo"), new Book("456","Harry Potter"));
    private final ListView<Book> lv = new ListView<>();   

    @Override
    public void start(Stage primaryStage) {
        Button addButton = new Button("Add Book");

        lv.setCellFactory(param->new BookCell());
        lv.setItems(data);
        Scene myScene = new Scene(new VBox(10,lv,addButton), 200, 200);
        myScene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(myScene);
        primaryStage.show();

        addButton.setOnAction((event)->{
            addButton.setDisable(true);
            data.add(new Book(String.valueOf(newBookIndex),"test"));

            VirtualFlow vf=(VirtualFlow)lv.lookup(".virtual-flow");
            if(!lv.lookup(".scroll-bar").isVisible()){
                FadeTransition f = new FadeTransition();
                f.setDuration(Duration.seconds(1));
                f.setFromValue(0);
                f.setToValue(1);
                f.setNode(vf.getCell(lv.getItems().size()-1));
                f.setOnFinished(t->{
                    newBookIndex++;
                    addButton.setDisable(false);                        
                });
                f.play();
            } else {
                PauseTransition p = new PauseTransition(Duration.millis(20));
                p.setOnFinished(e->{
                    vf.getCell(lv.getItems().size()-1).setOpacity(0);
                    vf.show(lv.getItems().size()-1);
                    FadeTransition f = new FadeTransition();
                    f.setDuration(Duration.seconds(1));
                    f.setFromValue(0);
                    f.setToValue(1);
                    f.setNode(vf.getCell(lv.getItems().size()-1));
                    f.setOnFinished(t->{
                        newBookIndex++;
                        addButton.setDisable(false);                        
                    });
                    f.play();
                });
                p.play();
            }
        });
    }

    class BookCell extends ListCell<Book>{
        private final Text text = new Text();
        private final HBox h = new HBox(text);

        {
            getStyleClass().add("book-list-cell");
        }

        @Override
        protected void updateItem(Book item, boolean empty){
            super.updateItem(item, empty);
            if(item!=null && !empty){
                text.setText(item.getIsbn());
                setGraphic(h);
            } else {
                setGraphic(null);
                setText(null);
            }
        }
    }

    class Book {
        private Book(String isbn, String name) {
            this.isbn.set(isbn);
            this.name.set(name);
        }

        private final StringProperty name = new SimpleStringProperty();

        public String getName() {
            return name.get();
        }

        public void setName(String value) {
            name.set(value);
        }

        public StringProperty nameProperty() {
            return name;
        }

        private final StringProperty isbn = new SimpleStringProperty();

        public String getIsbn() {
            return isbn.get();
        }

        public void setIsbn(String value) {
            isbn.set(value);
        }

        public StringProperty isbnProperty() {
            return isbn;
        }

    }

    public static void main(String[] args) {
        launch(args);
    }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Javafx 中为新的 ListView 条目添加动画 的相关文章

  • SAML 服务提供商 Spring Security

    当使用预先配置的服务提供者元数据时 在 Spring Security 中 是否应该有 2 个用于扩展元数据委托的 bean 定义 一份用于 IDP 元数据 一份用于 SP 元数据
  • Java:如何从转义的 URL 获取文件?

    我收到了一个定位本地文件的 URL 事实上我收到的 URL 不在我的控制范围内 URL 按照 RFC2396 中的定义进行有效转义 如何将其转换为 Java File 对象 有趣的是 URL getFile 方法返回一个字符串 而不是文件
  • Android在排序列表时忽略大小写

    我有一个名为路径的列表 我目前正在使用以下代码对字符串进行排序 java util Collections sort path 这工作正常 它对我的 列表进行排序 但是它以不同的方式处理第一个字母的情况 即它用大写字母对列表进行排序 然后用
  • 比较两个文本文件的最快方法是什么,不将移动的行视为不同

    我有两个文件非常大 每个文件有 50000 行 我需要比较这两个文件并识别更改 然而 问题是如果一条线出现在不同的位置 它不应该显示为不同的 例如 考虑这个文件A txt xxxxx yyyyy zzzzz 文件B txt zzzzz xx
  • java中如何连接字符串

    这是我的字符串连接代码 StringSecret java public class StringSecret public static void main String args String s new String abc s co
  • 如何安全地解决这个 Java 上下文类加载器问题?

    我的数百名用户中只有一位在启动我的 Java 桌面应用程序时遇到问题 他只有大约三分之一的时间开始 另外三分之二的时间在启动时抛出 NullPointerException Exception in thread AWT EventQueu
  • Java 文件上传速度非常慢

    我构建了一个小型服务 它从 Android 设备接收图像并将其保存到 Amazon S3 存储桶中 代码非常简单 但是速度非常慢 事情是这样的 public synchronized static Response postCommentP
  • 在 S3 中迭代对象时出现“ConnectionPoolTimeoutException”

    我已经使用 aws java API 一段时间了 没有遇到太多问题 目前我使用的是库 1 5 2 版本 当我使用以下代码迭代文件夹内的对象时 AmazonS3 s3 new AmazonS3Client new PropertiesCred
  • hibernate锁等待超时超时;

    我正在使用 Hibernate 尝试模拟对数据库中同一行的 2 个并发更新 编辑 我将 em1 getTransaction commit 移至 em1 flush 之后我没有收到任何 StaleObjectException 两个事务已成
  • 匿名类上的 NotSerializedException

    我有一个用于过滤项目的界面 public interface KeyValFilter extends Serializable public static final long serialVersionUID 7069537470113
  • Java 8 流 - 合并共享相同 ID 的对象集合

    我有一系列发票 class Invoice int month BigDecimal amount 我想合并这些发票 这样我每个月都会收到一张发票 金额是本月发票金额的总和 例如 invoice 1 month 1 amount 1000
  • 编辑文件名在 JComboBox 中的显示方式,同时保持对文件的访问

    我对 Java 很陌生 对堆栈溢出也很陌生 我正在尝试利用 JMF API 创建一个用 Java 编码的简单媒体播放器 到目前为止 我已经能够设置一个简单的队列 播放列表来使用JComboBox called playListHolder
  • AS3如何在角色死亡动画结束时转到MainTimeline中的下一帧

    所以我需要知道我的角色 鸟 是否用管道 hitTestObject 在动画结束后播放骰子动画 它需要在主时间轴中转到游戏结束帧 if bird hitTestObject pipe1 bird gotoAndStop 3 frame 3 w
  • Struts 2 + Sitemesh 3 集成 - FreemarkerDecoratorServlet 中的 NPE

    我将 Struts 2 版本 2 3 14 3 与 Sitemesh 3 版本 3 0 alpha 2 一起使用 并且在某些情况下遇到 NullPointerException 首先 这是我的 web xml 中的 struts2 site
  • 在 Spring 中重构这个的最佳方法?

    private final ExecutorService executorParsers Executors newFixedThreadPool 10 public void parse List
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • 将 Azure AD 高级自定义角色与 Spring Security 结合使用以进行基于角色的访问

    我创建了一个演示 Spring Boot 应用程序 我想在其中使用 AD 身份验证和授权 并使用 AD 和 Spring Security 查看 Azure 文档 我执行了以下操作 package com myapp contactdb c
  • Java中的Object类是什么?

    什么是或什么类型private Object obj Object http download oracle com javase 6 docs api java lang Object html是Java继承层次结构中每个类的最终祖先 从
  • JavaFX ImageView 未更新

    因此 我尝试将图像加载并保存到 imageView 中 其中图像的位置是通过文件浏览器选择的 我已经为此工作好几天了 如果我不能解决这个问题 我就会中风 我已经尝试了我能想到的一切 预先感谢您的帮助 UPDATED 这是我的主要课程 pub
  • 如何从 Maven 存储库引用本机 DLL?

    如果 JAR 附带 Maven 存储库中的本机 DLL 我需要在 pom xml 中放入什么才能将该 DLL 放入打包中 更具体地举个例子Jacob http search maven org artifactdetails 7Cnet s

随机推荐

  • Eclipse Subversive 提交变更列表?

    我刚刚创建了一个忽略提交更改列表 如中所述SVN 有没有办法将文件标记为 不提交 https stackoverflow com questions 635446 svn is there a way to mark a file as d
  • ReferenceError: $ 未定义 yii2

    在我的视图中添加 JavaScript 会导致ReferenceError is not defined 我认为问题是由于 Yii2 最后在我的页面上注入脚本造成的 如何解决这个问题 或者如何阻止 Yii2 自动加载脚本文件 My view
  • 从 .NET 调用 Java/AXIS Web 服务:“返回 null”问题

    我一直在通过谷歌 stackoverflow 等寻找这个问题 我找到了很多相关的答案 但没有真正的解决方案 我正在从 NET 客户端使用 Axis 服务 但返回始终为 null 无论我发送什么参数 始终为 null 所以我开始寻找 并尝试从
  • 在单元测试中比较字典时如何忽略某些值?

    我想断言两个字典是相等的 使用Python的unittest https docs python org 3 library unittest html 但忽略字典中某些键的值 采用方便的语法 如下所示 from unittest impo
  • 成功将分页 JSON 对象强制转换为 R 数据帧

    我正在尝试将从 API 中提取的 JSON 转换为 R 中的数据帧 以便我可以使用和分析数据 Install needed packages require RJSONIO require httr request a list of co
  • 今天查看扩展(小部件)无法正常工作

    我发现其他几个线程也有类似的问题 但没有人遇到完全相同的问题 除此之外 它确实工作了一段时间 现在 在之前工作一段时间时 错误不断发生 当运行我的应用程序时 它有一个构建目标 Today View Extension 我没有得到实际结果 该
  • 动态显示tinymce文本区域

    我有一个下拉列表 当进行选择时 将使用 ajax 将表单中的一堆元素插入到 DOM 在此表单中 我有一些文本区域 我希望将其设为 TinyMCE 文本区域 我的 HTML 头中有这样的内容 这是我用来添加一堆元素的 ajax 函数 它正在按
  • `pip install opencv-python` 是什么意思,它是一个完整的 opencv 吗?

    我认为让 opencv 在我的环境中运行的最快方法就像这样简单 sudo pip install opencv python 它似乎工作正常 我可以导入 import cv2 img cv2 imread a jpg 0 但不会加载 cv2
  • Magento 静态块。去除包装

    当我创建静态块时 magento 将内容包装为 p 标签 这对于 DOM 来说非常糟糕 有可能以某种方式将其删除 我想这是一些 JavaScript 但我不知道是哪一个 其实我之前的回答是错误的 您需要默认关闭静态块所见即所得编辑器 Go
  • 如何将按钮值发布到 PHP?

    我想在 html 页面上使用 A Z 按钮 如下所示 仅示例和几个单词
  • ASP Classic 下载文件脚本

    我有一个用 ASP Classic 构建的网站 并且在使用允许用户下载文件但隐藏文件路径的脚本时遇到一些问题 当用户在页面上时 他们将看到一个链接 该链接的编码如下 a href download asp file FILE NAME HE
  • Javascript正则表达式日期提取和分组问题

    我有这些行的文本行 Il Messaggero Roma 22 settembre 2023 Il Messaggero Roma 21 settembre 2023 Il Messaggero 22 settembre 2023 Il M
  • 异常类型可以通用吗?

    我尝试过以下方法 但不起作用 exception MyError lt a gt of a exception a MyError of a 我必须使用长形式吗 type MyError lt a gt value inherit Syst
  • 有人可以通过某种方式操作客户端应用程序来访问我的 Firestore 数据库吗?

    我真的很担心我将存储在 Firestore 中的数据的安全性 我想知道是否有人可以从我的 Android 应用程序中提取 google services json 文件或使用其他一些工具来访问我的 Firestore 数据库 有可能吗 如果
  • 安装rails时找不到Gem存储库

    我的 Windows 计算机上安装了 Ruby 1 8 7 和 Ruby 1 9 2 当我这样做时在我的控制台中ruby v它给了我 Ruby 1 8 7 现在当我尝试时 gem install rails v 2 3 8 我收到这个错误
  • 当 WPF ProgressBar 达到 100% 时,如何停止它的脉冲/动画?

    我有一个基于 MVVM 的 WPF 4 应用程序 它使用进度条 http msdn microsoft com en us library system windows controls progressbar aspx显示长时间运行的操作
  • C++ 运算符 []

    我正在尝试实现运算符 该运算符用于 Set 一次 用于 Get 一次 我需要区分这两种情况 就像 get 的情况一样 如果返回值相等 我需要抛出异常至 1 而在 Set 的情况下 我只是覆盖该值 苹果 2 X y 苹果 2 我不知道如何区分
  • 伊莎贝尔:setprod 的问题

    以下等式在伊莎贝尔中是否成立 setprod f UNIV n finite set setprod x x f UNIV n finite set 如果是 我该如何证明 tested with Isabelle2013 2 theory
  • 操作系统错误:没有这样的文件或目录,errno = 2

    我无法加载存储在资产文件夹中的 html 文件 我已经搜索了两天了 但似乎无法找出原因 我已确保将其也包含在 pubspec yaml 中 并具有适当的缩进并且所有构建都很好 我在 pubspec yaml 中加载的图像资源加载没有问题 所
  • 在 Javafx 中为新的 ListView 条目添加动画

    问题 Hi 我正在尝试编写一个应用程序 其中 ListView 中的每个新条目都会动画化 这是我的代码 public class BookCell extends ListCell