仿XP画图板是我接触JAVA以来的第二个项目。总得来说是比较成功的,不足是还有一些功能没有实现,之后有时间会慢慢完善。
话不多说,先上成果图(源码在最下面会附上链接):
可以看到,做的画图板已经实现了直线,矩形,椭圆,圆角矩形,多边形,刷子,喷漆,橡皮等基本功能,28种颜色可供选择。上方的菜单栏功能还没有开发~~
如何做到这一个画图板呢?接下来是我画图板的制作过程:
1 . 画图板界面框架
我将整个框架分为五个部位,主界面面板,菜单栏部位,画图板面板,工具栏面板,颜色选择面板;
主界面:先实例化一个窗体对象出来,设置好窗体的各种属性:标题、大小等。
setTitle("画板"); //窗体名字
setSize(600, 600); //设置窗体大小
setLocationRelativeTo(null); //设置窗体位置居中
setDefaultCloseOperation(EXIT_ON_CLOSE);//设置窗口点击关闭直接关闭
setResizable(false); //设置窗体不可改变
菜单栏:创建一个JmenuBar,把目录,和子目录加入JmenuBar,十分easy,但是没有实现具体相应功能;
JMenuBar jmb = new JMenuBar();
//添加菜单栏
JMenu jme = new JMenu("文件");
JMenu jme1 = new JMenu("编辑");
JMenu jme2 = new JMenu("查看");
JMenu jme3 = new JMenu("图像");
JMenu jme4 = new JMenu("颜色");
JMenu jme5 = new JMenu("帮助");
JMenuItem jmen1 = new JMenuItem("打开 Ctrl+O");
JMenuItem jmen2 = new JMenuItem("保存 Ctrl+S");
JMenuItem jmen3 = new JMenuItem("另存为..");
JMenuItem jmen4 = new JMenuItem("从扫描仪或照相机");
JMenuItem jmen5 = new JMenuItem("打印预览"); //so on
画图板:创建一个面板,设置相关属性便OK
setBackground(Color.WHITE);
setPreferredSize(new Dimension(600,600));
//创建线条边框
LineBorder lineBorder = new LineBorder(Color.GRAY,2);
//设置边框
setBorder(lineBorder);
工具栏:通过JRadioButton创建16个工具按钮,每一个工具有四个图片对应四个状态—默认状态—鼠标移到按钮处的状态—鼠标点击的状态—鼠标点击后的状态;把16个按钮加入一个ButtonGroup,实现单选。
public class ToolsPanel extends JPanel{
String[] allCommand = {"pgtailor","tailor","eraser","oildrum","color","enlarge","pencil","brush","spraypaint","textbox","line","arc","rect","polygon","oval","roundrect"};//添加按钮数组
MainFrame mainframe;
public ToolsPanel( MainFrame mainframe){
this.mainframe = mainframe;
setPreferredSize(new Dimension(60,0));
setLayout(new FlowLayout(FlowLayout.CENTER,0,5));
ButtonGroup gg = new ButtonGroup();
for (int i = 1; i < 17; i++) {
//创建imageicon组
ImageIcon i1 = new ImageIcon("img/"+i+"-1.png");
ImageIcon i2 = new ImageIcon("img/"+i+"-2.png");
ImageIcon i3 = new ImageIcon("img/"+i+"-3.png");
ImageIcon i4 = new ImageIcon("img/"+i+"-4.png");
JRadioButton jr = new JRadioButton();
jr.setActionCommand(allCommand[i-1]);
jr.setPreferredSize(new Dimension(25,25));
jr.setIcon(i1);
jr.setRolloverIcon(i2);
jr.setPressedIcon(i3);
jr.setSelectedIcon(i4);
jr.setMargin(new Insets(0, -2, 0, 0));
jr.addActionListener(new toolsactionlistener(mainframe));
add(jr);
gg.add(jr);
}
}
}
颜色选择面板:分为左右两个部分,左边为颜色被选择的状态显示面板,右边为28种颜色(通过颜色数组实现)
public class ColorsPanel extends JPanel{
MainFrame mainframe;
JButton jbt1;
JButton jbt2;
Color[] colors = { new Color(0, 0, 0), new Color(128, 128, 128),
new Color(128, 0, 0), new Color(128, 128, 0), new Color(0, 128, 0),
new Color(0, 128, 128), new Color(0, 0, 128),
new Color(128, 0, 128), new Color(128, 128, 64),
new Color(0, 64, 64), new Color(0, 128, 255),
new Color(0, 64, 128), new Color(128, 0, 255),
new Color(128, 64, 0), new Color(255, 255, 255),
new Color(192, 192, 192), new Color(255, 0, 0),
new Color(255, 255, 0), new Color(0, 255, 0),
new Color(0, 255, 255), new Color(0, 0, 255),
new Color(255, 0, 255), new Color(255, 255, 128),
new Color(0, 255, 128), new Color(128, 255, 255),
new Color(128, 128, 255), new Color(255, 0, 128),
new Color(255, 128, 64) };
public ColorsPanel(MainFrame mainframe){
this.mainframe = mainframe;
setPreferredSize(new Dimension(0,60));
setLayout(new FlowLayout(FlowLayout.LEFT,10,10));
//创建主面板
JPanel mainpanel =creatmainpanel();
mainpanel.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
//创建左边面板
JPanel leftpanel = creatleftpanel();
//创建右边面板
JPanel rightpanel = creatrightpanel();
mainpanel.add(leftpanel);
mainpanel.add(rightpanel);
add(mainpanel);
}
private JPanel creatmainpanel() {
JPanel jp = new JPanel();
//设置主面板大小
setPreferredSize(new Dimension(250,50));
// jp.setBorder(new EmptyBorder(10, 0, 0, 0));
return jp;
}
private JPanel creatrightpanel() {
JPanel jp = new JPanel();
jp.setPreferredSize(new Dimension(210, 30));
jp.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
//设置一个往上凸起的边框
BevelBorder border1 = new BevelBorder(1,Color.WHITE,Color.WHITE);
for (int i = 0; i < colors.length; i++) {
JButton jbt = new JButton();
jbt.setPreferredSize(new Dimension(15, 15));
jbt.setBackground(colors[i]);
jbt.setBorder(border1);
jp.add(jbt);
jbt.addMouseListener(new coloractionlistener(mainframe));
}
return jp;
}
private JPanel creatleftpanel() {
JPanel jp = new JPanel();
//设置组件大小
jp.setPreferredSize(new Dimension(30,30));
//设置组件背景颜色
jp.setBackground(Color.WHITE);
//设置组件布局为绝对布局
jp.setLayout(null);
//创建一个往上凸起的边框
BevelBorder border = new BevelBorder(1,Color.WHITE,Color.GRAY);
jp.setBorder(border);
//创建一个凹下的边框
BevelBorder border1 = new BevelBorder(0,Color.WHITE,Color.GRAY);
//创建一个按钮
jbt1 = new JButton();
//设置按钮的背景颜色
jbt1.setBackground(Color.BLACK);
//设置按钮的位置
jbt1.setBounds(5,5,15,15);
//设置按钮的不可点击
jbt1.setEnabled(false);
jbt1.setBorder(border1);
jbt2 = new JButton();
jbt2.setBackground(Color.WHITE);
jbt2.setBounds(10,10,15,15);
jbt2.setEnabled(false);
jbt2.setBorder(border1);
jp.add(jbt1);
jp.add(jbt2);
return jp;
}
2.加入监听
1.画图板 :获得画图面板的画笔,依据每一个传入的工具指令进行绘画,举例有:
public void mousePressed(MouseEvent e) {
x1 = e.getX();
y1 = e.getY();
}
public void mouseReleased(MouseEvent e) {
String stool = mainframe.getstool();
Color color = mainframe.getbkcolor();
Color color1 = mainframe.gettccolor();
<pre name="code" class="java">//直线
case "line":
x2 = e.getX();
y2 = e.getY();
graphics.setColor(color);
graphics.drawLine(x1, y1, x2, y2);
break;
//矩形
case "rect":
x2 = e.getX();
y2 = e.getY();
graphics.setColor(color);
raphics.drawRect(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2), Math.abs(y1-y2));
graphics.setColor(color1);
boolean equals = color1.getRGB()==Color.WHITE.getRGB();
if(equals){}
else {graphics.fillRect(Math.min(x1, x2)+1,Math.min(y1, y2)+1, Math.abs(x1-x2)-1, Math.abs(y1-y2)-1);
}
break;
//椭圆
case "oval":x2 = e.getX();y2 = e.getY();((Graphics2D) graphics).setStroke(new BasicStroke(1)); graphics.setColor(color);graphics.drawOval(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2), Math.abs(y1-y2)); graphics.setColor(color1);boolean equals1 = color1.getRGB() == Color.WHITE.getRGB();if (equals1) {} else {graphics.fillOval(Math.min(x1, x2) + 1, Math.min(y1, y2) + 1,Math.abs(x1 - x2) - 1, Math.abs(y1 - y2) - 1);}break; //圆角矩形case "roundrect":x2 = e.getX();y2 = e.getY();graphics.setColor(color);graphics.drawRoundRect(Math.min(x1, x2),Math.min(y1, y2), Math.abs(x1-x2), Math.abs(y1-y2), 10, 10);graphics.setColor(color1);boolean equals2 = color1.getRGB() == Color.WHITE.getRGB();if (equals2) {} else {graphics.fillRoundRect(Math.min(x1, x2)+1,Math.min(y1, y2)+1, Math.abs(x1-x2)-1, Math.abs(y1-y2)-1, 10, 10);}
}
//喷漆
case "spraypaint":
Random r = new Random();
int count = 0;
while (count < 8) {
int xP = r.nextInt(40) - 20;
int yP = r.nextInt(40) - 20;
double hypot = Math.hypot(xP, yP);
if (hypot < 10) {
graphics.setColor(color);
graphics.drawLine(x + xP, y + yP, x + xP, y + yP);
count++;
}
}
2.颜色面板:对选择的颜色进行传达--画图板监听,以及更新颜色选择面板的显示
public void mouseClicked(MouseEvent e) {
int button = e.getButton();
Object source = e.getSource();
if(source instanceof JButton)
{
JButton jbt = (JButton)source;
if(button == 1)
{
mainframe.setbkcolor(jbt.getBackground());
mainframe.colorspanel.setupcolor(jbt.getBackground());
}
if(button == 3)
{
mainframe.settccolor(jbt.getBackground());
mainframe.colorspanel.setdowncolor(jbt.getBackground());
}
}
}
3.工具栏面板:对选择的工具指令进行传达--画图板监听
public void actionPerformed(ActionEvent arg0) {
String actioncommand = arg0.getActionCommand();
// System.out.println(actioncommand);
mainframe.setstool(actioncommand);
3.画板重绘
我们在窗体中画了图形,当我们最小化窗体或者弹出颜色选择器时,原来的图形就不见了,于是我们需要学习重绘。此处是一个难点。
如何进行画板重绘:有两种方法
1.利用队列实现
将画过的图像均保存起来,这种保存方式适用于较简单的图案或者进行了较简单的操作。
2.利用Rebot方法进行重绘
该方法适用于图案较复杂,或者较复杂的操作。原理是将一幅图像保存下来,返回值为BufferedImage,我们再用BufferedImage的方法上上获取我们所需绘制的颜色,该方法同样的需要用到队列,将像素点的颜色一点一点的保存到队列里面。
因为画图板涉及到许多复杂的图形,且画出来多个图形时,再进行重绘浪费时间,代码复杂,所以我选择的是第二种方法:
//将图像保存进来
try {
rob = new Robot();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.setColor(new Color(0, 0, 0));
//获取pan面板左上角的位置和窗体大小
Point p = mf.drawpanel.getLocationOnScreen();
Dimension dim = mf.drawpanel.getSize();
Rectangle rect = new Rectangle(p, dim);
BufferedImage img = rob.createScreenCapture(rect);
image = new int[img.getHeight()][img.getWidth()];
//将图像按照像素分割成一个一个的点,将点的颜色存入数组
for (int i = 0; i < Listeners.image.length; i++)
{
for (int j = 0; j < Listeners.image[i].length; j++)
{
//将颜色存入数组
image[i][j] = img.getRGB(j, i);
}
}
//重绘
public void paint(Graphics g) {
// 调用父类的方法来正确的绘制窗体
super.paint(g);
// 如果数组存在,就重绘数据
if (null != Listeners.image) {
// 重绘数组
for (int i = 0; i < Listeners.image.length; i++)
{
for (int j = 0; j < Listeners.image[i].length; j++)
{
// 获取颜色
int c = Listeners.image[i][j];
// 创建颜色对象
Color color = new Color(c);
g.setColor(color);
g.drawLine(j, i, j, i);}
}
}
}
总结:画图板难点一是各个类之间的对象传递,二是复杂工具的实现,三是画图板的重绘,但是通过自己网上查阅资料,还是一步一步地克服了。做完这一个项目,感觉自己还是有很多地方不足,有很大的空间去提升!
加 油 吧! 少 年!