JavaFX 8自定义ListView单元是邪恶的 [英] JavaFX 8 Custom ListView Cells it's Evil

查看:204
本文介绍了JavaFX 8自定义ListView单元是邪恶的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有自定义ListView单元格我有两个控件的HBox:


  1. 带有名称字段的标签

  2. 必须从对象的上下文生成

例如我设置类别字段的Lis​​tView项数组,如果字段类型为Boolean我创建CheckBox ,如果String我创建TextField等等。



问题是:我只在方法updateItem()中得到Field对象 - 只有我可以创建我的控件。



在JavaFX 7中一切正常,但在Java 8上 - 我没有看到我的控件。



是否存在正确的方法解决我的问题?



UPDATE 1



这里有完整的复制问题示例:

  package javafx8listviewexample; 

import java.lang.reflect.Field;
import java.net.URL;
import java.util.Date;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.DatePicker;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.util.Callback;

/ **
*
* @author dmitrynelepov
* /
公共类FXMLDocumentController实现Initializable {

静态类TestClassForListView {

public String fieldString;
public Date fieldDate;
}

静态类MyListCell扩展ListCell< Field> {

/ **
*与教程中一样
*
* @param t
* @param bln
* /
@Override
protected void updateItem(Field t,boolean bln){
super.updateItem(t,bln);
if(t!= null){
if(t.getType()。equals(String.class)){
System.out.println(Draw string);
setGraphic(new TextField(t.getName()));
} else if(t.getType()。equals(Date.class)){
System.out.println(Draw Date);
setGraphic(new DatePicker());

}
}
}

}

@FXML
private ListView< Field>列表显示;

@FXML
private void handleButtonAction(ActionEvent event){
this.listView.getItems()。clear();
this.listView.setItems(
FXCollections.observableArrayList(
TestClassForListView.class.getFields()

);
}

@Override
public void initialize(URL url,ResourceBundle rb){
this.listView.setCellFactory(new Callback< ListView< Field>,ListCell<字段>>(){

@Override
public ListCell< Field> call(ListView< Field> p){
return new MyListCell();
}
});

}

}

这里的截图程序JDK 7





你可以看到我可以聚焦和编辑TextField。



但是如果我用JDK 8运行这个例子我得到





因为你可以看到TextField无法集中和编辑。

解决方案

看起来像你想要一个ControlsFX 。即使这个问题的答案是基于TableView,ListView和TableView虚拟化控件,所以实现概念有点类似于在你的问题中使用带有HBox的ListView。








根据问题的示例代码进行更新。



我仍然认为ControlsFX PropertySheet似乎是最合适的解决你想要完成的事情。使用像ListView这样的虚拟控件来完成这样的任务只会使事情变得比它们需要的更复杂。



基于ListView的属性编辑器的完整解决方案非常复杂在StackOverflow答案中可以合理提供的内容和范围之外。



您在问题中提供的示例代码存在一些问题。 ListView是一个虚拟化控件,因此您不应创建新的图形节点,以便在调用更新时始终放在ListView中。会发生什么是TextField获得焦点,然后在ListView上调用update并创建一个新的TextField,默认情况下新的TextField没有焦点。



我认为ListView本身对您的特定情况有一些实现问题,并且调用更新次数太多。无论如何,您应该编写代码,以便在单个单元格上适当地处理多次调用的更新。如果你这样做,ListView调用你的方法的次数多于它需要的时间并不重要。



这里有一些示例代码可以帮助你取得更多进展。我不相信示例代码是您的问题的完整解决方案,它肯定不是作为Java对象的综合属性编辑器提供的,但它可能会给您一些灵感来帮助改进和实现您的代码(假设您决定继续尝试以这种方式解决这个问题)。如果继续使用ListView,您可能需要查看ListView的编辑例程(类似于为编辑表格单元格

  import javafx.application 。应用; 
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control。*;
import javafx.stage.Stage;

import java.lang.reflect.Field;
import java.time.LocalDate;

/ **
* @author dmitrynelepov
*由Jewelsea修改
* /
公共类EvilHasSurvived延伸申请{

static class TestClassForListView {
public String fieldString;
public LocalDate fieldDate;

@Override
public String toString(){
returnTestClassForListView {+
fieldString ='+ fieldString +'\''+
,fieldDate =+ fieldDate +
'}';
}
}

静态类MyListCell扩展ListCell< Field> {
private TextField textField;
private DatePicker datePicker;
private Object editedObject;
private ChangeListener< Boolean> editCommitHandler;

public MyListCell(Object editedObject){
this.editedObject = editedObject;
setContentDisplay(ContentDisplay.RIGHT);
}

@Override
protected void updateItem(Field t,boolean bln){
super.updateItem(t,bln);

if(datePicker!= null){
datePicker.focusedProperty()。removeListener(editCommitHandler);
}
if(textField!= null){
textField.focusedProperty()。removeListener(editCommitHandler);
}

if(t == null){
setText(null);
setGraphic(null);
返回;
}

if(t.getType()。equals(String.class)){
if(textField == null){
textField = new TextField( );
}

editCommitHandler =(observable,oldValue,newValue) - > {
try {
t.set(editedObject,textField.getText());
System.out.println(editedObject +for+ textField +value+ textField.getText());
} catch(IllegalAccessException e){
e.printStackTrace();
}
};
textField.focusedProperty()。addListener(editCommitHandler);

setText(t.getName());
setGraphic(textField);
} else if(t.getType()。equals(LocalDate.class)){
if(datePicker == null){
datePicker = new DatePicker();
}

editCommitHandler =(observable,oldValue,newValue) - > {
try {
t.set(editedObject,datePicker.getValue());
System.out.println(editedObject +for+ datePicker +value+ datePicker.getValue());
} catch(IllegalAccessException e){
e.printStackTrace();
}
};
datePicker.focusedProperty()。addListener(editCommitHandler);

setText(t.getName());
setGraphic(datePicker);
}
}
}

@Override
public void start(阶段阶段)抛出异常{
ListView< Field> listView = new ListView<>();
listView.setItems(
FXCollections.observableArrayList(
TestClassForListView.class.getFields()

);
TestClassForListView testObject = new TestClassForListView();
listView.setCellFactory(p - > new MyListCell(testObject));

stage.setScene(new Scene(listView));
stage.show();
}

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


I have custom ListView cell i have HBox with two controls:

  1. Label with name field
  2. It's must generated from context of object

For example i set ListView items array of Class Fields, if field type Boolean i create CheckBox, if String i create TextField and etc.

Problem is: i get Field object only in method updateItem() - and only there i can create my controls.

In JavaFX 7 all works fine, but on Java 8 - i dont see my controls.

Is exists correct way to resolve my case?

UPDATE 1

Here complete example for replicate problem:

package javafx8listviewexample;

import java.lang.reflect.Field;
import java.net.URL;
import java.util.Date;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.DatePicker;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.util.Callback;

/**
 *
 * @author dmitrynelepov
 */
public class FXMLDocumentController implements Initializable {

    static class TestClassForListView {

        public String fieldString;
        public Date fieldDate;
    }

    static class MyListCell extends ListCell<Field> {

        /**
         * As in tutorial
         *
         * @param t
         * @param bln
         */
        @Override
        protected void updateItem(Field t, boolean bln) {
            super.updateItem(t, bln);
            if (t != null) {
                if (t.getType().equals(String.class)) {
                    System.out.println("Draw string");
                    setGraphic(new TextField(t.getName()));
                } else if (t.getType().equals(Date.class)) {
                    System.out.println("Draw Date");
                    setGraphic(new DatePicker());

                }
            }
        }

    }

    @FXML
    private ListView<Field> listView;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        this.listView.getItems().clear();
        this.listView.setItems(
                FXCollections.observableArrayList(
                        TestClassForListView.class.getFields()
                )
        );
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        this.listView.setCellFactory(new Callback<ListView<Field>, ListCell<Field>>() {

            @Override
            public ListCell<Field> call(ListView<Field> p) {
                return new MyListCell();
            }
        });

    }

}

Here screenshot of program JDK 7

as u can see i TextField can be focused and edited.

But if i run this example with JDK 8 i got

as here u can see TextField cant be focused and edited.

解决方案

Looks like you want a ControlsFX PropertySheet:


A similar implementation, is in the answer to JavaFX 2 TableView : different cell factory depending on the data inside the cell. Even though the answer to that question is based on TableView, both ListView and TableView virtualized controls, so the implementation concept is somewhat similar to using a ListView with an HBox as outlined in your question.


Update based on sample code from question.

I still think that a ControlsFX PropertySheet appears to be the most appropriate solution for what you are trying to accomplish. Using a Virtualized Control like a ListView for such a task simply makes things more complicated than they need to be.

A full solution for a ListView based property editor is a pretty complex thing and outside the scope of what can be reasonably provided in a StackOverflow answer.

The sample code you provide in your question has some issues. The ListView is a virtualized control so you should not create new graphic nodes to place inside the ListView all the time that update is being called. What happens is that a TextField gets focus, then update is called on the ListView and you create a new TextField and by default the new TextField does not have focus.

I think the ListView itself has some implementation issues for your particular case and is calling update too many times. Regardless of this, you should write your code so that you can appropriately handle update being called multiple times on a single cell and. if you do so, it won't matter if ListView calls your method more times than it needs to.

Here is some sample code which may help you make more progress. I don't believe the sample code is a complete solution to your issue, it is certainly not provided as a comprehensive property editor for Java objects, but perhaps it may give you some inspiration to help improve and implement your code (assuming you decide to continue attempting to solve this problem in this manner). If you continue using ListView, you may want to look into the editing routines for ListView (similar to what is defined for editing table cells in the Oracle JavaFX tutorials).

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

import java.lang.reflect.Field;
import java.time.LocalDate;

/**
 * @author dmitrynelepov
 * Modified by Jewelsea
 */
public class EvilHasSurvived extends Application {

    static class TestClassForListView {
        public String fieldString;
        public LocalDate fieldDate;

        @Override
        public String toString() {
            return "TestClassForListView{" +
                    "fieldString='" + fieldString + '\'' +
                    ", fieldDate=" + fieldDate +
                    '}';
        }
    }

    static class MyListCell extends ListCell<Field> {
        private TextField textField;
        private DatePicker datePicker;
        private Object editedObject;
        private ChangeListener<Boolean> editCommitHandler;

        public MyListCell(Object editedObject) {
            this.editedObject = editedObject;
            setContentDisplay(ContentDisplay.RIGHT);
        }

        @Override
        protected void updateItem(Field t, boolean bln) {
            super.updateItem(t, bln);

            if (datePicker != null) {
                datePicker.focusedProperty().removeListener(editCommitHandler);
            }
            if (textField != null) {
                textField.focusedProperty().removeListener(editCommitHandler);
            }

            if (t == null) {
                setText(null);
                setGraphic(null);
                return;
            }

            if (t.getType().equals(String.class)) {
                if (textField == null) {
                    textField = new TextField();
                }

                editCommitHandler = (observable, oldValue, newValue) -> {
                    try {
                        t.set(editedObject, textField.getText());
                        System.out.println(editedObject + " for " + textField + " value " + textField.getText());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                };
                textField.focusedProperty().addListener(editCommitHandler);

                setText(t.getName());
                setGraphic(textField);
            } else if (t.getType().equals(LocalDate.class)) {
                if (datePicker == null) {
                    datePicker = new DatePicker();
                }

                editCommitHandler = (observable, oldValue, newValue) -> {
                    try {
                        t.set(editedObject, datePicker.getValue());
                        System.out.println(editedObject + " for " + datePicker + " value " + datePicker.getValue());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                };
                datePicker.focusedProperty().addListener(editCommitHandler);

                setText(t.getName());
                setGraphic(datePicker);
            }
        }
    }

    @Override
    public void start(Stage stage) throws Exception {
        ListView<Field> listView = new ListView<>();
        listView.setItems(
            FXCollections.observableArrayList(
                TestClassForListView.class.getFields()
            )
        );
        TestClassForListView testObject = new TestClassForListView();
        listView.setCellFactory(p -> new MyListCell(testObject));

        stage.setScene(new Scene(listView));
        stage.show();
    }

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

这篇关于JavaFX 8自定义ListView单元是邪恶的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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