Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaFXListView:检索行的位置_Java_Listview_Javafx - Fatal编程技术网

JavaFXListView:检索行的位置

JavaFXListView:检索行的位置,java,listview,javafx,Java,Listview,Javafx,JavaFXListView是否有一个属性可以返回当前在其视图区域中显示的行?或者,有没有一种方法可以将EventHandler分配给一个特定的行,该行在事件在列表中的位置发生变化时(由于新对象添加到列表中或从列表中删除对象)发送事件 本质上,我需要知道所选行的x、y坐标,才能知道所选行是否离开当前视图区域。因为如果发生这种情况,我需要调用ListView.scrollTo()方法将焦点返回到所选行。如果用户选择一行,那么程序会向列表中添加更多的对象,从而将任何现有对象(包括所选对象)推离视图区

JavaFXListView是否有一个属性可以返回当前在其视图区域中显示的行?或者,有没有一种方法可以将EventHandler分配给一个特定的行,该行在事件在列表中的位置发生变化时(由于新对象添加到列表中或从列表中删除对象)发送事件

本质上,我需要知道所选行的x、y坐标,才能知道所选行是否离开当前视图区域。因为如果发生这种情况,我需要调用ListView.scrollTo()方法将焦点返回到所选行。如果用户选择一行,那么程序会向列表中添加更多的对象,从而将任何现有对象(包括所选对象)推离视图区域,则会出现这种情况

我尝试将ListChangeListener添加到下面的可观察列表中,但似乎无法将其应用于特定行。ListChangeListener似乎只指示行是否“添加/删除/更新/替换/置换”等。该事件处理程序/侦听器似乎没有给出特定行的x、y坐标,也没有给出某种布尔指示,表明该行位于ListView对象的当前视图区域内

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

public void onChanged(ListChangeListener.Change据我所知,没有API可以执行此操作。添加此功能有点棘手,因为单元格重用机制(在中描述)

您需要在列表视图上使用单元格工厂,跟踪列表中哪些项目具有活动单元格,以及这些单元格的边界是什么(因为在滚动期间,项目可能仍然具有单元格,但该单元格可能会被完全滚动到视图之外)。您可以使用将项目映射到其单元格边界的
ObservableMap
来执行此操作。在重复使用单元格时,您需要更新贴图,删除旧项目并为新项目添加贴图条目,如果单元格边界更改或单元格在场景中移动,还需要更新贴图

下面是这样做的示例。我将核心功能放入自定义单元格工厂并公开了可观察映射。例如,我创建了一个包含100个项目的
ListView
并演示创建了第二个列表视图,其中的内容是包含单元格的项。第二个列表视图显示显示这些项目及其边界(转换为包含单元格的列表视图中的坐标)

从理论上讲,这是相当高的性能密集型,因为它通过观察大量数据来更新第二个列表视图;这在某种程度上得到缓解,因为它只对每个单元格(而不是每个项目)执行此操作。在我的系统上,它似乎运行良好,而且因为性能只是单元格数量的函数,所以它应该精确地扩展到大型列表

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);
    }
}
import java.util.function.function;
导入java.util.stream.collector;
导入javafx.application.application;
导入javafx.beans.binding.Bindings;
导入javafx.beans.value.ChangeListener;
导入javafx.collections.FXCollections;
导入javafx.collections.MapChangeListener.Change;
导入javafx.collections.observemap;
导入javafx.geometry.Bounds;
导入javafx.scene.scene;
导入javafx.scene.control.ListCell;
导入javafx.scene.control.ListView;
导入javafx.scene.layout.HBox;
导入javafx.stage.stage;
导入javafx.util.Callback;
公共类TrackCellsInListView扩展了应用程序{
@凌驾
公共无效开始(阶段primaryStage){
//主列表视图:
ListView ListView=新建ListView();
用于(int i=1;i“项目”+i);
setCellFactory(cellFactory);
//从包含单元格的项目映射到单元格边界(在场景坐标中):
ObservableMap boundsByItem=cellFactory.getBoundsByItem();
//列表视图,该视图将显示哪些项目的单元格已完全显示
//(即,其边界完全包含在列表视图边界中):
ListView visibleCells=新建ListView();
//第二个列表单元格的单元格工厂显示项目及其边界(转换为
//列表视图坐标):
visibleCells.setCellFactory(lv->{
ListCell=新ListCell();
cell.textProperty().bind(Bindings.createStringBinding(
() -> {
if(cell.getItem()==null){
返回null;
}
Bounds b=boundsByItem.get(cell.getItem());
如果(b==null){
返回null;
}
Bounds=listView.sceneToLocal(b);
返回字符串.format(“%d:[%.1f、%.1f、%.1f、%.1f、%.1f]”,cell.getItem(),
bounds.getMinX(),bounds.getMinY(),bounds.getMaxX(),bounds.getMaxY());
},cell.itemProperty(),boundsByItem));
返回单元;
});
//通过观察地图,使第二个列表视图中的项目列表保持最新:

boundsByItem.addListener((更改)您要查找的内容没有API。Tomas Mikula的第三方库提供了一些与您要查找的内容类似的功能(我认为)。请注意,在您的大部分问题中,您会将数据与数据视图混淆。例如:“我尝试将一个
ListChangeListener
添加到
ObservableList
…该事件处理程序似乎没有给出特定行的x、y坐标”。重点是列表中的项目没有坐标(单元格有坐标,而不是项目):该项甚至可能没有任何对应的单元格或任何UI。请参见。所有这些都不会阻止
GetVisibleRowIndexes()
API;但它只是不存在请参见