第一篇介绍了如何设置一个简单的登录界面,今天就来讲讲界面JFrame的其他功能:绘图,但作为向递归分形的过渡内容,我们今天不需要画出多复杂多精美的图案,只需要在界面上能够画出一条简单的直线即可。
需要解决的问题:如何实现画一条直线?
可以解决问题的资源:有关Java的一些基础知识和简单的界面基础
首先是画一条直线需要具备的材料
画板(已知即为JFrame)
画笔(而如何在界面上编写出一支听我们指挥的画笔就是这次我们需要解决的主要问题之一)
其次直线是如何被画出来的
画直线的方法:两点确定一条直线(来源于数学思维,但也是Java画直线的本质)
那么两点在界面上是如何产生的呢
不妨假设:我们在界面上不重合的位置任意点击两次,就会产生一条直线(在后面可以得到验证)
最后如何提高我们所画直线的精度、准度和直观度
由于是我们任意点击的,随意性较高,并不具备严谨性。但由于机器语言需要我们遵守规则和底层逻辑的,所以如何让我们随机获取的点坐标和画出的直线在我们的掌控之中呢?
怎么才能让界面获取我们每次的动作和动作的具体信息?
答案是:利用监听器来抓捕。
定义:监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
监听器涉及的三个组件:事件源、事件对象和事件监听器。
事件源发出事件时,会调用事件监听器的方法,并将事件对象传递给该方法。通过事件对象,我们可以获取到事件源,并对其进行操作。
例:当一个组件(比如JButton)发生某个动作时,我们可以通过监听器(MouseListener)来捕捉到这个动作(比如点击),并对其进行相应的处理(如跳出弹窗)。
简单来说,监听器就像是我们的耳朵,可以听到某个对象发生的事情,并在这个对象发生事件时做出相应的反应。
所以我们需要在监听器的基础上去获取两次点击分别的坐标(我们的动作)然后在通过特定的方法画出此条直线,将抽象的画图具体化。
画笔与画笔监听器:
(1)什么是GPU:Graphics Processing Unit 图形处理器(注意与CPU区分开来)
(2)如何创建画笔监听器,格式如下可供参考
DrawListenr dl = new DrawLIstener();
jf.addMouseListenr(dl);
(3)画笔
Graphics g = jf.getGraphics();
获取坐标的java语言:
int x = e.getX();
int y = e.getY();
System.out.println("x:"+x+"y:"+y);
画直线的java语言:
g.drawLine(x1,y1,x2,y2);//按下松开的两组坐标
*请详细阅读我在代码中标注的注释,有利于更深入的理解
public class DrawLineUI {
public void showUI(){
JFrame jf = new JFrame();
jf.setSize(1200,300);
jf.setVisible(true);
jf.setTitle("绘图大师的封神之路:起点");
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//创建一个基础窗体
FlowLayout fl=new FlowLayout();
jf.setLayout(fl);//布局
DrawListener dl = new DrawListener(); //创建画笔监听器并加载到鼠标监听器上
jf.addMouseListener(dl);
Graphics g = jf.getGraphics();//设置画笔,获取一个已经存在的用于绘图的Graphics对象
//drawlistener.g=g;
}
public static void main (String[] args){
DrawLineUI drawUI = new DrawLineUI();
drawUI.showUI();
}
}
public class DrawListener implements MouseListener{
//定义一个名为DrawListener的类,它实现了MouseListener接口
//该类可以监听并响应与鼠标相关的事件
//长按CTRL点击MouseListener会出现鼠标五种监听方法:
//按下 释放 点击 进入 离开,将它们复制粘贴过来后把分号改为{}
//以下是一些全局变量
Graphics g;
int x1,y1,x2,y2;//全局坐标
@Override
//DrawListener类实现了MouseListener接口,但需重写接口中的方法
public void mousePressed(MouseEvent e){
System.out.println("按下");
//获取按下时的x,y坐标
int x = e.getX();
int y = e.getY();
System.out.println("x1:"+x + "y1:"+y);
x1=x;
y1=y;//赋值给全局坐标
}
public void mouseReleased(MouseEvent e){
System.out.println("释放");
//获取释放时的x,y坐标,按下与释放两点构成一条直线
int x = e.getX();
int y = e.getY();
System.out.println("x2:"+x + "y2:"+y);
x2=x;
y2=y;//赋值给全局坐标
g.drawLine(x1,y1,x2,y2);
//使用g.drawLine方法来绘制一条直线。
//该方法需要四个参数:起始点的x,y坐标、终点的x,y坐标。
}
public void mouseClicked(MouseEvent e){
System.out.println("点击");
}
public void mouseEntered(MouseEvent e){
System.out.println("进入");
}
public void mouseExited(MouseEvent e){
System.out.println("离开");
}
}
不知道大家在浏览上述代码后有没有注意到一个细节,在创建画笔的时候为什么是这样的写法,其实相对比JFrame上其他零部件的设置,多多少少看起来有点独特(在我眼里就是看着不顺眼)。
是(标准正确写法):Graphics g = jf.getGraphics();
而不是(大概类似这种格式):Graphics g = new Graphics; jf.add(g);
但可能很多人就会觉得这就是java的规则,会说我就是在这里硬杠没必要,记住就好。但我觉得每一个能让自己卡顿或者心生疑虑的地方都值得我们的思考,可以不去深究,但至少要有一刻的停留,不断地向自己去提问每一步的意义。最后给出的解释如下:
"Graphics g = jf.getGraphics();" 是获取一个已经存在的用于绘图的 Graphics 对象。
在 Java 中,Graphics 对象是由系统负责创建和管理的,并且只能通过某些特定的方法来获取。而不能像其他普通对象一样通过 "new" 关键字来创建。
通过调用 "jf.getGraphics()" 方法,我们可以从 JFrame(或其他组件对象)中获取 Graphics 对象的引用。这个 Graphics 对象是与组件关联的,在绘图时会将绘图操作直接显示在该组件上。
"jf.add(g)" 是添加一个组件,但我们并不是将 Graphics 对象直接添加为一个组件。Graphics 对象只是用于进行图形绘制的工具,并不能像其他组件一样直接添加到窗口上。
所以说:画笔并不能通过直接创建对象得到,而是必须从可视化组件上(如JFrame、JButton等等)获取(在窗体可视化setVisible之后获取)。
画笔还需要从JFrame的代码部分传递到Listener的部分的一个过程,这很简单,只需要一行代码就可以搞定。
drawlistener.g=g;
这个代码也是看似简单,其实也是大有不同,一行代码打通了两个世界,它的“传递性”是怎么体现的呢?这里甚至可以考一道语文题:请解释这两个g分别的含义。
该语句其实就是将一个名为g的Graphics对象赋值给drawlistener对象(dl)的成员变量g(意味着在监听器类中定义了一个Graphics类型的成员变量g)。这样,drawlistener对象就可以使用这个g变量来执行绘图操作。
Graphics g这个全局变量在这里就不多赘述了,那么就还剩intx1,y1,x2,y2这四个显眼包了,而需要它们的原因主要是方便了g.drawline()这个方法了,这其中也体现了传递性。按下的坐标和松开的坐标通过重新赋值传递给全局,通过方法从而可以画出直线。
Java的“传递性”是个很奇妙的东西,它可以打通代码也可以赋予一个方法生命和捷径,在未来的其他博客里,我还会继续探索发现java还有哪些其他的传递过程。