好久之前的一个小作业,用Java实现画板。毕竟当时花费不少功夫调试,在这里分享给大家,希望能够对需要的小伙伴有所帮助!声明一下,以下代码为参考网上相关代码修改,绝非完全原创。
运行效果图如下:
话不多说,直接上代码:
DrawBegin类:设置画板的框架,配置画布,进行监听。
package drawTable;
import java.awt.*;
import javax.swing.JFrame;
import drawTable.ColorChoose;
import drawTable.DrawPannel;
import drawTable.MouseListener;
import drawTable.ToolPannel;
/**
* 主函数类
*
* @author GZ
*
*/
public class DrawBegin extends JFrame {
/**
* 主函数
*
* @param args
*/
public static int Panel_width;
public static int Panel_height;
public static void main(String args[]) {
DrawBegin dp = new DrawBegin();
dp.showFrame();
}
private void showFrame() {
this.setTitle("GZ的画板");
this.setSize(800, 600);
this.setDefaultCloseOperation(3);
this.setLocationRelativeTo(null);
// 创建工具面板对象
ToolPannel tpl = new ToolPannel();
this.add(tpl, BorderLayout.WEST);
ColorChoose cpl = new ColorChoose();
this.add(cpl, BorderLayout.SOUTH);
DrawPannel dpl = new DrawPannel(tpl);//原发者
this.add(dpl, BorderLayout.CENTER);
// 将菜单栏的对象设置为窗体的菜单栏
this.setVisible(true);
//实例化鼠标事件处理类的对象
MouseListener M=new MouseListener(tpl,cpl,dpl);
//给事件源添加鼠标监听器方法,然后绑定事件处理类的对象
dpl.addMouseListener(M);
dpl.addMouseMotionListener(M);
Panel_width=dpl.panel.getWidth();
Panel_height=dpl.panel.getHeight();
}
}
DrawPannel类:设置画布。主要是在画布上呈现不同的形状,比如:矩形、椭圆等。
这里注意一点:画布在呈现过程中,由于不断的涮新页面,会导致“闪烁”现象。解决方法是使用缓存技术。
package drawTable;ToolPannel类:主要就是工具栏的实现。
package drawTable;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
/**
* 工具类
*
* @author GZ
*
*/
public class ToolPannel extends JPanel {
//声明一个存储用户选择的图形属性
public String shape_command = "line";
//private Graphics g;
// 构造方法
public ToolPannel() {
show();// 调用方法
}
// 制作工具栏的方法
public void show() {
// 实例化一个JPanel对象,充当容器来添加工具。
JPanel panel = new JPanel();
// 设置面板的布局为网格布局
panel.setLayout(new GridLayout(5, 2, 4, 4));
// 定义一个数组。
String[] array = { "brush.jpg", "easer.jpg",
"line.jpg", "oval.jpg",
"pencil.jpg", "rect.jpg",
"roundrect.jpg", "spray.jpg" };
// 循环创建按钮对象
for (int i = 0; i < array.length; i++) {
// 实例化一个ImageIcon的对象
// ImageIcon image = new ImageIcon(array[i]);
Icon image = new ImageIcon(this.getClass().getResource(
array[i]));
// 实例化按钮对象,并设置按钮的图标
JButton btn = new JButton(image);
// 设置按钮的大小
btn.setPreferredSize(new Dimension(28, 28));
//获取到唯一的动作命令(截取图片的名字)
String fileName = array[i].substring(array[i].indexOf("/")+1, array[i].lastIndexOf(".jpg"));
//设置按钮的动作命令值
btn.setActionCommand(fileName);
//设置按钮的动作命令监听器方法,绑定事件处理类的对象tool_listener
btn.addActionListener(toollistener);
// 将按钮添加到面板上
panel.add(btn);
// 设置Tool_panel的布局为流式布局布局
this.setLayout(new FlowLayout());
}
// 将panel添加到Tool_panel上
this.add(panel);
// 设置工具栏颜色
this.setBackground(null);
}
/**
* 使用匿名内部类来实现动作事件处理接口add
*/
private ActionListener toollistener = new ActionListener() {
/**
* 事件处理方法
*/
public void actionPerformed(ActionEvent e) {
//将按钮上的动作命令值获取到,存储到属性shape_command中
shape_command =e.getActionCommand();
//System.out.println(shape_command);
}
};
}
ColorChoose类:颜料板的实现。
package drawTable;
/**
* 颜色类
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JPanel;
public class ColorChoose extends JPanel {
//定义存储前景色和背景色的颜色属性
public Color bcolor = Color.WHITE,fcolor = Color.BLACK;
/**
* 构造方法(构造方法里面不能实例化对象)
*/
public ColorChoose() {
init();// 调用显示的方法
}
// 将颜色栏显示的方法
private void init() {
// 实例化一个panelFB对象
JPanel panelFB = new JPanel();
// 设置面板大小
panelFB.setPreferredSize(new Dimension(30, 30));
// 设置面板布局为空布局
panelFB.setLayout(null);
// 创建两个按钮(这里必须要定义为final类型,要控制其不能改变)
final JButton btnF = new JButton();
final JButton btnB = new JButton();
// 设置颜色按钮的位置和大小。setBounds,记住,去查询下。
btnF.setBounds(5, 5, 15, 15);
btnB.setBounds(10, 10, 15, 15);
// 设置颜色按钮的颜色
btnF.setBackground(Color.black);
btnB.setBackground(Color.red);
// 将颜色按钮添加到面板上
panelFB.add(btnF);
panelFB.add(btnB);
// 将可换颜色面板添加到总的颜色面板上
this.add(panelFB);
/**
* 实例化一个MouseAdapter抽象类的对象(匿名内部类)
*/
MouseAdapter ma = new MouseAdapter(){
/**
* 鼠标按下时执行的方法
*/
public void mousePressed(MouseEvent e) {
//获取到事件源对象 按钮
JButton btn = (JButton)e.getSource();
//判断当前点击的是否是左键,如果是则改变背景色,条件是e.getButton()返回的值是否为1
if(e.getButton() == 1){
//获取到按钮上的颜色了
fcolor = btn.getBackground();
//改变背景色按钮上的颜色
btnF.setBackground(fcolor);
}else if(e.getButton() == 3){//表示点击了右键
//获取到按钮上的颜色了
bcolor = btn.getBackground();
//改变背景色按钮上的颜色
btnB.setBackground(bcolor);
}
}
};
// 创建一个 可选颜色面板的对象
JPanel panel1 = new JPanel();
Color[] array = {Color.BLACK,Color.BLUE,Color.CYAN,Color.DARK_GRAY,
Color.GRAY,Color.GREEN,Color.LIGHT_GRAY,Color.MAGENTA,Color.ORANGE,
Color.PINK,Color.RED,Color.YELLOW,Color.WHITE,new Color(150,200,130),
new Color(100,120,130),new Color(150,125,130),new Color(150,125,130),new Color(150,125,130),
new Color(150,125,130),new Color(150,90,130),new Color(150,160,130),new Color(150,175,130),
new Color(150,190,130),new Color(150,125,120),new Color(160,125,130),
new Color(180,100,130),new Color(150,125,160),new Color(150,125,160) };
for (int i = 0; i < array.length; i++) {
// 创建颜色按钮对象
JButton jbn = new JButton();
// 设置颜色按钮大小,颜色
jbn.setPreferredSize(new Dimension(20, 20));
jbn.setBackground(array[i]);
//给按钮添加鼠标监听器方法,绑定事件处理类的对象
jbn.addMouseListener(ma);
// 将按钮添加到可选颜色面板对象上
panel1.add(jbn);
}
// 设置可选颜色面板上的布局为网格布局
panel1.setLayout(new GridLayout(2, 14));
// 设置为从左至右的流式布局
this.setLayout(new FlowLayout(FlowLayout.LEFT));
// 设置背景颜色
this.setBackground(null);
// 将可选颜色面板添加到颜色面板上
this.add(panel1);
}
}
Shape类:基本形状类。
package drawTable;
import java.awt.*;
/**
* Created by GZ on 2017/11/4 9:23.
* Description:定义形状,实现形状的显示
*/
public class Shape {
private int x_start, y_start, x_end, y_end;
private Color current_color;
private String shape_command;
protected int getXstart() {
return x_start;
}
void setXstart(int x1) {
this.x_start = x1;
}
protected int getYstart() {
return y_start;
}
void setYstart(int y1) {
this.y_start = y1;
}
protected int getXend() {
return x_end;
}
void setXend(int x2) {
this.x_end = x2;
}
protected int getYend() {
return y_end;
}
void setYend(int y2) {
this.y_end = y2;
}
protected Color getColor() {
return current_color;
}
protected void setColor(Color color) {
this.current_color = color;
}
protected String getShapeCommand() {
return shape_command;
}
public void setShapeCommand(String shape_command) {
this.shape_command = shape_command;
}
public Shape(int x1, int y1, int x2, int y2, Color color, String shapeCommand) {
this.x_start = x1;
this.y_start = y1;
this.x_end = x2;
this.y_end = y2;
this.current_color = color;
this.shape_command = shapeCommand;
}
public String toString() {
return this.getXstart()+","+this.getYstart()+","+this.getXend()+","+this.getYend()+","+this.getColor()+","+this.getShapeCommand();
}
public void draw(Graphics g){
Graphics2D g2d=(Graphics2D)g;
g2d.setStroke(new BasicStroke(1));
//抗锯齿处理
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(this.getColor());
String shape_command=getShapeCommand();
System.out.println(this);
//命令判别
if(shape_command.equals("line")){
g.drawLine(x_start, y_start, x_end, y_end);
}else if(shape_command.equals("oval")){
if(x_start
}else if(x_start>x_end&&y_start>y_end){
g.drawOval(x_end, y_end, Math.abs(x_start-x_end), Math.abs(y_start-y_end));
}else if (x_start>x_end&&y_start
}else if(x_start
g.drawOval(x_start, y_end,Math.abs(x_start-x_end) , Math.abs(y_start-y_end));
}
}else if(shape_command.equals("rect")) {
if(x_start
}else if(x_start>x_end&&y_start>y_end){
g.drawRect(x_end, y_end, Math.abs(x_start-x_end), Math.abs(y_start-y_end));
}else if(x_start>x_end&&y_start
}else if(x_start
g.drawRect(x_start, y_end, Math.abs(x_start-x_end), Math.abs(y_start-y_end));
}
}else if(shape_command.equals("roundrect")){
if(x_start
}else if(x_start>x_end&&y_start>y_end){
g.drawRoundRect(x_end, y_end, Math.abs(x_start-x_end), Math.abs(y_start-y_end),10,10);
}else if(x_start>x_end&&y_start
}else if(x_start
g.drawRoundRect(x_start, y_end, Math.abs(x_start-x_end), Math.abs(y_start-y_end),10,10);
}
}else if(shape_command.equals("pencil")){
g.drawLine(x_start, y_start, x_end, y_end);
//将直线的结束点的坐标作为下一条直线的开始点
//x_start=x_end;
//y_start=y_end;
}else if(shape_command.equals("brush")) {
g2d.setStroke(new BasicStroke(8));
g2d.drawLine(x_start, y_start, x_end, y_end);
g2d.setStroke(new BasicStroke(1));
}else if(shape_command.equals("easer")) {
g2d.setStroke(new BasicStroke(8));
g2d.drawLine(x_start, y_start, x_end, y_end);
g2d.setStroke(new BasicStroke(1));
} else if(shape_command.equals("spray")) {
g2d.drawLine(x_start+x_end, y_start+y_end, x_start+x_end+1, y_start+y_end+1);
}
}
}
MouseListener类:MVC中的controller,主要是业务逻辑。重点讲解一下相关工具的实现,具体图形是拖拽呈现但不保存,鼠标抬起的那一刻图形定格并保存到Shape数组中。喷枪依靠随机的生成点营造效果,生成点多了就变成了刷子。铅笔就是点动成线的原理,诸多的小线段进行首尾连接。橡皮可以类比颜色为白的刷子。
package drawTable;
/**
* 鼠标监听器类
*/
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
public class MouseListener extends MouseAdapter{
// 定义一些基本属性
// 画笔颜色
private Color penColor = Color.black;
// 画笔
private Graphics g;
// 坐标参数
private int x_start, y_start, x_end, y_end;
// 画板上的工具栏
private ToolPannel tp ;
// 颜料板
private ColorChoose cp;
// 画板上的画布
// 标识
public static boolean flag;
private DrawPannel dp;
private final static int MAX_CACHED_PAINTED_SHAPE = 100000;
//一个数组,用于缓存已经绘制的所有形状
private Shape[] shapeArr = new Shape[MAX_CACHED_PAINTED_SHAPE];
//当前已经绘制的形状的数目
private int index;
//绘制的每一个形状(除去拖动)
private Shape shape;
//保存拖动过程中的每一个形状
private Shape shape_dragged;
//图像缓存空间
String shapeCommand = "line";//形状
/**
* 构造方法
*/
public MouseListener(ToolPannel tp ,ColorChoose cp,DrawPannel dp){
this.tp = tp;
this.cp = cp;
this.dp = dp;
//this.jp = dp.panel;//画板上的画布
this.g = dp.panel.getGraphics(); //获得画笔
}
@Override
public void mousePressed(MouseEvent e) {
// 获得x_start,y_start的坐标
x_start = e.getX();
y_start = e.getY();
if(e.getButton() == 1){
penColor = cp.fcolor;
}else if(e.getButton() == 3){
penColor = cp.bcolor;
}
System.out.println("x1->" + x_start + ",y1->" + y_start);
}
/**
* 事件监听类。包括按钮点击事件和鼠标事件
*/
//鼠标释放监听事件
@Override
public void mouseReleased(MouseEvent e) {
x_end = e.getX();
y_end = e.getY();
System.out.println("x2->" + x_end + ",y2->" + y_end);
//判别区域
if (x_start<0||x_start >= DrawBegin.Panel_width||y_start<0||y_start>DrawBegin.Panel_height) {
System.out.println("Can't draw out of Panel!");
return;
}
if (x_end<0){
x_end=0;
}
if(y_end<0) {
y_end=0;
}
if (x_end>DrawBegin.Panel_width){
x_end=DrawBegin.Panel_width;
}
if(y_end>DrawBegin.Panel_height) {
y_end=DrawBegin.Panel_height;
}
g.setColor(penColor); //画笔颜色默认为黑色
/**
* 松开鼠标时,将已经绘制的形状加入缓存
*/
if(tp.shape_command.equals("easer")) {
shape = new Shape(x_start, y_start, x_end, y_end, Color.white,tp.shape_command);
shapeArr[index++] = shape;
dp.display(index, shapeArr, shape);
}else {
shape = new Shape(x_start, y_start, x_end, y_end, penColor,tp.shape_command);
shapeArr[index++] = shape;
dp.display(index, shapeArr, shape);
}
}
//鼠标拖拽监听事件
/**
* 鼠标拖拽预览。
* 预览的实现:拖拽之后,立即重绘,但是不把绘制的形状加入缓存。
*/
@Override
public void mouseDragged(MouseEvent e){
//判别画图区域是否在画板范围内
g.setColor(penColor); //画笔颜色默认为黑色
int x_end = e.getX();
int y_end = e.getY();
System.out.println("Dragged:x->" + x_end + ",y->" + y_end);
if (x_start<0||x_start >= DrawBegin.Panel_width||y_start<0||y_start>DrawBegin.Panel_height) {
System.out.println("Can't draw out of Panel!");
return;
}
if (x_end<0){
x_end=0;
}
if(y_end<0) {
y_end=0;
}
if (x_end>DrawBegin.Panel_width){
x_end=DrawBegin.Panel_width;
}
if(y_end>DrawBegin.Panel_height) {
y_end=DrawBegin.Panel_height;
}
//shape_dragged = new Shape(x_start, y_start, x_end, y_end, penColor, tp.shape_command);
//display();//根据不同命令开始画画
if(tp.shape_command.equals("brush")){
shape = new Shape(x_start, y_start, x_end, y_end, penColor,tp.shape_command);
x_start=x_end;
y_start=y_end;
shapeArr[index++] = shape;
dp.display(index, shapeArr, shape);
}else if(tp.shape_command.equals("easer")){
//g.setColor(Color.WHITE);
shape = new Shape(x_start, y_start, x_end, y_end, Color.white,tp.shape_command);
x_start=x_end;
y_start=y_end;
shapeArr[index++] = shape;
dp.display(index, shapeArr, shape);
// g.setColor(penColor);
}else if(tp.shape_command.equals("spray")){//如果是喷枪
//获取坐标值
x_end=e.getX();
y_end=e.getY();
//实例化一个随机数对象
Random rand = new Random();
//循环画多个点
for(int i=0;i<20;i++){
//随机生成新的x和y点,用来绘制不同的点
int x = rand.nextInt(8);
int y = rand.nextInt(8);
//开始绘制点
shape = new Shape(x_end, y_end, x, y, penColor,tp.shape_command);
shapeArr[index++] = shape;
dp.display(index, shapeArr, shape);
}
}else if(tp.shape_command.equals("pencil")){
shape = new Shape(x_start, y_start, x_end, y_end, penColor,tp.shape_command);
x_start=x_end;
y_start=y_end;
shapeArr[index++] = shape;
dp.display(index, shapeArr, shape);
}else{
shape_dragged = new Shape(x_start, y_start, x_end, y_end, penColor, tp.shape_command);
dp.display(index,shapeArr,shape_dragged);
}
}
}
上述就是相关代码的说明,总体上的效果还是比较理想的。但在实现过程中,每一次在画布上增添新的笔墨,其实都要整体重绘,感觉在实现方法和效率上不是很合理!不知道Windows的画板是如何实现的?!应该有更为优化的方法吧!上述代码改了很长时间,又由于是作业的缘故,所以当时特别希望能够有完善的代码直接拿来用,但之后想想,感觉还是通读代码后,加之自己的理解,在此基础上完善代码的经历更有意义!所以也期望阅读博文的你有用心去研读!
完整代码资源可在此链接下载:https://download.csdn.net/download/gzharryanonymous/10425695