JavaFX的ListView控件:检索行的位置 [英] JavaFX ListView: retrieve position of a row

查看:1265
本文介绍了JavaFX的ListView控件:检索行的位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否JavaFX的ListView控件有它返回行,它认为目前区所示的特性?还是有分配一个事件处理程序来发送时(被添加到列表由于新对象或从列表中移除对象)在列表中的位置的变化的情况下的特定行的方法吗?

Does the JavaFX ListView have a property which returns which rows are currently shown in its view area? Or is there a way to assign an EventHandler to a specific row that sends the event when its position in the list changes (due to new objects being added to the List or objects being removed from the list)?

在本质我需要知道的x,选定行的y坐标知道,如果所选择的行是关闭当前视图区域。因为如果出现这种情况,我需要调用ListView.scrollTo()方法来把焦点回到选定行。如果用户选择一个行,则程序增加了更多的对象,以产生将推动任何现有的对象,包括所选择的一个,关闭视图区域列表,就可能出现这种情况。

In essence I need to know the x,y coordinates of a selected row to know if a selected row is off the current view area. Because if that happens I need to call the ListView.scrollTo() method to give focus back to the selected row. And this situation can occur if a user selects a row then the program adds more objects to the list which would push any existing objects, including the selected one, off the view area.

我尝试添加一个ListChangeListener下可观察名单,但似乎我不能适用于特定的行。这ListChangeListener如果出现了一排,只注明添加/删除/更新/更换/置换的等等。这事件处理函数/侦听器似乎并没有给一个x,具体的行y坐标,也不给一些排序布尔指示该行是在ListView对象的当前视图区域内。

I tried adding a ListChangeListener to the observable list below, but it seems like I can't apply that to a specific row. And that ListChangeListener appears to only indicate if a row was "added/removed/updated/replaced/permutated" etc. That event handler/listener doesn't seem to give an x,y coordinate of the specific row, nor does it give some sort of boolean indication that the row is within the current view area of the ListView object.

