java - 使用动态数据设计 JavaFX 自定义控件
问题描述
我正在尝试在 JavaFX 中创建自己的控件。我的问题是我的控件不是静态的(例如标签、按钮...)而是动态的(例如 ListView),因此不断添加和删除新记录。我不确定如何在 JavaFX 中实现它,所以为了感受它,我想重新创建一个类似于 ListView 的控件。下面我写了一个小例子。
- MyList
类似于 ListView 的实际控件。 - MyListItem
存储在 MyList 中的对象,它只包含一个要显示的字符串。 - MyListSkin
负责图形表示的皮肤。
该示例可以正常工作,但感觉有点脏。为了保持一切小而简单,我不想使用 CSS 和 FXML。
我怎样才能实现这个清洁器,尤其是在更复杂的情况下?
- 如何保持数据、逻辑和图形分离?
- 我应该使用 BehaviorBase 吗?如果可以,如何使用?
- 如何跟踪 MyListItem 对象及其相应的图形表示/节点?
- 我应该如何处理初始化?
- 如何让它们保持同步?
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);
}
}
解决方案
如何保持数据、逻辑和图形分离?
单独的类就是这样做的,MyListItem
是你的数据,MyList
是你的逻辑,MyListSkin
是你的图形。
我应该使用 BehaviorBase 吗?如果可以,如何使用?
真的取决于您,如果您愿意,那么我建议您阅读ListViewBehavior
并可能从中扩展。另请查看CellBehaviorBase
及其子类。
如何跟踪
MyListItem
对象及其相应的图形表示/节点?
不完全确定你在问什么,在你的中跟踪它们Control
并跟踪你的图形表示Skin
,这看起来就像你正在做的那样。
我应该如何处理初始化?
看起来你做得很好。
如何让它们保持同步?
与您保持其他任何同步的方式相同,侦听更改Control
并更新Skin
它看起来也像您正在做的那样,它不起作用吗?
如果您想要Label
列表中单个 s 的自定义图形,那么您可以通过您的 s 来处理一些一般情况MyListSkin
,例如。PseudoClass
为偶数或奇数索引元素设置 a 。
推荐阅读
- java - 如何在没有通过引用传递的资源的情况下在 java 中简化此代码?
- c# - 如何为 IDictionary 实施 RemoveAll,尤其是。并发字典?
- snowflake-cloud-data-platform - Snowflake POC(将数据从 Microsoft AX on Premise 加载到 Snowflake)
- c++ - 如何从 SetupDiGetDeviceRegistryProperty 获取可读字符串?
- deepsecurity - Trend DSM V12 SAAS API 示例给出“无法建立新连接:[Errno 11001] getaddrinfo failed')': /api/policies”
- powershell - powershell 导出组成员资格多个职位
- azure - 在 Azure IoT Edge 中运行的容器能否访问监视器以可视化数据?
- graphql - 我可以在 AppSync 的嵌套类型中查询基于 SS 的对象吗
- javascript - 将数组值放入多个变量 Javascript
- angular - 获取 QuotaExceededError