当前位置: 首页 > 知识库问答 >
问题:

如何让tableRow闪烁,即使它不可见?(JavaFx)

桓风史
2023-03-14

起初我的问题是如何让一个新添加的行在JavaFx中闪烁,然后我经历了很多与这个主题相关的问题(例如javafx:表行闪烁)。他们中的大多数人都在使用setRowFactory并通过添加时间轴动画来覆盖updateItem方法,该动画改变了行的伪类状态。下面是我的代码,我正在尝试构建一个可以重用的FlashControl。

public class TableFlashControl<T> {

    private PseudoClass flashHighlight = PseudoClass.getPseudoClass("flash-highlight");
    private List<T> newAdded = new ArrayList<>();
    private boolean isFilterApplied = false;
    private boolean isSorted = false;

    public void setIsFilterApplied(boolean isFilterApplied) {
        this.isFilterApplied = isFilterApplied;
    }

    public void add(TableView<T> table){
        ListChangeListener<T> change = c -> {
            while (c.next()) {
                if (c.wasAdded()) {
                    List<? extends T> added = c.getAddedSubList();
                    T lastAdded = added.get(0);
                    if (!isFilterApplied) {
                        newAdded.add(lastAdded);
                    }
                }
            }
        };
        table.getItems().addListener(change);
        table.setRowFactory(param -> new TableRow<T>() {
            @Override
            protected void updateItem(T item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null || empty) {
                    return;
                }
                if (newAdded.contains(item)) {
                    if (isSorted) {
                        new Thread(()->{
                            Timeline flasher = new Timeline(
                                    new KeyFrame(Duration.seconds(0.4), e -> pseudoClassStateChanged(flashHighlight, true)),
                                    new KeyFrame(Duration.seconds(0.8), e -> pseudoClassStateChanged(flashHighlight, false))
                            );
                            flasher.setCycleCount(2);
                            flasher.play();
                        }).start();
                        if (item == newAdded.get(0)) {
                            newAdded.clear();
                            isSorted = false;
                        }

                    }else{
                        if(item == newAdded.get(0)){
                            isSorted = true;
                        }

                    }
                }

            }
        });
    }
}

在这里,ListChangeListener与table.getItems()相关联,这有助于我记录新插入的行。

可以在一个操作中添加多行,这意味着newAdded.size()可以大于1。此外,行是从tableView的顶部插入的(因为我使用Number对其进行排序。)

在tableView中,并非所有行都是可见的,updateItem方法只更新那些可见的行。当这两种情况发生时,我的问题就来了(见下文)。

第一种情况

在第一种情况下,只有4行可见,如果用户在一次内插入5行,我无法记录最后一行更新(new_row_5不会调用updateItem)。因此,我无法清除newAdded列表(通过执行 newAdded.clear())

第二种情况

在第二种情况下,只有 4 行再次可见。但是,在可见行的顶部和底部都有不可见的行。如果用户插入 2 行,则一行可见,另一行不可见。就我而言,new_row_2会闪烁,而new_row_1仍然不可见。如果用户在new_row_2闪烁时向上滚动 tableView,他将看到new_row_2在闪烁,而new_row_1不是,这真的很奇怪。

我还想知道是否有任何方法可以找到可见行数。

我还是JavaFx的新手,我不知道这个方法是否好。我希望有人能帮我解决问题。谢谢!

共有1个答案

严安志
2023-03-14

你的方法看起来不像是一个干净的方法。动画取决于项目所在的< code>TableRow,并且似乎不支持同时出现多个动画。此外,它依赖于item类的< code>equals方法不会被覆盖,并且依赖于用户不会将一个项目多次添加到< code>TableView中。此外,您可能会创建大量的< code>Timeline(不必从单独的线程btw启动它们,因为< code>Timeline.play()不会阻塞)。

最好使动画依赖于索引。此外,跟踪创建的 TableRow还允许您访问现有单元格,如果为它们分配了需要动画的索引。您也可以使用单个动画计时器处理动画,方法是将数据存储在合适的数据结构中。

此外,使用< code>rowFactory类来实现这个逻辑也是最方便的。

以下示例使行闪烁,无论它们是否在屏幕上。

public class FlashTableRowFactory<T> implements Callback<TableView<T>, TableRow<T>> {

    private final static PseudoClass FLASH_HIGHLIGHT = PseudoClass.getPseudoClass("flash-highlight");

