技術概述
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能很好的幫助我們實現畫板繪制功能,必要時也需創建子類對其進行功能拓展和完善,以滿足項目產品的需求。