首页 > 解决方案 > 使用动态数据设计 JavaFX 自定义控件

问题描述

我正在尝试在 JavaFX 中创建自己的控件。我的问题是我的控件不是静态的(例如标签、按钮...)而是动态的(例如 ListView),因此不断添加和删除新记录。我不确定如何在 JavaFX 中实现它,所以为了感受它,我想重新创建一个类似于 ListView 的控件。下面我写了一个小例子。

该示例可以正常工作,但感觉有点脏。为了保持一切小而简单,我不想使用 CSS 和 FXML。

我怎样才能实现这个清洁器,尤其是在更复杂的情况下?

示例应用程序屏幕截图。

MyList.java

public class MyList extends Control
{
    private ListProperty<MyListItem> mItems;

    public MyList()
    {
        mItems = new SimpleListProperty<>(FXCollections.observableArrayList());
    }

    @Override
    protected Skin<?> createDefaultSkin() 
    {
        return new MyListSkin(this);
    }

    public ObservableList<MyListItem> getItems()
    {
        return mItems.get();
    }

    public void setItems(ObservableList<MyListItem> pItems)
    {
        mItems.set(pItems);
    }

    public ListProperty<MyListItem> itemsProperty()
    {
        return mItems;
    }
}

MyListItem.java

public class MyListItem 
{
    private StringProperty mName;


    public MyListItem(String pName)
    {
        mName = new SimpleStringProperty(pName);
    }


    public String getName()
    {
        return mName.get();
    }

    public void setName(String pName)
    {
        mName.set(pName);
    }

    public StringProperty nameProperty()
    {
        return mName;
    }
}

MyListSkin.java

public class MyListSkin extends SkinBase<MyList>
{
    private VBox mVBox;
    private Map<MyListItem, Label> mNodes;

    protected MyListSkin(MyList pControl)
    {
        super(pControl);

        mVBox = new VBox();
        mNodes = new HashMap<>();

        getChildren().add(mVBox);

        getSkinnable().getItems().addListener(new ListChangeListener<MyListItem>()
        {
            @Override
            public void onChanged(Change<? extends MyListItem> pChange)
            {
                removeItems(pChange.getRemoved());
                addItems(pChange.getAddedSubList());
            }
        });

        getSkinnable().itemsProperty().addListener(new ChangeListener<ObservableList<MyListItem>>() 
        {

            @Override
            public void changed(ObservableValue<? extends ObservableList<MyListItem>> pObservable,
                    ObservableList<MyListItem> pOldValue, ObservableList<MyListItem> pNewValue) 
            {
                clearItems();
                addItems(pNewValue);
            }
        });

        addItems(getSkinnable().getItems());
    }

    private void addItems(List<? extends MyListItem> list)
    {
        for(MyListItem lItem : list)
        {
            Label lLabel = new Label();
            lLabel.setMinWidth(Control.USE_PREF_SIZE);
            lLabel.setPrefWidth(200);
            lLabel.textProperty().bind(lItem.nameProperty());
            mVBox.getChildren().add(lLabel);
            mNodes.put(lItem, lLabel);
        }
    }

    private void removeItems(List<? extends MyListItem> list)
    {
        for(MyListItem lItem : list)
        {
            mVBox.getChildren().remove(mNodes.get(lItem));
            mNodes.remove(lItem);
        }
    }

    private void clearItems()
    {
        for(Map.Entry<MyListItem, Label> lEntry : mNodes.entrySet())
        {
            mVBox.getChildren().remove(lEntry.getValue());
            mNodes.remove(lEntry.getKey());
        }
    }
}

MainApplication.java

public class MainApplication extends Application
{
    @Override
    public void start(Stage pPrimaryStage)
    {
        MyList lControl = new MyList();
        lControl.getItems().add(new MyListItem("Test 1"));
        lControl.getItems().add(new MyListItem("Test 2"));
        lControl.getItems().add(new MyListItem("Test 3"));
        lControl.getItems().add(new MyListItem("Test 4"));

        Pane lRoot = new Pane();
        lRoot.getChildren().add(lControl);

        pPrimaryStage.setScene(new Scene(lRoot, 800, 800));
        pPrimaryStage.show();
    }


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

标签: javauser-interfacejavafxcustom-controls

解决方案


如何保持数据、逻辑和图形分离?

单独的类就是这样做的,MyListItem是你的数据,MyList是你的逻辑,MyListSkin是你的图形。

我应该使用 BehaviorBase 吗?如果可以,如何使用?

真的取决于您,如果您愿意,那么我建议您阅读ListViewBehavior并可能从中扩展。另请查看CellBehaviorBase及其子类。

如何跟踪MyListItem对象及其相应的图形表示/节点?

不完全确定你在问什么,在你的中跟踪它们Control并跟踪你的图形表示Skin,这看起来就像你正在做的那样。

我应该如何处理初始化?

看起来你做得很好。

如何让它们保持同步?

与您保持其他任何同步的方式相同,侦听更改Control并更新Skin它看起来也像您正在做的那样,它不起作用吗?


如果您想要Label列表中单个 s 的自定义图形,那么您可以通过您的 s 来处理一些一般情况MyListSkin,例如。PseudoClass为偶数或奇数索引元素设置 a 。


推荐阅读