JavaFX 之 Canvas
技术概述
javaFX Canvas API提供了一个灵活的画笔。它可以通过创建canvas对象,获取其GraphicsContext以及调用绘图操作以在屏幕上呈现自定义形状,它可以在JavaFX场景图中使用。本次项目你画我猜要求呈现出一个画板并且能够绘制自定义图形,因此学习此组件来应用于项目中。
技术详述
定义GraphicsContext对象g,之后使用GraphicsContext提供的一组图形命令来实现绘制图像。
GraphicsContext g = getGraphicsContext2D();
设置监听器来获取画笔的大小和颜色
nowSize.addListener(evt -> {
g.setLineWidth(nowSize.get() * Math.min(getWidth(), getHeight()));
});
nowColor.addListener(evt -> g.setStroke(nowColor.get()));
实现画笔绘制操作事件,包括按下鼠标,拖动鼠标,释放鼠标三个响应事件
//鼠标按下事件
setOnMousePressed(evt -> {
if (!myTurn || !evt.isPrimaryButtonDown())
return;
nowPi = new PainInfo();
pis.add(nowPi);
g.beginPath();
double x = evt.getX() / getWidth();
double y = evt.getY() / getHeight();
nowPi.xys.add(new BinaryDoubleTuple(x, y));
g.moveTo(x * getWidth(), y * getHeight());
g.lineTo(x * getWidth(), y * getHeight());
g.stroke();
count = 0;
});
//鼠标拖动事件
setOnMouseDragged(evt -> {
if (!myTurn || !evt.isPrimaryButtonDown())
return;
count++;
if (count % 20 == 0) {
double x = evt.getX() / getWidth();
double y = evt.getY() / getHeight();
nowPi.xys.add(new BinaryDoubleTuple(x, y));
g.lineTo(x * getWidth(), y * getHeight());
g.stroke();
}
});
//鼠标释放事件
setOnMouseReleased(evt -> {
g.closePath();
refresh();
PainInfo painInfoLast=null;
if(pis!=null) {
painInfoLast= pis.get(pis.size()-1);
}
HashMap <String,Object> hm = new HashMap<>();
Color color1;
double size1;
color1 = nowColor.get();
size1 = nowSize.get();
hm.put("drawed", new TernaryTuple<>(color1,size1,painInfoLast.xys));
System.out.println("i do it");
NewIo.send(ClientPost.Type.DRAW, hm);
System.out.println("i do it end");
});
技术使用中遇到的问题和解决过程
问题:画布的大小无法随着窗口自适应,用户在放大或缩小窗口后画布仍保持原大小。
解决方法:在javaFX中没有方法可以调整画布自适应,唯一的解决方案是从Canvas扩展。
重写isResizable()方法并返回true
@Override
public boolean isResizable() {
return true;
}
重写prefWidth()和prefHeight()方法,返回getWidth()和getHeight()的值
@Override
public double prefWidth(double var1) {
return getWidth();
}
@Override
public double prefHeight(double var1) {
return getHeight();
}
将侦听器添加到画布的width和height属性中,以便在画布大小更改时触发重绘。
//添加窗口大小监听器
widthProperty().addListener(evt -> refresh());
heightProperty().addListener(evt -> refresh());
//设计重绘方法
private void refresh() {
Color color = nowColor.get();
double size = nowSize.get();
clear();
for (PainInfo pi : pis) {
nowColor.set(pi.color);
nowSize.set(size);
g.beginPath();
BinaryDoubleTuple first = pi.xys.getFirst();
g.moveTo(first.first * getWidth(), first.second * getHeight());
for (BinaryDoubleTuple xy : pi.xys) {
g.lineTo(xy.first * getWidth(), xy.second * getHeight());
g.stroke();
}
g.closePath();
}
nowColor.set(color);
nowSize.set(size);
}
将画布的width和height属性绑定到父窗格StackPane的width和height属性。
DngCanvas dc = new DngCanvas();
StackPane ret = new StackPane(dc);
dc.widthProperty().bind(ret.widthProperty());
dc.heightProperty().bind(ret.heightProperty());
总结
javafx中的Canvas能很好的帮助我们实现画板绘制功能,必要时也需创建子类对其进行功能拓展和完善,以满足项目产品的需求。