最近终于学到了Swing,做了个简易画板。先完成自由绘制的功能,然后添加一堆按钮并加上相应的功能。
想加一个撤消上一步操作的功能,但是想不到应该怎么实现。
换背景色是用重新填充整块画布的方式来完成,这样会把画布上的所有内容清空,该如何在不影响已有内容的情况下,更换背景色呢?不知道有没有类似PhotoShop里的图层的组件。
总结出以下两点收获:
1.Image默认背景色是黑色,Graphics默认背景色是白色。
2.JToolBar和JPopupMenu都可以使用add(Action a)的方法添加一个按钮,但是JPanel就不行,只能把Action当作事件监听器添加。
另外推荐大家使用Sublime Text,体积小巧,功能强大。我选了自带的Solarized(Dark)主题,另外装了个Monaco字体和MacType字体渲染程序,界面非常漂亮。
刚用没多久,有两个功能非常实用。
一是分屏浏览+克隆,如下图,写界面的时候看变量名非常方便。
二是定位。cmd调试代码时会提示哪一行出现什么错误,这时回到Sublime Text,按下Ctrl+G,输入行数直达。
以下是我搜集的Sublime Text 3下载地址和使用技巧,其中破解版集成了中文补丁了很多插件。
最后附上程序代码
import java.awt.image.BufferedImage;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Test{
//窗口
private JFrame f = new JFrame("绘图板");
//区域大小
private final int WIDTH = 600;
private final int HEIGHT = 600;
//上一次鼠标坐标
private int preX;
private int preY;
//按下鼠标时的坐标
private int startX;
private int startY;
//画布
MyCanvas drawArea = new MyCanvas();
//画图用的image
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//重写Graphics中的画矩形方法
Graphics g = image.getGraphics();
//前景色默认红,背景色默认白
private Color foreColor = new Color(255, 0, 0);
private Color backColor = new Color(255, 255, 255);
//画笔,标记执行何种绘画模式
//画笔的值均为四字符的小写字母
private String brush = "free";
//上方工具栏
private JPanel p = new JPanel();
private JButton lineButton = new JButton("直线");
private JButton rectButton = new JButton("矩形");
private JButton circButton = new JButton("圆形");
private JButton freeButton = new JButton("自由绘制");
private JButton foreButton = new JButton("前景色");
private JButton backButton = new JButton("背景色");
private JButton clearButton = new JButton("清空画布");
//右键菜单,功能与工具栏上的按钮一模一样
private JPopupMenu popMenu = new JPopupMenu("画图选项");
//添加清空按钮
//AbstractAction是Action接口的实现类,故不须重写所有方法
//按钮和右键菜单都使用这个Action
//JPanel不能使用JPanel.add(Action)来创建Button
//按钮全要做到右键菜单上,所以每个按钮单独一个Action
Action clearDrawArea = new AbstractAction("清空画布"){
public void actionPerformed(ActionEvent e){
//调用画布初始化方法,即完成清空画布
imageInit();
}
};
//按下按钮的事件监听器
Action drawLine = new AbstractAction("直线"){
public void actionPerformed(ActionEvent e){
brush = "line";
}
};
Action drawRect = new AbstractAction("矩形"){
public void actionPerformed(ActionEvent e){
brush = "rect";
}
};
Action drawCirc = new AbstractAction("圆形"){
public void actionPerformed(ActionEvent e){
brush = "circ";
}
};
Action drawFree = new AbstractAction("自由绘制"){
public void actionPerformed(ActionEvent e){
brush = "free";
}
};
Action chooseForeColor = new AbstractAction("前景色"){
public void actionPerformed(ActionEvent e){
foreColor = chooseColor();
}
};
Action chooseBackColor = new AbstractAction("背景色"){
public void actionPerformed(ActionEvent e){
backColor = chooseColor();
imageInit();
}
};
//初始化
private void init(){
drawArea.setPreferredSize(new Dimension(WIDTH, HEIGHT));
f.add(drawArea);
//组合按钮
lineButton.addActionListener(drawLine);
rectButton.addActionListener(drawRect);
circButton.addActionListener(drawCirc);
freeButton.addActionListener(drawFree);
foreButton.addActionListener(chooseForeColor);
backButton.addActionListener(chooseBackColor);
clearButton.addActionListener(clearDrawArea);
p.add(lineButton);
p.add(rectButton);
p.add(circButton);
p.add(freeButton);
p.add(foreButton);
p.add(backButton);
p.add(clearButton);
f.add(p, BorderLayout.NORTH);
//组合右键菜单
popMenu.add(drawLine);
popMenu.add(drawRect);
popMenu.add(drawCirc);
popMenu.add(drawFree);
popMenu.addSeparator();
popMenu.add(chooseForeColor);
popMenu.add(chooseBackColor);
popMenu.addSeparator();
popMenu.add(clearDrawArea);
drawArea.setComponentPopupMenu(popMenu);
//image初始化
imageInit();
//按下鼠标时记录坐标
drawArea.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
startX = e.getX();
startY = e.getY();
}
});
//拖曳鼠标时绘画
drawArea.addMouseMotionListener(new MouseMotionAdapter(){
public void mouseDragged(MouseEvent e){
switch (brush){
case "free":
if((preX > 0) && (preY > 0)){
g.setColor(foreColor);
g.drawLine(preX, preY, e.getX(), e.getY());
}
drawArea.repaint();
setPreXY(e);
break;
case "line":
//如果鼠标移动,则撤消原来的直线
//以背景色画该直线模拟取消
if(((e.getX() != preX)&&(preX != -1)) || ((e.getY() != preY)&&(preY != -1))){
g.setColor(backColor);
g.drawLine(startX, startY, preX, preY);
}
g.setColor(foreColor);
g.drawLine(startX, startY, e.getX(), e.getY());
setPreXY(e);
drawArea.repaint();
break;
case "rect":
if(((e.getX() != preX)&&(preX != -1)) || ((e.getY() != preY)&&(preY != -1))){
g.setColor(backColor);
myDrawRect(startX, startY, preX, preY);
}
g.setColor(foreColor);
myDrawRect(startX, startY, e.getX(), e.getY());
setPreXY(e);
drawArea.repaint();
break;
case "circ":
if(((e.getX() != preX)&&(preX != -1)) || ((e.getY() != preY)&&(preY != -1))){
g.setColor(backColor);
myDrawOval(startX, startY, preX, preY);
}
g.setColor(foreColor);
myDrawOval(startX, startY, e.getX(), e.getY());
setPreXY(e);
drawArea.repaint();
break;
}
}
});
//松开鼠标时preX、preY复位
drawArea.addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent e){
preX = -1;
preY = -1;
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
//选择颜色对话框
private Color chooseColor(){
//显示有模式的颜色选取器,在隐藏对话框之前一直阻塞
return JColorChooser.showDialog(drawArea, "请选择画笔颜色",foreColor);
}
//画布初始化
private void imageInit(){
//image初始是黑色,填充白色背景
g.setColor(backColor);
g.fillRect(0,0,WIDTH, HEIGHT);
drawArea.repaint();
}
//preX,preY复位
private void setPreXY(MouseEvent e){
preX = e.getX();
preY = e.getY();
}
//自定义画矩形,起始点有可能在右下角,则须先排序再画
private void myDrawRect(int a, int b, int c, int d){
//根据(a,b),(c,d)两点画矩形,求出左上角顶点的坐标
//宽和高是固定的|a-c|和|b-d|
//罗列四种情况并计算得出如下算法
g.drawRect(a<c?a:c, b<d?b:d, Math.abs(a-c), Math.abs(b-d));
}
//自定义画圆,起始点有可能在右下角,则须先排序再画
private void myDrawOval(int a, int b, int c, int d){
//根据(a,b),(c,d)两点画矩形,求出左上角顶点的坐标
//宽和高是固定的|a-c|和|b-d|
//罗列四种情况并计算得出如下算法
g.drawOval(a<c?a:c, b<d?b:d, Math.abs(a-c), Math.abs(b-d));
}
//自定义画布,继承自JPanel,重写Paint方法
private class MyCanvas extends JPanel{
public void paint(Graphics g){
g.drawImage(image, 0,0,null);
}
}
public static void main(String[] args){
new Test().init();
}
}