    public FlashTableRowFactory(TableView<T> tableView) {
        tableView.getItems().addListener((ListChangeListener.Change<? extends T> c) -> {
            while (c.next()) {
                if (c.wasPermutated()) {
                    int from = c.getFrom();
                    int to = c.getTo();
                    permutationUpdate(scheduledTasks, c, from, to);
                    permutationUpdate(unscheduledTasks, c, from, to);
                }
                if (c.wasReplaced()) {
                    addRange(c.getFrom(), c.getTo());
                } else if (c.wasRemoved()) {
                    int from = c.getFrom();
                    int removed = c.getRemovedSize();
                    removeRange(scheduledTasks, from, from + removed);
                    removeRange(unscheduledTasks, from, from + removed);
                    modifyIndices(unscheduledTasks, from, -removed);
                    modifyIndices(scheduledTasks, from, -removed);
                } else if (c.wasAdded()) {
                    int from = c.getFrom();
                    int to = c.getTo();
                    modifyIndices(unscheduledTasks, from, to - from);
                    modifyIndices(scheduledTasks, from, to - from);
                    addRange(from, to);
                }
            }

            // remove all flashTasks that are older than the youngest for a
            // given index
            Set<Integer> indices = new HashSet<>();
            removeDuplicates(unscheduledTasks, indices);
            removeDuplicates(scheduledTasks, indices);

            flashingIndices.clear();
            updateFlash(lastUpdate);
            refreshFlash();
            if (!unscheduledTasks.isEmpty()) {
                flasher.start();
            }
        });
        this.tableView = tableView;
    }

    private static void removeDuplicates(List<FlashTask> list, Set<Integer> found) {
        for (Iterator<FlashTask> iterator = list.iterator(); iterator.hasNext();) {
            FlashTask next = iterator.next();
            if (!found.add(next.index)) {
                iterator.remove();
            }
        }
    }

    private static void modifyIndices(List<FlashTask> list, int minModify, int by) {
        for (FlashTask task : list) {
            if (task.index >= minModify) {
                task.index += by;
            }
        }
    }

    private void addRange(int index, int to) {
        for (; index < to; index++) {
            unscheduledTasks.add(new FlashTask(index));
        }
    }

    private static void removeRange(List<FlashTask> list, int from, int to) {
        for (Iterator<FlashTask> iterator = list.iterator(); iterator.hasNext();) {
            FlashTask next = iterator.next();
            if (next.index >= from && next.index < to) {
                iterator.remove();
            }
        }
    }

    private static void permutationUpdate(List<FlashTask> list, ListChangeListener.Change<?> c, int from, int to) {
        for (FlashTask task : list) {
            if (task.index < to && task.index >= from) {
                task.index = c.getPermutation(task.index);
            }
        }
    }

    // set of item indices that should flash
    private final Set<Integer> flashingIndices = new HashSet<>();

    // references to every row created by this factory
    private final List<SoftReference<TableRow<T>>> rows = new LinkedList<>();

    // tasks waiting to be scheduled
    private final List<FlashTask> unscheduledTasks = new LinkedList<>();

    // tasks currently being animated
    private final List<FlashTask> scheduledTasks = new LinkedList<>();

    private static class FlashTask {

        int index;
        long schedulingTime;

        public FlashTask(int index) {
            this.index = index;
        }

    }

    private final TableView<T> tableView;
    private long lastUpdate;

    /**
     * Updates flashingIndices set
     * @param now the current timestamp
     * @return true if the set has been modified, otherwise false.
     */
    private boolean updateFlash(long now) {
        boolean modified = false;
        for (Iterator<FlashTask> iterator = scheduledTasks.iterator(); iterator.hasNext();) {
            FlashTask next = iterator.next();

            // running time in seconds
            double runningTime = (now - next.schedulingTime) / (1000d * 1000d * 1000d);

            // slows down the animation for demonstration
            final double animationSpeed = 0.1;

            if (runningTime < 0.4 / animationSpeed) {
                // no need to handle tasks that run for less than 0.4 seconds
                break;
            } else if (runningTime > 1.6 / animationSpeed) {
                // end of task reached
                iterator.remove();
                modified |= flashingIndices.remove(next.index);
            } else if (runningTime > 0.8 / animationSpeed && runningTime < 1.2 / animationSpeed) {
                // second "inactive" interval during animation
                modified |= flashingIndices.remove(next.index);
            } else {
                // activate otherwise
                modified |= flashingIndices.add(next.index);
            }
        }
        return modified;
    }

