【问题标题】:JavaFX - ListView scroll one by one element when clicking down or up in the scrollbarJavaFX - 在滚动条中向下或向上单击时,ListView 会一一滚动
【发布时间】:2019-08-10 11:50:52
【问题描述】:

我有一个包含 16 个项目的 ListView。当我滚动默认 ListView ScrollBar(使用箭头或鼠标)时,它会滚动一半项目。

我需要的是滚动一项(单击箭头或用鼠标滚动时),这样列表就不会显示一半的项目。有没有办法做到这一点? 这是我的代码:

public void start(Stage primaryStage) throws IOException {
    ListView<String> lstRequestOpt = new ListView<>();
    List<String> x = new ArrayList<>();
    for(int i = 0; i < 30; i++) {
        x.add("item " + i);
    }
    ObservableList<String> items = FXCollections.observableArrayList(x);
    lstRequestOpt.setItems(items);
    Scene scene = new Scene(lstRequestOpt);
    primaryStage.setScene(scene);
    primaryStage.show();
}

这是输出图像: ListView with half items

我在这里想要实现的是元素显示完整,而不像他的图片:

  • 滚动时
  • 用鼠标移动滚动条时
  • 单击滚动条中的箭头时

有什么办法吗?

更新:

我可以设法创建一个在使用滚动或箭头时打印某些内容的方法,即使它是最小的滚动。现在我需要找到一种方法来设置滚动中的移动值并将其与 ListView 集成。我会继续努力。代码如下:

for (Node node : lstRequestOpt.lookupAll(".scroll-bar")) {
        if (node instanceof ScrollBar) {
            final ScrollBar bar = (ScrollBar) node;
            bar.valueProperty().addListener(new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> value, Number oldValue, Number newValue) {
                    if ((double) oldValue < (double) newValue) {
                        System.out.println("UP");
                    } else {
                        System.out.println("DOWN");
                    }
                }
            });
        }
    }

感谢所有帮助我的人。

【问题讨论】:

标签: listview javafx


【解决方案1】:

我有一个非常老套的方法。不确定这是否适合您。但最终意图是不逐个滚动像素。

思路是监听垂直滚动条的value属性,根据当前滚动条的值设置ListView滚动。下面的方法可以给你一些输入。一个缺点是我需要依赖 VirtualFlow。

import com.sun.javafx.scene.control.skin.VirtualFlow;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.AccessibleAttribute;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;

public class ListViewScrollDemo  extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        StackPane root = new StackPane();
        Scene sc = new Scene(root, 600, 200);
        stage.setScene(sc);
        stage.show();
        List<String> items = new ArrayList<>();
        for (int i = 1; i < 21; i++) {
            items.add("Item "+i);
        }
        ObservableList<String> itemList = FXCollections.observableArrayList();
        itemList.addAll(items);
        ScrollListView<String> list = new ScrollListView<>();
        list.setItems(itemList);
        root.getChildren().add(list);
    }

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

    class ScrollListView<T> extends ListView<T>{
        boolean firstRender = false;
        public ScrollListView() {
            needsLayoutProperty().addListener((obs,old,needsLayout)->{
                if(!firstRender && !needsLayout){
                    firstRender = true;
                    VirtualFlow<?> virtualFlow = (VirtualFlow<?>) lookup(".virtual-flow");

                    // Keeping vertical scrollBar node reference and tweaking the vertical scroll bar behavior to
                    // scroll by row and not by pixel.
                    ScrollBar vScrollBar = (ScrollBar) queryAccessibleAttribute(AccessibleAttribute.VERTICAL_SCROLLBAR);
                    vScrollBar.valueProperty().addListener((obs1, oldVal1, newVal) -> {
                        int visibleRowCount = virtualFlow.getLastVisibleCell().getIndex()-virtualFlow.getFirstVisibleCell().getIndex();
                        final double scrollVal = newVal.doubleValue();
                        final int size = getItems().size();
                        if (scrollVal < 1.0) {
                            final int virtualRowCount = size - visibleRowCount;
                            final double eachRowBuff = 1d / virtualRowCount;
                            for (int index = 0; index < virtualRowCount; index++) {
                                final double start = eachRowBuff * index;
                                final double end = start + eachRowBuff;
                                if (start <= scrollVal && scrollVal < end) {
                                    scrollTo(index);
                                    vScrollBar.setValue(start);
                                    break;
                                }
                            }
                        } else {
                            scrollTo(size - 1);
                        }
                    });
                }
            });
        }
    }
}

【讨论】:

  • 不错的尝试(为此 +1 :) - 但不可靠(至少在 fx11 中不):a)在这个确切的例子中,由于固定大小的舞台(相对于让场景出现在其首选项)。 b)有更多项目(比如 100 个)和首选项,一切似乎都按预期工作...... c)......只有一段时间:有虚假的人工制品 f.i.当通过单击滚动条向上箭头向上滚动时,第一行看起来上面有一些空白空间 - 这可能是内部状态混乱和不完整的单元格重用的结果(没有挖掘)但是问题也是如此模糊..
  • @kleopatra,感谢您的反馈。是的,我也意识到这种方法存在一些问题。因为我使用它的上下文完全不同(固定的可见行数,固定的表格高度等),它完全适合我的目的。我认为提供我的意见可能有助于提问者朝那个方向思考以获得更好的解决方案。
【解决方案2】:

试试这个:

  ListView listView = new ListView();
    listView.setItems(listConetnt);

    EventHandler onePositionScrollEvent = (EventHandler<ScrollEvent>) event -> {
        int scrollIndex = 0;

        if (event.getDeltaY() > 0) {
            scrollIndex = listView.getSelectionModel().getSelectedIndex() - 1;
        } else {
            scrollIndex = listView.getSelectionModel().getSelectedIndex() + 1;
        }

        listView.getSelectionModel().select(scrollIndex);
    };

    listView.setOnScroll(onePositionScrollEvent);

【讨论】:

  • 这真是太好了。但是,当我滚动时,我需要在滚动时保留我的最后一个选择,并且您评论的方式会在滚动时选择一个选项。如果滚动很小,滚动事件也不会触发,这在我的情况下很重要。还是谢谢你。
  • 我不确定是否正确理解...您是否希望滚动列表一个元素而不更改选择?
  • 是的。滚动时,我唯一的兴趣是查看元素是否完整而不是减半,但不更改选择。
猜你喜欢
  • 1970-01-01
  • 2021-09-01
  • 2023-04-10
  • 2013-05-23
  • 2019-05-17
  • 2014-10-06
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
相关资源
最近更新 更多