画图板初步
从只是一个窗口,到画图板各种组件一步一步的齐全。先说一下我最大的收获,也是以后要注意的。
一:设定参数变量的时候,变量名一定要起好,要有规律...不然的话自己都很难找。
二:传值的时候一定要小心,(例如在界面和监听器中传值),漏传,传错等,皆是空值。
心得总结:
通过这次的画图板,让我初步体会到了 做一个大的程序的一些经验。
以前学的只是一个个小的程序,变量不多,但是真正的大程序,数据繁琐,必须要做到每一步的清晰(尤其是我错了n次的传值)。
其次,一定要把同一部分的内容放在一起,不然会使程序显得十分凌乱不堪,一旦出错,会花上更多的精力去寻找。
最后,是关于空指针和空值的处理。
最有用的方法,无非就是一行一行的打印,当然,当我们遇到没用过不理解的方法的时候,也是要用到打印的方法去理解。
我所遇到的空值,主要是两种错误:
1:传值的时候出错。
2:声明了多次,无法识别。
下面,开始就自己的体会,详细分析画图板。
我的画图板,是就windows自带的画图板进行模仿。windows自带画图板,主要有以下几个部分组成,我们可以将其分区,分为centre,left,foot三个部分。其中,centre主要是画板以及背景,left主要是形状的工具条,而foot就比较复杂,有颜色的工具条,以及坐标。(这里是主要部分,其他小附件先略过)
主要分为画图板部分和重绘保存部分
先来画图板部分
创建界面,代码如下:
public class Draw extends JFrame {
/**
* drawlistener要在第一个class中声明 创立监听器对象
*/
Drawlistener drawlistener;
// 主函数
public static void main(String args[]) {
// 创建窗体对象,打开画板对象,固定格式
Draw draw = new Draw();
draw.draw1();
}
/**
* 初始化窗体
*/
public void draw1() {
this.setTitle("画图板");
this.setSize(600, 500);
this.setBackground(java.awt.Color.gray);
// 居中显示
this.setLocationRelativeTo(null);
// 关闭方式
this.setDefaultCloseOperation(3);
// 设置画板的布局格式
// 切记BorderLayout是java.awt.BorderLayout下面的要在前面引入
BorderLayout borderlayout = new BorderLayout();
this.setLayout(borderlayout);
这样就初步做好了我们的登陆界面
这里要注意的是,1,最好要居中显示,不然自动默认到左上角。2,记得要有关闭方式,不然仍然存在在系统内存里面。3,记得要设置布局模式,在此处,我用的是BorderLayout。4,主函数主要起得是一个引入的作用里面不要放太多的东西。
再来简单介绍下BorderLayout:
这是一个布置容器的边框布局,它可以对容器组件进行安排,并调整其大小,使其符合下列五个区域:北、南、东、西、中。每个区域最多只能包含一个组件,并通过相应的常量进行标识:NORTH
、SOUTH
、EAST
、WEST
、CENTER
。当使用边框布局将一个组件添加到容器中时,要使用这五个常量之一,例如:
Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(new Button("Okay"), BorderLayout.SOUTH);
设置下左边的工具条:
/**
* 开始工具条 左边
*/
// 创建左边的工作条,默认是水平的,我们此处要改成垂直的--vertical
javax.swing.JToolBar left = new javax.swing.JToolBar(
javax.swing.JToolBar.VERTICAL);
// 创建尺寸对象 记着要设计其长宽
java.awt.Dimension dimension1 = new java.awt.Dimension(70, 400);
// 设置大小 其传入的值是对象,所以传入dimension1
left.setPreferredSize(dimension1);
// 加在画板上面
this.add(left, BorderLayout.WEST);
这里加在画板上的时候,要小心是在WEST~~
对比windows画板可知,左边的工具条是有很多按钮的,于是,我们在左边工具条上加一个buttongroup,并且设置布局,如代码所示:
left.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
// 按钮分组
javax.swing.ButtonGroup group = new ButtonGroup();
// 大小
Dimension dimension2 = new Dimension(30, 30);
这里将其布局设置为流式布局~
下面,加按钮:
// 直线的
// 加单选按钮
JRadioButton line = new JRadioButton("直线");
// 设置其动作命令
line.setActionCommand("line");
// 设置默认选中
line.setSelected(true);
// 放在组里面
group.add(line);
left.add(line);
// 矩形~
JRadioButton rectangle = new JRadioButton("矩形");
rectangle.setActionCommand("rectangle");
// 放在组里面
group.add(rectangle);
left.add(rectangle);
这里我只贴出直线和矩形的,其他的方法一样,复制即可,就不贴出来了~~
需要注意的是,要有个默认选中,我的默认选中是直线。
下面是centre的部分。
中间要创建两个Jpanel,先是背景的,要注意设置其背景颜色为灰色,不要忘了,还有要记得布局是centre
// 创建中间的panel
javax.swing.JPanel cjp = new JPanel();
// 设置中间的背景颜色
cjp.setBackground(java.awt.Color.gray);
// 加在画板上 都是borderlayout的
this.add(cjp, BorderLayout.CENTER);
再来是画板的
/**
* 给中间加绘制的面板 jpanel
*/
// 设置布局 流式中间左对齐
FlowLayout center = new FlowLayout(FlowLayout.LEFT);
this.setLayout(center);
// 创建面板对象
javax.swing.JPanel jp = new Panel1();
// 设置大小
jp.setPreferredSize(new Dimension(450, 350));
// 设置背景颜色
jp.setBackground(java.awt.Color.WHITE);
// 加
cjp.add(jp);
注意:1,要设置为左对齐。2,背景颜色为白色。
下面是底部
由于底部由两部分组成,颜色的工具条和坐标,因此我们要在底部设置一个Jpanel
javax.swing.JPanel southjp = new javax.swing.JPanel();
// 设置底部面板的大小
southjp.setPreferredSize(new java.awt.Dimension(100, 60));
// 加在画板上面
this.add(southjp, BorderLayout.SOUTH);
// 设置底部面板的布局
southjp.setLayout(new BorderLayout());
底部的颜色条因为和形状的工具条基本相同,就不贴代码了~
然后就是坐标滴,坐标也是一个Jpanel~,上面主要显示为两部分~
1:坐标移动的显示
2:显示出画图二字
// 再设置一个jpanel...
javax.swing.JPanel zhuangtailan = new javax.swing.JPanel();
// 设置底部面板的大小
zhuangtailan.setPreferredSize(new java.awt.Dimension(600, 30));
// 设置背景色
zhuangtailan.setBackground(java.awt.Color.white);
// 加在画板上面
southjp.add(zhuangtailan, BorderLayout.CENTER);
// 加label label,标签
JLabel left1 = new JLabel("画板");
JLabel center1 = new JLabel("");
JLabel right = new JLabel("");
// 加在zhuangtailan(jpanel)
zhuangtailan.add(left1);
zhuangtailan.add(center1);
zhuangtailan.add(right);
最后一步 就是传值和显示~~
传值这里要格外小心,要传入的值有graphics, center1, group1, group
且在监听器出要一一对应
// 显示
this.setVisible(true);
// 这些要放在setvisible之后~!
// 从jp上获取画布对象
java.awt.Graphics graphics = jp.getGraphics();
// 创建监听器对象
// 在第23行已经定义了drawlistener,这里就不需要再定义,不然会 出现空值
drawlistener = new Drawlistener(graphics, center1, group1, group);
// 给jp加鼠标监听器
jp.addMouseListener(drawlistener);
jp.addMouseMotionListener(drawlistener);
}
其余注释以及注意事项皆在代码里面
监听器
监听器要继承MouseListener, MouseMotionListener 两个接口
注意要实现接口中的所有方法
同时,一定要声明以及定义,具体如代码:
public class Drawlistener implements MouseListener, MouseMotionListener {
// 定义x1,x2,y1,y2
private int x1, x2, y1, y2;
// 声明Graphics 是画直线的~画图~
private java.awt.Graphics g1;
// 声明ButtonGroup
// 颜色的
private javax.swing.ButtonGroup color_group;
// 形状的
private javax.swing.ButtonGroup group;
// line,RoundRect;rectangle
private String type = "line";
private String type1 = "black";
// 声明JLabel
private javax.swing.JLabel centerjl;
接下来是传值
传值的时候一定要小心~~
不然会空值!!!!!!!!这里出错了n次~
// 说明下Graphics g1,JLabel centerjl,ButtonGroup gl, ButtonGroup group
public Drawlistener(java.awt.Graphics g1, javax.swing.JLabel centerjl,
ButtonGroup gl, ButtonGroup group) {
// 传值
// 设数值的时候一定要要小心啊
this.g1 = g1;
this.color_group = gl;// 传入选择颜色的group
this.group = group;// 传入选择图形的group
// 记得把JLabel的值传过来 不然又是空指针
this.centerjl = centerjl;
下面主要就是方法
先来介绍下MouseListener, MouseMotionListener 两个接口的方法以及我们所需要的
MouseListener:
方法摘要 | |
---|---|
void |
mouseClicked(MouseEvent e) 鼠标按键在组件上单击(按下并释放)时调用。 |
void |
mouseEntered(MouseEvent e) 鼠标进入到组件上时调用。 |
void |
mouseExited(MouseEvent e) 鼠标离开组件时调用。 |
void |
mousePressed(MouseEvent e) 鼠标按键在组件上按下时调用。 |
void |
mouseReleased(MouseEvent e) 鼠标按钮在组件上释放时调用。 |
MouseMotionListener:
方法摘要 | |
---|---|
void |
mouseDragged(MouseEvent e) 鼠标按键在组件上按下并拖动时调用。 |
void |
mouseMoved(MouseEvent e) 鼠标光标移动到组件上但无按键按下时调用。 |
其中,我们主要用到的方法有:
mousePressed(MouseEvent e){颜色 吸管}
mouseReleased(MouseEvent e) {形状,重绘保存}
mouseMoved(MouseEvent e), mouseExited(MouseEvent e) {坐标的移动}
剩下的三个方法放在后面即可,不用~~
颜色 ,形状差不多
下面就贴出形状的代码
/**
* 形状
*/
// 在绘制前获取
javax.swing.ButtonModel buttonmodel = group.getSelection();
// 得到按钮模型的动作命令
type = buttonmodel.getActionCommand();
System.out.println(type);
// 得到鼠标按下时候的光标的坐标 用x1,y1来表示相对于画布
x1 = e.getX();
y1 = e.getY();
/**
* mouseReleased 鼠标释放 主要是画形状和截图保存
*/
public void mouseReleased(MouseEvent e) {
// 得到鼠标释放的时候光标的坐标用x2,y2来表示
x2 = e.getX();
y2 = e.getY();
/**
* 画形状
*/
// 开始画~ 直线
if (type.equals("10")) {
// 这是是固定格式
// 这里点的获取要小心~(x1,y1)
g1.drawLine(x1, y1, x2, y2);
}
// 矩形
if (type.equals("12")) {
g1.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2),
Math.abs(y1 - y2));
System.out.print(g1 + "reck");
}
// 圆角矩形
if (type.equals("15")) {
g1.drawRoundRect(Math.min(x1, x2), Math.min(y1, y2),
Math.abs(x1 - x2), Math.abs(y1 - y2), 35, 35);
}
// 弧线
if (type.equals("11")) {
g1.drawArc(x1, y1, 200, 200, 150, 150);
}
这里可以看到,我type.equals后面跟的不是前面定义的变量名,这个会在后面解释,主要是牵涉到我们把按钮变成和windows画图板一样的图标
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
下面是重绘部分
重绘有两种方法。
一:队列
二:数组:1:二位数组
2:直接截图保存
但是,如果我们想把数据储存在硬盘里面,只能用二位数组的保存方法
下面详解用二维数组的保存方法
在界面里:
现在主函数里面声明drawlistener
接着 我们在写一个class
也即嵌套一个class
代码如下
class Panel1 extends javax.swing.JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
// 重写绘制方法
// 当改变时自动调用
// 重写paint,参数从ava.awt.Graphics graphics 调用
public void paint(java.awt.Graphics graphics) {
// 调用父类方法 此时不可用this 要用super
super.paint(graphics);
// if循环
// 在监听器中创建的二维数组
// drawlistener,drawlistener.keep不是空值的时候进入循环
if (drawlistener != null && drawlistener.keep != null) {
// 遍历
for (int i = 0; i < drawlistener.keep.length; i++) {
for (int j = 0; j < drawlistener.keep[i].length; j++) {
// 取颜色
int cNum = drawlistener.keep[i][j];
// 若储存颜色变 了,就在绘制一次
if (cNum != this.getBackground().getRGB()) {
// 数字转颜色~强制转型
Color color = new Color(cNum);
// 绘制点
graphics.setColor(color);
graphics.drawLine(j, i, j, i);
}
}
}
}
}
}
}
1:这里的原理,就是把整幅图保存在内存里面,依据是其颜色的改变
2:关键字这里不可再用this,要用super~
下面是监听器
首先,声明保存的二维数组,在创建一个robot对象
*/
// 声明保存的二维数组(绘制区域)
int[][] keep;
// 声明robot
java.awt.Robot robot;
robot要写在public drawlistener里面
添加后代码如下
public Drawlistener(java.awt.Graphics g1, javax.swing.JLabel centerjl,
ButtonGroup gl, ButtonGroup group) {
// 传值
// 设数值的时候一定要要小心啊
this.g1 = g1;
this.color_group = gl;// 传入选择颜色的group
this.group = group;// 传入选择图形的group
// 记得把JLabel的值传过来 不然又是空指针
this.centerjl = centerjl;
// robot
/**
* 报错出现Unhandeled Exception...要加上try{}catch{} 这里就是个格式~
*/
try {
robot = new java.awt.Robot();
} catch (Exception ef) {
ef.printStackTrace();
}
}
1:try...catch是固定的格式
2:关于报错 如果出现Syntax error on token(s)...表示要加()
主要写在mouseReleased(MouseEvent e)里面 具体代码如下
Object object = e.getSource();
// object一定是中间绘制的面板将jpanel对象---jp~
javax.swing.JPanel jp = (javax.swing.JPanel) object;
// 得到jp左上角坐标,获取相对屏幕的坐标
java.awt.Point leftpoint = jp.getLocationOnScreen();
// 得到宽度 高度
java.awt.Dimension dimension1 = jp.getPreferredSize();
// 创建截取的区域对象,以jp左上角为起点,jp的大小
// 区域对象是长方形 所以用rectangle
java.awt.Rectangle rectangle = new java.awt.Rectangle(leftpoint,
dimension1);
// 截图
java.awt.image.BufferedImage image = robot
.createScreenCapture(rectangle);
/**
* 创建二维数组
*/
keep = new int[image.getHeight()][image.getWidth()];
// 写两个for循环
for (int i = 0; i < keep.length; i++) {
for (int j = 0; j < keep[i].length; j++) {
// 获取图像对应坐标点颜色,储存在数组中,对应的下标位置
/**
* 这里要小心 联系线代 i=y,j=x 不然就会弄反
*/
keep[i][j] = image.getRGB(j, i);
}
}
}
1:i=y,j=x,这里一定要注意!
2:截图只要是截的是矩形的区域
这样~~我们基本的架构已经做完啦~~
最后一步,就是美化一下~~
将按钮变成图标
主要也是用数组
首先,先要截好图~~(按钮的图片)
在将其放在代码所在的文件夹下面
先屏蔽掉以前的
新代码如下~
String[] commands = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15", "16" };
for (int i = 0; i < commands.length; i++) {
// 单选按钮
JRadioButton shape = new JRadioButton();
// 设置大小
// 小心这里传值又别弄错了!!!
shape.setPreferredSize(dimension2);
// 设置内边距
shape.setMargin(new java.awt.Insets(0, 0, 0, 0));
// 因为鼠标按下时候有4种形态
// 创建4个图标ImageIcon---图标对象
javax.swing.ImageIcon image1 = new javax.swing.ImageIcon(
"images/draw" + i + ".jpg");
javax.swing.ImageIcon image2 = new javax.swing.ImageIcon(
"images/draw" + i + "-1.jpg");
javax.swing.ImageIcon image3 = new javax.swing.ImageIcon(
"images/draw" + i + "-2.jpg");
javax.swing.ImageIcon image4 = new javax.swing.ImageIcon(
"images/draw" + i + "-3.jpg");
// 设置4种形态
shape.setIcon(image1);
shape.setRolloverIcon(image2);
shape.setPressedIcon(image3);
shape.setSelectedIcon(image4);
// 动作命令
shape.setActionCommand(commands[i]);
if (i == 6)
shape.setSelected(true);
// 放进组
group.add(shape);
// 加在左边的工具条上
left.add(shape);
}
现在,知道我前面监听器里面的11等数字的代表了吧~~~
画图板总体的架构已经完成,剩余的,就是其他功能的实现~~