画板总结

画板总结
今天,画板项目终于告一段落了。说句实在话,我觉得这个项目搞的时间实在是太长了。我第一天到蓝杰来就开始搞画图板,我大概是去年11月份来的吧,而现在却已经是2月份了。不过,虽然这个画板项目的时间比较长,但是我在其中的收获还是对的起这段时间的付出的。下面就来总结下我做画板这个项目所学到的东西和一些经验教训吧。
一、 对类的理解更加深入,逐渐养成了面向对象编程的思路。
做为一名计算机科学与技术的学生,我以前是接触过其他的编程语言的。我在来蓝杰学习之前学习过C,C++两门编程语言。上课时,C++老师虽然在上面讲了类的重载,继承,多态等等的东西,但是虽然我在上课是好像听懂了,下课后自己想用C++编一个什么什么东西来还是觉得无法下手。而且在C++里面的指针弄的我有点头痛,自己写完一个C++程序一运行,一般就会报N条红字出来。自从来这边接触JAVA之后我很快就感觉到它比C++更容易上手一些,学习C++时新手们看到指针就头痛了,而java 屏蔽了指针,这样至少对新手来说编程就变得容易一点了。以前学的C和C++两门语言中,我对C语言熟悉一些,我觉得好像像C++一样弄一个类把数据包装起来有些多余,我觉得很多东西,我用C就可以直接解决。所以,我编程思想一直停留在面向过程的编程方式上。接触了JAVA后一开始觉得什么都用对象,写程序有点不顺手。后来,代码写多了之后,我发现类的继承,重载,多态等一系列操作确实是编程更加人性化,更加简单了。
就拿重载来说吧,以前在C里面写程序时 如果要求两个数的最大值,int 型的数要写一个函数,FLOAT类型的数要写个名字不同的函数,调用时还要手动的调用INT类型的函数或者FLOAT类型的函数,显得比较麻烦。而利用JAVA或者C++里面的重载特性,
只需要写一个相同函数名的重载函数,然后调用时系统会自动的帮你调用INT 类型或者是FLOAT类型的那个函数,这就方便多了。所以从面向过程到面向对象,确实是一大进步。
二、 学会调用java API。
以前学C++时我对如何编一个界面非常感兴趣,但就是不知道如何去编。我还觉得编写一个界面是什么了不得的事情。但是我接触JAVA的API后才发现,编写一个界面是如此简单的事情。所有的组件你都可以在JAVA API中找到,你只需要注意它们调用的方法就可以编出你想要的界面了。
三、 形成了良好的编程习惯,和自己的编程风格。
以前我写代码时从来不会写注释,有时候程序出错回头检查时,自己都头晕。斌哥和龙哥每次看到我的代码没写注释,都反复强调要求我把注释写上。后来我强制要求自己写一些注释,慢慢的我开始习惯写注释了。而且我发现一点,就是在写注释的过程中,我脑海里就已经有了程序的框架,这样程序的思路就清晰了,写起来也不会因为前面写得不好卡住而觉得写不下去了。
还有就是编程风格,以前写代码的变量名定义是一天一个规范,每次都不同,造成了自己回看代码还要翻到前面变量定义那里去看看,才知道那个变量是做什么的。自从自己在网上看到别人用的命名定义方法,和龙哥又讲了一遍所谓驼峰法的命名规则之后,我为了使自己的代码更加规范。每次定义变量都严格按照规则来定义,比如说类的定义每个单词首字母都大写,其余小写。变量名或者方法名是首字母小写,后面的单词首字母大写,常量大写。这样我看到变量名就知道那个变量大概是做什么的。大大提高了代码的可读性。


上面那三点,是我做画板所提高的软实力。也就是好像感觉不那么重要的东西,其实我还是觉得比较重要的,一个良好的习惯,胜过一切其他技术方面的东西。习惯一养成,就很难去更改了,但是技术不知道,还可以重新学习。但是技术也是很重要的,所以下面我来谈一谈我从无到有做画板的经历吧。以下是我从无到有做学习做画板的过程。
1) 画板界面制作
2) 监听器的使用
3) 界面的美化
4) 图像的重绘
5) BMP文件的保存


1. 画板界面制作。这个到现在来说真的感觉没什么好说的,因为这没牵涉到算法,而且凡事有一定编程经验的看看系统API就会,关键是个熟练度的问题,你要把那些方法适用范围和调用方法注意一下就行了。我以前就是对着API里的类,接口自己实践的,熟能生巧。

// 主窗体
public void drawUI() {
// 设置窗体大小
this.setSize(600, 500);
// 设置窗体标题
this.setTitle("仿XP画板");
// 设置窗体在屏幕上居中
this.setLocationRelativeTo(null);
// 设置窗体默认退出操作为关闭
this.setDefaultCloseOperation(3);
// 设置窗体布局模式
BorderLayout bdlot = new BorderLayout();
this.setLayout(bdlot);
// 设置窗体可见
this.setVisible(true);
}

 

