要解决的问题:
1:防止画的图形会画在功能按钮的上面
功能按钮放一个Jpanel里,变量为jp1,画板放另一个Jpanel里,变量为jp2,画笔Graphics g就从jp2里获取。这样画笔的坐标原点就是以jp2为基础。
2:如何实现在鼠标拖动中实时显示直线,矩形和椭圆?
第一种解决方案是在拖动过程中画两种线,一种是我们想画的线,一种是和背景颜色相同的线来擦掉之前画的线。这种解决方案会产生一个问题:在拖动过程中,如果遇到了我们拖动之前就画好的线它也会擦除,就算我们已经把之前画的线保存了,没有真正意义上的擦除,但是实时显示是擦除的。所以该方法不太行。
第二种解决办法是给Jpanel的paint()方法增加点东西,也是我使用的方法,完美解决了方案一的不足之处
让每次执行mouseDragged代码块的时候都会执行paint()方法,paint()方法不光会保存的线条画出来,还会根据起点和鼠标当前位置点和图形名字实时画出直线,椭圆或矩形。
3窗体状态在发生变化的时候会一种触发repait()方法,如果不把之前画的线保存下来,之前画的线就会消失,画布就会变成空白
我的解决方案是定义一个Shape类,用Arraylist保存它,重写paint()方法。paint()方法本身就是一种刷新原来的画布,重新生成paint()方法里的代码的方法。这个方法并不是在原有画布的基础上再画图形。要想在原有基础上再画图,必须要保存之前的画布数据。
4如何画多边形?
系统自带的画图工具的效果是这样的:每点击一个点,画一条边,双击就让首尾两点合并。
要想实现画多边形,出来每次要知道按压鼠标和松开鼠标的坐标,还需要保存起始点的坐标和每次松开鼠标后的坐标
5如何画曲线
g.drawLine(x3,y3,x4,y4)其实就是画一个点,鼠标每次拖动即换坐标后就会不断运mouseDragged这个方法,就能实现画曲线的目标。
6如何实现橡皮擦功能?
与实现曲线原理一样,不过是线条颜色换成和画板背景颜色相同的颜色了。
7鼠标监听器的代码执行顺序:
鼠标要想拖拽的时候实现想要的效果,必须要知道拖拽看似是一个动作,其实它会执行三个方法的代码。第一步mousePressed,第二步mouseDragged,第三步mouseReleased。只有知道这个我们才能知道三个方法里该各自写什么代码。
8完整代码
主类
import javax.swing.*;
import java.awt.*;
public class DrawUI {
public void initUI(){
JFrame dtool =new JFrame();
dtool.setTitle("画图工具");
dtool.setSize(800,800);
dtool.setLocation(600,200);
dtool.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
dtool.setLayout(new BorderLayout());
JPanel jp=new JPanel();
String[] function={"直线","曲线","等腰三角形","任意三角形","矩形","椭圆","多边形","橡皮擦"};
DrawMouse listerner =new DrawMouse();
for (String item : function) {
JButton jbshape = new JButton(new ImageIcon("D:\\lianxi\\imagine\\" + item + ".png"));
jbshape.setPreferredSize(new Dimension(30, 30));
jbshape.setActionCommand(item);
jp.add(jbshape);
jbshape.addActionListener(listerner);
// System.out.println(jbshape.getActionCommand());
}
String[] color={"yellow","red","black","green"};
for (String value : color) {
JButton jbcolor = new JButton(new ImageIcon(""));
jbcolor.setPreferredSize(new Dimension(30, 30));
// 根据color[j]的值设置背景颜色
switch (value) {
case "yellow" -> jbcolor.setBackground(Color.YELLOW);
case "red" -> jbcolor.setBackground(Color.RED);
case "black" -> jbcolor.setBackground(Color.BLACK);
case "green" -> jbcolor.setBackground(Color.GREEN);
default ->
// 默认设置为黑色
jbcolor.setBackground(Color.BLACK);
}
jp.add(jbcolor);
jbcolor.addActionListener(listerner);
}
// jp.add(toolBar);
MyJPanel jp2=new MyJPanel();
jp2.setshapes(listerner.getshapes());
jp2.setBackground(Color.white);
dtool.add(jp,"North");
dtool.add(jp2,"Center");
JPanel jp3=new JPanel();
jp3.setLayout(new FlowLayout());
jp3.setPreferredSize(new Dimension(220,70));
String[] strock={"三像素","五像素","八像素"};
for (String s : strock) {
JButton jbshape = new JButton(new ImageIcon("D:\\lianxi\\imagine\\" + s + ".png"));
jbshape.setPreferredSize(new Dimension(215, 20));
// jbshape.setActionCommand(strock[i]);
jbshape.addActionListener(listerner);
jp3.add(jbshape);
// System.out.println(jbshape.getActionCommand());
}
jp.add(jp3);
dtool.setVisible(true);
//画笔:自定义内容显示在哪个组件上,画笔就从该组件上获取
//从窗体上获取画笔,一定要在窗体显示可见之后
Graphics g = jp2.getGraphics();
listerner.setDrawMouse(g);
listerner.jp2=jp2;
//给图标添加监视器
//给窗体添加鼠标监听器方法
jp2.addMouseListener(listerner);
jp2.addMouseMotionListener(listerner);
}
public static void main(String[] args) {
DrawUI draw = new DrawUI();
draw.initUI();
}
}
DrawMouse类
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
//下一步曲线 粗细 橡皮擦 实时显示,重绘问题
public class DrawMouse implements MouseListener, ActionListener, MouseMotionListener {
//构造方法
public Graphics g;
public int x1=0;
public int x2=0;
public int y1=0;
public int y2=0;
public int x4;
public int y4;
//存起始点
public int ployX;//值为起始点
public int ployY;
public int ployX1;//值为上一次结束画直线的末尾点
public int ployY1;
//按钮名称 默认为null 可直接画曲线
public String ActionCommand="直线";
public int flag=0;//判断多边形第一条边是否画出
public String colorcommand;
public Color color=Color.black;
public Stroke stroke = new BasicStroke(8);
// public JFrame dtool;
public MyJPanel jp2;
private Shape s1,s2,s3;
private ArrayList<Shape> shapes=new ArrayList<>();
public ArrayList<Shape> getshapes(){
return shapes;
}
public void setDrawMouse(Graphics g){
this.g = g;
}
public void actionPerformed(ActionEvent e) {
//获取按钮标签
JButton jButton = (JButton) e.getSource();
if(!(jButton.getActionCommand().equals(""))) {
ActionCommand = jButton.getActionCommand();
}
System.out.println("ActionCommand = "+ActionCommand);
colorcommand = jButton.getIcon().toString();
System.out.println("路径 = "+jButton.getIcon().toString());
if(jButton.getIcon().toString() == "") {
//提取颜色
color = jButton.getBackground();
g.setColor(color);//画笔颜色
System.out.println(color);
}
switch (colorcommand){
case "D:\\lianxi\\imagine\\三像素.png":
stroke =new BasicStroke(3);
break;
case "D:\\lianxi\\imagine\\五像素.png":
stroke=new BasicStroke(5);
break;
case"D:\\lianxi\\imagine\\八像素.png":
stroke=new BasicStroke(8);
break;
}
Graphics2D g2=(Graphics2D)g;
g2.setStroke(stroke);
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed (MouseEvent e){
//g.setColor(color);
System.out.println("按下");
//获取当前坐标值
y1 = e.getY();
x1 = e.getX();
}
//存着上一个x2,y2,并获取释放后落点坐标
public void mouseReleased (MouseEvent e) {
System.out.println("松开");
ployX1 = x2;//先存着再更新x2,y2
ployY1 = y2;
//获取当前坐标值
x2 = e.getX();
y2 = e.getY();
// if (flag==0) {
// g.drawLine(x1, y1, x2, y2);
// //存起始点
// ployX = x1;
// ployY = y1;
// flag=1;
// System.out.println("继续点击");
// }
if (ActionCommand.equals("任意三角形")&&(flag==1)) {
g.drawLine(ployX,ployY, x2,y2);
s2=new Shape(ployX,ployY, x2,y2,"直线",color,stroke);
shapes.add(s2);
g.drawLine(ployX1,ployY1, x2, y2);
s3 = new Shape(ployX1,ployY1, x2, y2,"直线",color,stroke);
shapes.add(s3);
flag = 0;
}
else if (ActionCommand.equals("任意三角形") && (flag==0)) {
//存起始点
ployX = x1;
ployY = y1;
//画多边形的第一条边
flag=1;
g.drawLine(x1, y1, x2, y2);
s1 = new Shape(x1, y1, x2, y2,"直线",color,stroke);
shapes.add(s1);
}
else if (ActionCommand.equals("等腰三角形")) {
g.drawLine(x1, y2, (x2 + x1) / 2, y1);
s1 = new Shape(x1, y2, (x2 + x1) / 2, y1,"直线",color,stroke);
shapes.add(s1);
g.drawLine((x2 + x1) / 2, y1, x2, y2);
s2=new Shape((x2 + x1) / 2, y1, x2, y2,"直线",color,stroke);
shapes.add(s2);
g.drawLine(x1, y2, x2, y2);
s3 = new Shape(x1, y2, x2, y2,"直线",color,stroke);
shapes.add(s3);
}
else if (ActionCommand.equals("多边形") && (flag==0)) {
//存起始点
ployX = x1;
ployY = y1;
//画多边形的第一条边
g.drawLine(x1, y1, x2, y2);
s1 = new Shape(x1, y1, x2, y2,"直线",color,stroke);
shapes.add(s1);
System.out.println("多边形第一条线画出");
flag=1;
}
else if (ActionCommand.equals("多边形")&& (flag==1)) {
//双击形成封闭图形
if (e.getClickCount() == 2) {
//首尾点相连
g.drawLine(x1, y1, ployX, ployY);
s1 = new Shape(x1, y1, ployX, ployY,"直线",color,stroke);
shapes.add(s1);
flag=0;
}//否则继续循序连接
else {
g.drawLine(x2, y2, ployX1, ployY1);
s1 = new Shape(x2, y2, ployX1, ployY1,"直线",color,stroke);
shapes.add(s1);
System.out.println("多边形第二条线画出");
}
}
//画直线
else if(ActionCommand.equals("直线")) {
g.drawLine(x1, y1, x2, y2);
s1=new Shape(x1, y1, x2, y2,"直线",color,stroke);
shapes.add(s1);
}
else if(ActionCommand.equals("椭圆")) {
g.drawOval(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x1-x2),Math.abs(y1-y2));
s1=new Shape(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x1-x2),Math.abs(y1-y2),"椭圆",color,stroke);
System.out.println(s1);
shapes.add(s1);
flag=0;
}
else if(ActionCommand.equals("矩形")) {
g.drawRect(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x1-x2),Math.abs(y1-y2));
s1=new Shape(Math.min(x1,x2),Math.min(y1,y2),Math.abs(x1-x2),Math.abs(y1-y2),"矩形",color,stroke);
shapes.add(s1);
flag=0;
}
}
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public void mouseDragged(MouseEvent e){
// ployX1 = x2;
// ployY1 = y2;
int x3 = e.getX();
int y3 = e.getY();
if( ActionCommand.equals("曲线")){
g.setColor(color);
// System.out.println("ActionCommand = "+ActionCommand);
//添加若干条直线
x4=x3;
y4=y3;
g.drawLine(x3,y3,x4,y4);
s1=new Shape(x3, y3, x4, y4,"直线",color,stroke);
shapes.add(s1);
}
else if( ActionCommand.equals("橡皮擦")){
//区域添加背景色的图形
g.setColor(jp2.getBackground());
x4=x3;
y4=y3;
g.drawLine(x3,y3,x4,y4);
s1=new Shape(x3, y3, x4, y4,"直线",jp2.getBackground(),stroke);
shapes.add(s1);
}
//实现实时生成直线的效果
else if((flag==0)&&(!ActionCommand.equals("椭圆")&&!ActionCommand.equals("矩形")&&!ActionCommand.equals("等腰三角形"))){//隐性条件:肯定不是曲线
jp2.startX = x1;
jp2.startY = y1;
jp2.currentX = e.getX();
jp2.currentY = e.getY();
jp2.color=color;
jp2.stroke=stroke;
jp2.repaint();
jp2.name="直线";
x2=e.getX();
y2=e.getY();
//System.out.println("进入直线了");
}
else if(flag==1){
jp2.startX =x2;
jp2.startY = y2;
jp2.currentX = e.getX();
jp2.currentY = e.getY();
jp2.color=color;
jp2.stroke=stroke;
jp2.repaint();
//System.out.println(ployX1+"和"+ployY1);
}
else if(ActionCommand.equals("矩形")||ActionCommand.equals("椭圆")){
g.setColor(color);
jp2.startX = x1;
jp2.startY = y1;
jp2.currentX = e.getX();
jp2.currentY = e.getY();
jp2.color=color;
jp2.stroke=stroke;
if(ActionCommand.equals("矩形")){
jp2.name = "矩形";
}
else if(ActionCommand.equals("椭圆")){
jp2.name = "椭圆";
}
jp2.repaint();
//System.out.println(jp2.color);
}
}
public void mouseMoved(MouseEvent e){
}
}
MyJpanel类
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class MyJPanel extends JPanel {
private ArrayList<Shape> shapes= new ArrayList<Shape>();
public int startX;
public int startY, currentX, currentY;
public String name="";
public Color color=Color.BLUE;
public Stroke stroke;
public void setshapes(ArrayList<Shape> shapes){
this.shapes=shapes;
}
public void paintComponent(Graphics g){
super.paintComponent(g);//保留绘制组件的功能
Graphics2D g1 = (Graphics2D) g;
for(Shape shape :shapes ){
shape.drawshape(g);//shape是shapes ArrayList中的每一个元素对象
}
if(name != null &&name.equals("直线")){
g.setColor(color);
g1.setStroke(stroke);
g.drawLine(startX, startY, currentX, currentY);
}
else if(name != null &&name.equals("矩形")){
g.setColor(color);
Graphics2D g2=(Graphics2D)g;
g2.setStroke(stroke);
g.drawRect(Math.min(startX,currentX),Math.min(startY,currentY),Math.abs(startX-currentX),Math.abs(startY-currentY));
}
else if(name != null &&name.equals("椭圆")){
g.setColor(color);
Graphics2D g2=(Graphics2D)g;
g2.setStroke(stroke);
g.drawOval(Math.min(startX,currentX),Math.min(startY,currentY),Math.abs(startX-currentX),Math.abs(startY-currentY));
}
}
}
Shape类
import java.awt.*;
public class Shape {
private int x1;
private int x2;
private int y1;
private int y2;
private String name;
private Color color;
private Stroke stroke;
public Shape(int x1,int y1,int x2,int y2,String name,Color color,Stroke stroke){
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.name = name;
this.color = color;
this.stroke = stroke;
}
public void drawshape(Graphics g){
if(this.name.equals("椭圆")){//椭圆
g.setColor(this.color);
Graphics2D g2=(Graphics2D)g;
g2.setStroke(this.stroke);
g.drawOval(x1,y1,x2,y2);
}
else if (this.name.equals("矩形")) {//矩形
//设置矩形和椭圆的粗细和颜色
g.setColor(this.color);
Graphics2D g2=(Graphics2D)g;
g2.setStroke(this.stroke);
//左上角坐标 长宽
g.drawRect(x1,y1,x2,y2);
}
else{//线(其他多边形 曲线都是由若干条线组成的)
g.setColor(this.color);
Graphics2D g2=(Graphics2D)g;
g2.setStroke(this.stroke);
g.drawLine(x1,y1,x2,y2); //连接两端点
}
}
}
代码运行结果
只要把代码中图片的绝对存储路径改成自己文件夹内的就可以运行该代码了。