    private final AnimationTimer flasher = new AnimationTimer() {

        @Override
        public void handle(long now) {
            lastUpdate = now;

            // activate waiting flash tasks
            for (FlashTask task : unscheduledTasks) {
                task.schedulingTime = now;
            }
            scheduledTasks.addAll(unscheduledTasks);
            unscheduledTasks.clear();

            if (updateFlash(now)) {
                refreshFlash();
                if (scheduledTasks.isEmpty()) {
                    // stop, if there are no more tasks
                    stop();
                }
            }
        }

    };

    /**
     * Sets the pseudoclasses of rows based on flashingIndices set
     */
    private void refreshFlash() {
        for (Iterator<SoftReference<TableRow<T>>> iterator = rows.iterator(); iterator.hasNext();) {
            SoftReference<TableRow<T>> next = iterator.next();
            TableRow<T> row = next.get();
            if (row == null) {
                // remove references claimed by garbage collection
                iterator.remove();
            } else {
                row.pseudoClassStateChanged(FLASH_HIGHLIGHT, flashingIndices.contains(row.getIndex()));
            }
        }
    }

    @Override
    public TableRow<T> call(TableView<T> param) {
        if (tableView != param) {
            throw new IllegalArgumentException("This factory can only be used with the table passed to the constructor");
        }
        return new FlashRow();
    }

    private class FlashRow extends TableRow<T> {

        {
            rows.add(new SoftReference<>(this));
        }

        @Override
        public void updateIndex(int i) {
            super.updateIndex(i);

            // update pseudoclass based on flashingIndices set
            pseudoClassStateChanged(FLASH_HIGHLIGHT, flashingIndices.contains(i));
        }

    }

}
 类似资料:
  • 在重定向的情况下,Flash消息似乎会被破坏。我编写了简单的测试代码: 我调用不带参数的操作,它会添加一个flash并重定向。当它呈现页面时-flash不存在。 视图部分很好,因为如果我设置flash并在不重定向的情况下进行渲染,它会正确渲染。 为什么? 编辑:布局视图代码:

  • 我怎样才能使背景色在每次按下按钮时连续闪现2个新颜色?现在它只显示两种颜色,但它们不会连续闪烁或闪烁。

  • 这是因为在开发模式下,为了通过 Webpack 实现热加载,CSS代码是打包在 JavaScript 代码中,并动态打到页面中去,从而元素重绘引起了闪烁。 不用担心,在生产模式下,CSS代码会单独打包至独立的文件并置于head标签内,不会出现页面闪烁的现象。

  • 本文向大家介绍如何使用JavaScript创建闪烁文本?,包括了如何使用JavaScript创建闪烁文本?的使用技巧和注意事项,需要的朋友参考一下 要创建闪烁的文本,请使用JavaScript方法。此方法使字符串闪烁,就像在BLINK标记中一样。 注–不推荐使用HTML <blink>标记,并且并非在所有浏览器中都适用。 示例 您可以尝试运行以下代码以使用JavaScript方法创建闪烁的文本-

  • 我在Lollipop上的共享元素转换中看到了奇怪的事情。共享元素在开始动画之前闪烁(请看视频https://www.youtube.com/watch?v=DCoyyC_S-9A) 我不知道为什么会这样。但是,当我添加

  • 我在约束布局中有一些视图。我使用动画来显示和隐藏视图。当视图消失时,它会闪烁一秒钟,然后变为可见,然后消失。

  • 我试图用pyplay制作一个游戏,我几乎完成了,但我想让被画在墙上的盒子不闪烁,这些红色的盒子在整个游戏中闪烁,我不想让它们闪烁,最后,我在一个if条件下调用player碰撞函数,在这里每当我制作新的碰撞器时,我每次都要在if条件下添加函数,我想要的是碰撞器对象自动调用这个函数,而不需要我在if语句中为碰撞器的每个实例调用它对象。请指导我如何这样做。

  • 减低闪烁    以Interlace(交错扫描)方式在电视机输出PSP™规格软件的影像时,设定是否要减低画面的闪烁。 关 不减低画面的闪烁。 开 减低画面的闪烁。