2. 监听器的使用。界面做好之后,我那个程序还不能画东西出来。没有实习画板的基本要求,正在这个时候,龙哥教了我要程序怎么画一个想要的东西到画板的界面上。
首先,创建一个鼠标监听器,用来监听鼠标事件。然后充主界面获取画布,通过监听器的构造器传入监听器中。最后只需在监听器相应的监听方法中用获得的画布画出相应的图形即可。
这里需要注意的是参数传递问题,和MouseListener里面方法执行的顺序
在java里基本类型参数是按值传递的,类类型是按引用传递(传地址)的
MouseListener里面的方法执行顺序依次是pressed  dragged  released  clicked
监听器类代码:

public class DrawListener extends java.awt.event.MouseAdapter
{   
public DrawListener(java.awt.Graphics g) {
this.g = (java.awt.Graphics2D)g;
}
  }

 
创建监听器代码:

DrawListener dlis = new DrawListener(g);

 
画图代码:

public void mousePressed(MouseEvent e) {
// 获取画布
g = (Graphics2D) drawPanel.getGraphics();
// 设置颜色
if (e.getButton() == 1) {
g.setColor(leftClick.getBackground());
presentColor = leftClick.getBackground();
color = rightClick.getBackground();
} else if (e.getButton() == 3) {
presentColor = rightClick.getBackground();
color = leftClick.getBackground();
g.setColor(rightClick.getBackground());
}

// 得到动作指令
type = shapeGroup.getSelection().getActionCommand();
style = listLis.getStyle();
x1 = e.getX();
y1 = e.getY();
if (type.equals("duobianxing")) {
if (flag == false) {
firstx = x1;
firsty = y1;
flag = true;
} else {
x1 = x2;
y1 = y2;
}
}
}

public void mouseReleased(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
if (type.equals("cut")) {

} else if (type.equals("select")) {

} else if (type.equals("fill")) {

} else if (type.equals("line")) {
// 设置笔画轮廓
BasicStroke stroke = new BasicStroke(style + 1);
g.setStroke(stroke);
g.drawLine(x1, y1, x2, y2);
// 重置笔画轮廓
stroke = new BasicStroke(0);
g.setStroke(stroke);

} else if (type.equals("quxian")) {

} else if (type.equals("rect")) {
if (style == 0) {
g.drawRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2));
} else if (style == 1) {
g.fillRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2));
} else if (style == 2) {
// 把颜色调成反色
g.setColor(color);
g.fillRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2));
// 把颜色调回去
g.setColor(presentColor);
}

} else if (type.equals("oval")) {
if (style == 0) {
g.drawOval(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2));
} else if (style == 1) {
g.fillOval(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2));
} else if (style == 2) {
g.setColor(color);
g.fillOval(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2));
g.setColor(presentColor);
}

} else if (type.equals("duobianxing")) {
g.drawLine(x1, y1, x2, y2);
if (e.getClickCount() >= 2) {
g.drawLine(firstx, firsty, x1, y1);
flag = false;
}

} else if (type.equals("roundrect")) {
if (style == 0) {
g.drawRoundRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2), 10, 10);
} else if (style == 1) {
g.fillRoundRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2), 10, 10);
} else if (style == 2) {
g.setColor(color);
g.fillRoundRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2), 10, 10);
g.setColor(presentColor);
}

}
// 每次释放后截屏
image.saveImage(drawPanel);
}

