Java小程序之高级画板重绘篇II
前言:前面我们利用不同的图形产生不同类,每画一个相应的图形,产生一个相应的图形的对象,并利用容器把对象存储起来,取出对象时,我们需要判断取出来的是什么对象,利用instanceof关键字来判断取出来的对象,然后根据取出来的是什么对象,画相应的图形;
你发现没有,其实这些类的属性和方法基本上是一样的,只是不同的对象对应不同的画法;我们可以把这些类抽象出来一个父类,利用多态的原理(参考前面多态知识的博客),重写子类的方法,同样可以实现画板的重绘;
Java中的三大特性:封装性,继承性,多态性;
附:Java中多态知识笔记:
Java自动转型和多态
自动转型:子类的对象自动转型为父类的类型
注意:如果转型后的对象调用方法,这个方法如果子类重写了,则执行的是重写后的,如果没有重写,则调用的是父类的
多态:多个同一个类型的对象,调用同一个方法,执行的过程不一样,多态的前提是有继承关系并且子类中重写了父类当中的某些方法;
重绘思路:
1、抽象出父类Shape以及父类的draw方法;
2、Line类,Rect类,Oval类继承父类
3、重写父类的draw方法,实现不同的子类画出不同的图形;
4、在DrawListener中,根据选择画笔的不同,先产生相应的对象,利用对象的draw方法,实现不同图形的画法;
5、把产生的对象装入容器对象;
6、在panelcenterchild中的paint方法中,将容器中的对象一个一个取出来,进行图形的重绘;
源代码:
抽象父类:Shape类
package com.huaxin.zhou1;
import java.awt.Color;
import java.awt.Graphics2D;
//抽象父类
public abstract class Shape {
public int x1,y1,x2,y2;//绘制图形的坐标
public Color color;//画笔颜色
public int width;//画笔粗细
//抽象的Draw方法
public abstract void Draw(Graphics2D g);
}
子类1:Line类
package com.huaxin.zhou1;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class Line extends Shape{
public Line(){
}
//子类构造函数
public Line(int x1,int y1,int x2,int y2,Color color,int width){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
this.color=color;
this.width=width;
}
//重写父类的Draw方法,实现直线的绘制
public void Draw(Graphics2D g) {
g.setColor(this.color);
g.setStroke(new BasicStroke(width));
g.drawLine(x1, y1, x2, y2);
}
}
子类2:Rect类
package com.huaxin.zhou1;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class Rect extends Shape{
public Rect(){
}
//矩形的构造方法
public Rect(int x1,int y1,int x2,int y2,Color color,int width){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
this.color=color;
this.width=width;
}
//重写父类的Draw方法,实现矩形的绘制
public void Draw(Graphics2D g) {
g.setColor(this.color);
g.setStroke(new BasicStroke(width));
g.drawRect(x1, y1, x2, y2);
}
}
子类3:Oval类
package com.huaxin.zhou1;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class Oval extends Shape{
public Oval(){
}
public Oval(int x1,int y1,int x2,int y2,Color color ,int width){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
this.color=color;
this.width=width;
}
public void Draw(Graphics2D g) {
g.setColor(this.color);
g.setStroke(new BasicStroke(width));
g.drawOval(x1, y1, x2, y2);
}
}
子类4:RoundRect类
package com.huaxin.zhou1;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class RoundRect extends Shape{
//子类新增属性,圆角矩形的角的弯曲程度
public int arcWidth,arcHeight;
public RoundRect(){
}
public RoundRect(int x1,int y1,int x2,int y2,int i,int j,Color color ,int width){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
this.arcWidth=i;
this.arcHeight=j;
this.color=color;
this.width=width;
}
public void Draw(Graphics2D g) {
g.setColor(this.color);
g.setStroke(new BasicStroke(width));
g.drawRoundRect(x1, y1, x2, y2, this.arcWidth, this.arcHeight);
}
}
DrawBorder类:注意在这个类中添加容器,并把容器地址传给DrawListener;panelcenterchild中重写paint方法,重绘图形;
package com.huaxin.zhou1;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.BevelBorder;
public class DrawBorder extends JFrame{
//声明颜色属性,并赋默认值
public Color c=Color.RED;
//按钮属性,便于其他类访问
public JButton bt ;
//容器
ArrayList list = new ArrayList();
public void initFrame(){
//设置窗体相关属性
this.setSize(600,500);
this.setTitle("我的画板");
this.setDefaultCloseOperation(3);
this.setLocationRelativeTo(null);
//窗体添加主面板
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
this.add(panel);
JPanel panelcenter = new JPanel(){
public void paint(Graphics g1) {
Graphics2D g=(Graphics2D)g1;
super.paint(g);
for (int i = 0; i <list.size(); i++) {
Shape shape =(Shape)list.get(i);
shape.Draw(g);
}
}
};
panelcenter.setBackground(Color.white);
panel.add(panelcenter);
//主面板添加左面板
JPanel panelleft = new JPanel();
panelleft.setPreferredSize(new Dimension(50,0));
panelleft.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
panelleft.setBackground(new Color(235,233,238));
panel.add(panelleft,BorderLayout.WEST);
//面板中添加按钮
//按钮归类,统一管路
ButtonGroup bg = new ButtonGroup();
for(int i=0;i<16;i++){
JRadioButton jrb = new JRadioButton();
//给按钮添加图片
ImageIcon img1 = new ImageIcon("images/draw"+i+".jpg");
ImageIcon img2 = new ImageIcon("images/draw"+i+"-1.jpg");
ImageIcon img3 = new ImageIcon("images/draw"+i+"-2.jpg");
ImageIcon img4 = new ImageIcon("images/draw"+i+"-3.jpg");
jrb.setIcon(img1);
jrb.setRolloverIcon(img2);
jrb.setPressedIcon(img3);
jrb.setSelectedIcon(img4);
jrb.setBorder(null);
//设置默认选中的按钮
if(i==10){
jrb.setSelected(true);
}
jrb.setActionCommand("pic"+i);
bg.add(jrb);
panelleft.add(jrb);
}
//主面板添加下方面板
JPanel paneldown =new JPanel();
paneldown.setPreferredSize(new Dimension(0,60));
paneldown.setLayout(null);
paneldown.setBackground(Color.gray);
panel.add(paneldown, BorderLayout.SOUTH);
//下方面板添加子面板
JPanel paneldownchild = new JPanel();
paneldownchild.setBackground(Color.cyan);
paneldownchild.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
paneldownchild.setBounds(10,10,280,40);
paneldown.add(paneldownchild);
//按钮特效
BevelBorder bb = new BevelBorder(0, Color.gray,Color.white);
BevelBorder bb1 = new BevelBorder(1, Color.gray,Color.white);
JPanel left = new JPanel();
left.setBackground(Color.white);
left.setLayout(null);
left.setBorder(bb);
left.setPreferredSize(new Dimension(40,40));
//左面板中的两棵颜色按钮
bt = new JButton();
bt.setBounds(5, 5, 20, 20);
bt.setBorder(bb1);
bt.setBackground(Color.black);
bt.setSize(20,20);
JButton bt1 = new JButton();
bt1.setBorder(bb1);
bt1.setBounds(15,15,20,20);
left.add(bt);
left.add(bt1);
//右面板
JPanel right = new JPanel();
right.setBackground(Color.BLUE);
right.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
right.setPreferredSize(new Dimension(240,40));
paneldownchild.add(left);
paneldownchild.add(right);
//给右面板的颜色按钮天添加监听器,注意传递this对象
ButtonListener bl =new ButtonListener(this);
//颜色数组,用来设置按钮的背景颜色
Color []colors = {new Color(0,56,67),new Color(89,3,14),new Color(189,3,14)
,new Color(89,93,14),new Color(89,113,14),new Color(89,73,14)
,new Color(89,3,14),new Color(89,3,14),new Color(29,83,14)
,new Color(89,3,184),new Color(189,233,14),new Color(89,253,14)
,new Color(89,93,14),new Color(89,89,94),new Color(1,3,14)
,new Color(9,83,94),new Color(89,178,147),new Color(9,33,164)
,new Color(34,23,14),new Color(89,173,154),new Color(8,193,194)
,new Color(9,253,76),new Color(89,240,104),new Color(199,73,4)};
//循环添加24个颜色按钮
for(int i=0;i<24;i++){
JButton bt3 = new JButton();
Color c=new Color(i*10,30-i,i*7+50);
bt3.setBackground(colors[i]);
bt3.setPreferredSize(new Dimension(20,20));
bt3.setBorder(bb);
bt3.addActionListener(bl);
right.add(bt3);
}
this.setVisible(true);
//画笔必须在setVisible后才能拿
Graphics g=panelcenter.getGraphics();
//传递画笔,按钮组管理对象,以及this对象
DrawListener dl =new DrawListener(g,bg,this,list);
//添加普通鼠标监听器
panelcenter.addMouseListener(dl);
//添加鼠标拖动监听器
panelcenter.addMouseMotionListener(dl);
}
}
DrawListener类:根据图形的不同选择,利用对台先产生不同的对象,再利用对象的Draw方法,绘制图形,并将该图形对象那个添加到容器中
package com.huaxin.zhou1;
import java.awt.AWTException;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
public class DrawListener implements MouseListener,MouseMotionListener{
public Graphics2D g;
public int x1,y1,x2,y2,ox,oy,x3,y3;
public ButtonGroup bg;
public String command;
public Color color;
public DrawBorder db;
public ArrayList list;
public boolean flag=true;
public static final Stroke s1 = new BasicStroke(1);
public static final Stroke s2 = new BasicStroke(10);
public static final Stroke s3 = new BasicStroke(15);
public Random r =new Random();
//构造函数1
public DrawListener(Graphics g1){
g=(Graphics2D)g1;
}
//构造函数2
public DrawListener(Graphics g2, ButtonGroup bg2) {
g=(Graphics2D)g2;
bg=bg2;
}
//构造函数3
public DrawListener(Graphics g2, ButtonGroup bg2, DrawBorder db1,ArrayList list) {
g=(Graphics2D)g2;
bg=bg2;
db=db1;
this.list=list;
}
//鼠标按下事件监听
public void mousePressed(MouseEvent e) {
//获取鼠标按下点的坐标
x1=e.getX();
y1=e.getY();
//判断选择的是左面板中的那个按钮被选中(前面已经设置每个按钮的名称了)
ButtonModel bm=bg.getSelection();//拿到按钮组中被选中的按钮
command=bm.getActionCommand();//拿到选中按钮的名字
}
public void mouseReleased(MouseEvent e) {
//获取鼠标释放的坐标
x2=e.getX();
y2=e.getY();
//如果选中的是绘制直线的按钮,那么根据鼠标按下点的坐标和释放点的左边绘制直线(两点确定一条直线)
if("pic10".equals(command))
{
Shape line = new Line(x1, y1, x2, y2,g.getColor(),1);
line.Draw(g);
list.add(line);
}//同理选中的是矩形按钮,那么绘制矩形(这里有绘制矩形的纠正,不纠正的话从右下角往左上角方向绘制矩形会出现问题,参看后面难点解析)
else if("pic12".equals(command)){
Shape rect = new Rect(Math.min(x2, x1),Math.min(y2, y1), Math.abs(x2-x1),Math.abs(y1-y2),g.getColor(),1);
rect.Draw(g);
list.add(rect);
}//绘制椭圆
else if("pic14".equals(command)){
Shape oval = new Oval(Math.min(x2, x1),Math.min(y2, y1), Math.abs(x2-x1),Math.abs(y1-y2),g.getColor(),1);
oval.Draw(g);
list.add(oval);
} else if("pic15".equals(command)){
Shape roundrect = new RoundRect(Math.min(x2, x1),Math.min(y2, y1), Math.abs(x2-x1),Math.abs(y1-y2),40,40,g.getColor(),1);
roundrect.Draw(g);
list.add(roundrect);
}//绘制曲线
else if("pic13".equals(command)){
//第一次画直线,设置标志
if(flag){
Shape line = new Line(x1, y1, x2, y2,g.getColor(),1);
line.Draw(g);
list.add(line);
flag=false;
//记录这次鼠标释放的坐标,作为下次绘制直线的起点
x3=x2;
y3=y2;
//记录第一点击的坐标,绘制封闭的曲线
ox=x1;
oy=y1;
}
else{
Shape line = new Line(x3, y3, x2, y2,g.getColor(),1);
line.Draw(g);
list.add(line);
//记录上次鼠标释放的坐标
x3=x2;
y3=y2;
}
}
//取色功能
else if("pic4".equals(command)){
//拿到相对面板的那个坐标
int x=e.getXOnScreen();
int y=e.getYOnScreen();
try {
Robot robot = new Robot();//Robot类的使用
//拿到坐标点的那个矩形
Rectangle rect = new Rectangle(x,y,1,1);
//生成该矩形的缓冲图片
BufferedImage bi =robot.createScreenCapture(rect);
//得到图片的背景颜色
int c =bi.getRGB(0, 0);
//将该颜色进行封装
Color color = new Color(c);
//将取色笔取来的图片设置成画笔的颜色
db.c=color;
} catch (AWTException e1) {
e1.printStackTrace();
}
}
}
public void mouseClicked(MouseEvent e) {
//多边形图形双击封闭
int count =e.getClickCount();
if(count==2 && "pic13".equals(command)){
Shape line = new Line(ox, oy, x2, y2,g.getColor(),1);
line.Draw(g);
list.add(line);
flag=true;
}
}
public void mouseEntered(MouseEvent e) {
color=db.c;//设置画笔颜色
g.setColor(color);
g.setStroke(s1);
}
public void mouseExited(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
int x=e.getX();
int y=e.getY();
//画笔功能
if("pic6".equals(command)){
Shape line = new Line(x1, y1, x, y,g.getColor(),1);
line.Draw(g);
list.add(line);
x1=x;
y1=y;
}
//橡皮擦功能
else if("pic2".equals(command)){
db.c=Color.white;
g.setColor(db.c);
g.setStroke(s3);
Shape line = new Line(x1, y1, x, y,g.getColor(),15);
line.Draw(g);
list.add(line);
x1=x;
y1=y;
}
//刷子功能
else if("pic7".equals(command)){
g.setStroke(s2);//设置画笔 粗细
Shape line = new Line(x1, y1, x, y,g.getColor(),10);
line.Draw(g);
list.add(line);
x1=x;
y1=y;
}
//喷桶功能
else if("pic8".equals(command)){
//随机产生30个-15到15之间的整数
for (int i = 0; i < 30; i++) {
int xp=r.nextInt(31)-15;
int yp=r.nextInt(31)-15;
//在x,y附件绘制原点
Shape line = new Line(x+xp, y+yp, x+xp, y+yp,g.getColor(),1);
line.Draw(g);
list.add(line);
}
}
}
public void mouseMoved(MouseEvent e) {
}
}
测试类:
package com.huaxin.zhou1;
public class Test {
//测试函数
public static void main(String[] args) {
DrawBorder db = new DrawBorder();
db.initFrame();
}
}
最小化后再最大化,图形还是在的哟!
总结:
1、Java小项目之高级画板基本完全竣工,用到的知识有继承,重写,多态,构造函数,对象传递,容器的使用等;
2、通过自己完成画板项目,对Java中的知识加深了理解,同时自己的动手能力得到加强;
3、个人经验,做东西一定首先要理解思路,思路要非常清楚,这样写东西才能知道怎么写,要一步一步来,不要急于求成;当然思路是建立在你对知识有一定的理解的基础上的;
4、一定要多动手实践,简单的代码也要敲上两三遍,深刻了解其中的原理;
5、学路漫漫,一起共勉!
画板左边的按钮图片资源以上传,在我的资源中!欢迎下载使用!