在Swing应用程序中,我在JPanel
上绘制了覆盖paintComponent
方法的元素。
我必须画猫,狗,骆驼等......我想用一系列paintComponent
语句污染if (... instanceof ...)
会是一件坏事,所以我一直在寻找以另一种方式画我的宠物。
使用WPF(C#),我使用了Data Templates,即告诉View如何显示模型实体。
所以,我提出了一个从JS移植的解决方案:
import my.super.Callback; // Because Runnable does not take parameters.
/**
* The class that draws in the canvas.
*/
public class CanvasDrawer {
private HashMap<Class, Callback<Graphics2D, Object>> drawers;
private Graphics2D graphics;
public CanvasDrawer(Graphics2D g) {
graphics = g;
drawers = new HashMap<Class, Callback<Graphics2D, Object>>();
}
/**
* Defines a way to draw the objects of a certain type in the canvas.
*/
public <T> void setDataTemplate(Class<T> type, Callback<Graphics2D, T> drawer) {
drawers.put(type, (Callback<Graphics2D, Object>)drawer);
}
/**
* Actually draws an object in the canvas.
*/
public void draw(Object object) {
drawers.get(object.getClass()).run(graphics, object);
}
}
......可以这样使用:
class HuntingPartyView extends JPanel {
CanvasDrawer drawer;
public HuntingPartyView() {
drawer = new CanvasDrawer((Graphics2D)getGraphics());
// Where the drawImage method is intentionally skipped in this code.
drawer.setDataTemplate( Cat.class, (g, o) -> drawImage(g, 'res/cat.png', o.x, o.y));
drawer.setDataTemplate( Dog.class, (g, o) -> drawImage(g, 'res/dog.png', o.x, o.y));
drawer.setDataTemplate(Sniper.class, (g, o) -> drawImage(g, 'res/sniper.png', o.x, o.y));
drawer.setDataTemplate(Ranger.class, (g, o) -> drawImage(g, 'res/ranger.png', o.x, o.y));
}
@Override
public void repaintComponent(Graphics g) {
// Where Game is part of the Model layer.
for (Animal animal : Game.singleton.animalList) drawer.draw(animal);
for (Hunter hunter : Game.singleton.hunterList) drawer.draw(hunter);
}
}
...但是上面CanvasDrawer
的代码在编译时给了我一个未经检查的警告:
$ javac *.java -d out -Xdiags:verbose -Xlint:unchecked
CanvasDrawer.java:15: warning: [unchecked] unchecked cast
drawers.put(type, (Callback<Graphics2D, Object>)drawer);
^
required: Callback<Graphics2D,Object>
found: Callback<Graphics2D,T>
where T is a type-variable:
T extends Object declared in method <T>setDataTemplate(Class<T>,Callback<Graphics2D,T>)
1 warning
问题1。我在研究过程中是否遗漏了什么?我的意思是:Java Swing中存在数据模板吗?
问题2。还有其他办法可以做我想做的事吗?我走对了路吗?
问题3。我真的必须使用@SuppressWarnings("unchecked")
吗?
答案 0 :(得分:1)
我想用一系列if(... instanceof ...)语句污染paintComponent会是一件坏事
正确。
一种方法是只保留一个Animals
的ArrayList,其中每只动物都有一个绘制方法来绘制自己。
类似的东西:
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Animal animal: animals)
{
animal.draw(g);
}
}
Animal
可以是简单的接口或抽象类。
答案 1 :(得分:1)
参考A Swing Architecture Overview,Swing设计的完整指南超出了本网站的范围,但一些指南可能会帮助您继续前进。
我在研究过程中是否遗漏了什么?我的意思是:Java Swing中是否存在Data Templates?没有明确。关注教程示例,Swing JList
显示ListModel<E>
。直接子类AbstractListModel<E>
使用所检查的方案here添加事件监听器管理。具体的子类DefaultListModel<E>
是一个典型的实现。根据需要,您的模型可以直接实现接口,利用抽象模型的事件管道或简单地包含具体的模型实例,如How to Use Lists: Creating a Model中所述。
还有其他办法可以做我想做的事吗?我是否采取正确的方式?提到了实现观察者模式的几种常用方法here。
我是否真的必须使用@SuppressWarnings(“未选中”)?您可以指定类型参数,例如{{1},而不是像Object
这样的原始类。如here所述。另请参阅Class Literals as Runtime-Type Tokens。
将这样的视图绘制逻辑放在所谓的解耦模型中不是一件坏事吗? example outlined是一个视图组件; 模型可能包含CanvasDrawer<T>
,当观察视图更新自身以响应事件时,会访问该文件。
听起来List<Animal>
类是控制器;因为一个控制器可能是一个匿名类,所以它们会崩溃。一个吸引人的设计方法允许客户注入一个新的控制器,如here jmapviewer所示。
您是否建议我以某种方式使用这些数据模型?仅在适当情况下使用。有太多方法可以显示…Listener
来说明更多内容。