public void mouseDragged(MouseEvent e) {
if (type.equals("penwrite")) {
int x2 = e.getX();
int y2 = e.getY();

g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
} else if (type.equals("penqiang")) {
int x2 = e.getX();
int y2 = e.getY();
// 设置打点范围
int size = (style + 1) * 5;
// 创建随机生成数对象
Random rd = new Random();
// 在size范围内随机打点
for (int i = 0; i < 4; i++) {
int rdx = rd.nextInt(size);
int rdy = rd.nextInt(size);
g.drawLine((rdx - size / 2) + x2, (rdy - size / 2) + y2,
(rdx - size / 2) + x2, (rdy - size / 2) + y2);
}

} else if (type.equals("eraser")) {
g.setColor(color);
BasicStroke stroke = new BasicStroke(8, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
int x2 = e.getX();
int y2 = e.getY();
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
// 重置颜色
g.setColor(presentColor);
} else if (type.equals("brush")) {
BasicStroke stroke = new BasicStroke(8, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
g.setStroke(stroke);
int x2 = e.getX();
int y2 = e.getY();
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
}
 
3. 界面的美化。这个主要是利用布局管理器,在主界面上把按钮,容器等组件按一定顺序布置好,按钮设置好图标,其余组件设置好背景颜色等操作。
/**
* 创建左面板
* 
* @return 容器
*/
private JPanel leftPanel() {
// 创建容器
JPanel left = new JPanel();
left.setPreferredSize(new Dimension(80, 500));
// 设置为流式布局,水平垂直间距均为0
left.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
// 创建风格选择框
JList<ImageIcon> list = new JList<ImageIcon>();
list.setPreferredSize(new Dimension(40, 60));
// 设置边框
list.setBorder(new BevelBorder(BevelBorder.LOWERED, Color.WHITE,
Color.GRAY));
// 创建JList监听器
listLis = new ListListener(list);
// 添加监听器
list.addMouseListener(listLis);
// 创建按钮组
shapeGroup = new ButtonGroup();
// 创建 动作指令字符串
String str[] = { "cut", "select", "eraser", "fill", "xiguan", "view",
"penwrite", "brush", "penqiang", "word", "line", "quxian",
"rect", "duobianxing", "oval", "roundrect" };
// 创建绘画风格监听器
StyleListener stlLis = new StyleListener(list);
for (int i = 0; i < str.length; i++) {
// 添加图片网络路径
URL butUrl = DrawFrame.class
.getResource("images/draw" + i + ".jpg");
URL butRolloverUrl = DrawFrame.class.getResource("images/draw" + i
+ "-1.jpg");
URL butPressedUrl = DrawFrame.class.getResource("images/draw" + i
+ "-2.jpg");
URL butSelectedUrl = DrawFrame.class.getResource("images/draw" + i
+ "-3.jpg");
// 创建图标对象
ImageIcon but = new ImageIcon(butUrl);
ImageIcon butRollover = new ImageIcon(butRolloverUrl);
ImageIcon butPressed = new ImageIcon(butPressedUrl);
ImageIcon butSelected = new ImageIcon(butSelectedUrl);
// 创建单选按钮
JRadioButton radioButton = new JRadioButton();
// 设置按钮图标
radioButton.setIcon(but);
radioButton.setRolloverIcon(butRollover);
radioButton.setPressedIcon(butPressed);
radioButton.setSelectedIcon(butSelected);
// 设置动作指令
radioButton.setActionCommand(str[i]);
// 设置默认选中按钮为铅笔
if (radioButton.getActionCommand().equals("penwrite")) {
radioButton.setSelected(true);
}
// 给按钮添加风格监听器
radioButton.addActionListener(stlLis);
// 添加按钮到按钮组
shapeGroup.add(radioButton);
// 添加按钮到左面板上
left.add(radioButton);
}
// 添加按钮到左面板上
left.add(list);
return left;
}
 
4. 图像的重绘。因为屏幕上的图像都是保存在缓存区内的,如果屏幕的图像被覆盖或者是没有在屏幕上显示完全,或者是改变了窗体大小,图像在缓存区的数据都会发生变化,甚至消失。正因如此,所以我们需要在窗体发生改变是重新绘制原来的数据,让它不至于消失掉。这个很简单,重写画布对象的paint方法即可,因为每次窗体发生变化是系统会自动调用它的paint方法。代码如下:
public void paint(Graphics g) {
super.paint(g);
// 重绘代码
if (CatchScrn.a != null) {
for (int i = 0; i < CatchScrn.a.length; i++) {
for (int j = 0; j < CatchScrn.a[0].length; j++) {
// 取点
int point = CatchScrn.a[i][j];
if (point != this.getBackground().getRGB()) {
// 设置颜色
g.setColor(new Color(point));
// 画点
g.drawLine(j, i, j, i);
}
}
}
}
} 
 
5. BMP文件的保存。这个东西是最近开始做的,目前我只能完美写入24位的位图。
但是其他的比如说1 ,4, 8位位图的数据结构和储存原理我全部掌握了,写的话只是时间问题了。这个首先得讲下BMP文件结构。BMP有4部分组成。分别是文件头,信息头,调色板和数据域。
文件头:

信息头:

调色板:只有位深度低于24位的位图才有。
数据域:存储一个像素点的空间大小为位深度/8个字节,如果宽*位深度%4不为0则在宽度后补0,使宽*位深度能被4整除
注意事项:1:机器字节序列(一般是小端存储)这意味着你不能直接用writeInt()或者是writeShort()直接写入2:图像宽高要设置正确(宽度不包括补零项)3:文件偏移量bfOffBits要设置正确,24位为54(文件头14+信息头40),小于24位则还要加上颜色板数组长度4:位深度要设置正确5:文件一定要写完整6:其它的有硬性规定的就按照上面的表设置值,没有的可以直接写0进去。
以上就是我画板的总结了。做这个做了这么久,也是有点小感慨的。我拿我以前没来蓝杰时候的我跟现在对比一下,以前这种编程的工作量是我根本不感想的,虽然现在看来这并不算什么。
学东西不能想当然,一定要脚踏实地。不能说这个我好像懂了,一定要动动手,动手之后你会发现很多新的问题。有些东西不是你能不能做到的问题而是你肯不肯下功夫的问题。
好了,大概就这么一些吧。顺便祝福一下自己能够越学越好吧。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值