observableList.addListener(new ListChangeListener<Object>()
{
   @override
   public void onChanged(ListChangeListener.Change<? extends Object> c)
       {
           while(c.next())
           {
             if(c.wasAdded())
             {
             }
             else if(c.wasRemoved())
             {
             }
             .....
           }
       }
    }

我也看到,获得 ListViews.getSelectionModel()。getSelectedItem()的getObject()。getLayoutX()或LayoutY()
出于某种原因,总是返回0,0 x-y坐标。

I also see that getting the ListViews.getSelectionModel().getSelectedItem().getObject().getLayoutX() or LayoutY() for some reason always returns 0,0 for x-y coordinates.

这似乎给我有效的x-y坐标的唯一的事情是,当我点击一个行和OnMouseClickEvent(事件的MouseEvent)回调开始。该事件返回X,其点击位置的y位置。但不幸的是,不给我所选择的行的x-y位置,如果在ListView动态地添加新对象列表的顶部和选择行的改变的位置。

The only thing that appears to give me valid x-y coordinates is when I click on a row and the OnMouseClickEvent(MouseEvent event) callback is initiated. The event returns the x,y position of where its clicked. But unfortunately that doesn't give me the x-y position of a selected row if the ListView dynamically adds new objects to the top of the list and the position of the selected row is changed.

任何意见将大大AP preciated。

Any ideas would be greatly appreciated.

推荐答案

有是,据我所知,没有API来做到这一点。添加此功能是有点棘手,因为该单元复用机制(在的 细胞文档)。

There is, to my knowledge, no API to do this. Adding this functionality is a bit tricky, because of the cell reuse mechanism (described in the Cell documentation).

您需要在持续跟踪该列表中的项目有活跃的细胞,以及这些细胞的边界是(因为它滚动,某个项目可能仍然有一个细胞中,可以在列表视图中使用单元格工厂的,但该小区可以被完全滚出视)。你可以用 ObservableMap 做到这一点的地图项目其细胞的边界。您需要更新时,该单元被重复使用的地图,删除旧的项目,并添加新项目映射条目,并且还更新地图,如果该小区变更,或者如果在场景内的细胞移动的界限。

You need to use a cell factory on the list view that keeps track of which items in the list have active cells, and what the bounds of those cells are (since it is possible during scrolling that an item may still have a cell, but that cell may be scrolled completely out of view). You can do this with an ObservableMap that maps items to the bounds of their cells. You need to update the map when the cell is reused, removing the old item and adding a map entry for the new item, and also update the map if the bounds of the cell change or if the cell moves within the scene.

下面就是这样的一个例子。我把核心功能到定​​制电池厂,暴露了观察到的地图。举个例子,我创建了一个的ListView&LT;整数GT; 100项,并演示创建的,其内容是完全显示其具有细胞项的第二个列表视图的功能。第二个列表视图显示这两个项目及其范围(折算为含有细胞列表视图坐标)。

Here's an example of doing this. I put the core functionality into a custom cell factory and exposed the observable map. As an example, I created a ListView<Integer> with 100 items, and to demo the functionality created a second list view whose contents are the items which have cells that are completely displayed. The second list view displays both those items and their bounds (translated to coordinates in the list view containing the cells).

在理论上这是pretty性能密集型的,因为它更新通过观察大量数据的第二个列表视图;这是由以下事实,它不仅会为每个小区(未每个项目)有所缓解。在我的系统似乎运行正常,而且由于表现只是细胞的数目的函数应该扩展细到大型列表

In theory this is pretty performance intensive, as it updates the second list view by observing a lot of data; this is alleviated somewhat by the fact that it only does that for each cell (not each item). On my system it seems to run fine, and because performance is a function only of the number of cells it should scale fine to large lists.

import java.util.function.Function;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener.Change;
import javafx.collections.ObservableMap;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TrackCellsInListView extends Application {

    @Override
    public void start(Stage primaryStage) {

        // main list view:
        ListView<Integer> listView = new ListView<>();
        for (int i = 1 ; i <= 100; i++) {
            listView.getItems().add(i);
        }

        // create a cell factory that tracks items which have cells and the bounds of their cells:
        TrackingListCellFactory<Integer> cellFactory = new TrackingListCellFactory<>(i -> "Item "+i);
        listView.setCellFactory(cellFactory);

        // map from items with cells to bounds of the cells (in scene coordinates):
        ObservableMap<Integer, Bounds> boundsByItem = cellFactory.getBoundsByItem();

        // list view which will display which items have cells that are completely displayed
        // (i.e. whose bounds are completely contained in the list view bounds):

        ListView<Integer> visibleCells = new ListView<>();

        // cell factory for second list cell displays item and its bounds (translated to
        // list view coordinates):
        visibleCells.setCellFactory(lv -> {
            ListCell<Integer> cell = new ListCell<>();
            cell.textProperty().bind(Bindings.createStringBinding( 
                () -> {
                    if (cell.getItem()==null) {
                        return null ;
                    }
                    Bounds b = boundsByItem.get(cell.getItem());
                    if (b == null) {
                        return null ;
                    }
                    Bounds bounds = listView.sceneToLocal(b);
                    return String.format("%d: [%.1f, %.1f, %.1f, %.1f]", cell.getItem(), 
                            bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY());
                }, cell.itemProperty(), boundsByItem));
            return cell ;
        });

        // keep list of items in second list view up to date by observing map:
        boundsByItem.addListener((Change<? extends Integer, ? extends Bounds> c) -> {
            Bounds listBounds = listView.localToScene(listView.getBoundsInLocal());
            visibleCells.getItems().setAll(
                    boundsByItem.keySet().stream()
                    .filter(s -> listBounds.contains(boundsByItem.get(s)))
                    .sorted()
                    .collect(Collectors.toList()));
            System.out.println();
        });

        // usual UI setup:
        Scene scene = new Scene(new HBox(5, listView, visibleCells));
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static class TrackingListCellFactory<T> implements Callback<ListView<T>, ListCell<T>> {

        // function for mapping item to text to display:
        private Function<T,String> textFunction ;

        // map items which have cells to bounds of those cell in scene coordinates:
        private ObservableMap<T, Bounds> boundsByItem = FXCollections.observableHashMap();

        TrackingListCellFactory(Function<T,String> textFunction) {
            this.textFunction = textFunction ;
        }

        // default text function just calls toString():
        TrackingListCellFactory() {
            this(T::toString);
        }

        public ObservableMap<T, Bounds> getBoundsByItem() {
            return boundsByItem ;
        }


        @Override
        public ListCell<T> call(ListView<T> param) {

            //create cell that displays text according to textFunction:
            ListCell<T> cell = new ListCell<T>() {
                @Override
                protected void updateItem(T item, boolean empty) {
                    super.updateItem(item, empty);
                    setText(item == null ? null : textFunction.apply(item));
                }
            };

            // add and remove from map when cell is reused for different item:
            cell.itemProperty().addListener((obs, oldItem, newItem) -> {
                if (oldItem != null) {
                    boundsByItem.remove(oldItem);
                }
                if (newItem != null) {
                    boundsByItem.put(newItem, cell.localToScene(cell.getBoundsInLocal()));
                }
            });

            // update map when bounds of item change
            ChangeListener<Object> boundsChangeHandler = (obs, oldValue, newValue) -> {
                T item = cell.getItem() ;
                if (item != null) {
                    boundsByItem.put(item, cell.localToScene(cell.getBoundsInLocal()));
                }
            };

            // must update either if cell changes bounds, or if cell moves within scene (e.g.by scrolling):
            cell.boundsInLocalProperty().addListener(boundsChangeHandler);
            cell.localToSceneTransformProperty().addListener(boundsChangeHandler);

            return cell;
        }

    }

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

这篇关于JavaFX的ListView控件:检索行的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