<JAVA图像学习笔记>关于Graphics/Graphics2D以及简单的几何图像制作(一个简单钟表的实现)
题外话:正好赶上OperatingSystem的作业要做一个模拟线程/进程调度的问题,决定用JAVA实现才发现这些内容和之前学过的GUI制作是两码事儿- -b
通过学习java.swing库的ActionListener接口我们初步了解了一些关于java框体程序的各个部件JFrame,JPanel,JComponent和控件之间是如何联系通讯的,然而这次我们要从另一个视角来看java程序框体。
从一个框体实现“表”的代码我们来初步看一下java是如何在JFrame平台上制作出自己想要的图案的吧!
(实现效果图)

有几个可能会比较陌生的类先筛选出来:
(1)GeneralPath:是java.awt.geom.Path2D的一个子类,path的设定过程主要用两个函数moveTo(double x,double y)和linkTo(double x,double y),当然如果需要实现path的动态绘制的话需要的方法远不止这两个,可以自行去javadoc查阅。
(2)Ellipse2D:是java.awt.geom下的一个类,可以用来绘制理论上的椭圆构造参数为(double x,double y,double width,double height)意义不多赘述。
(3)AffineTransform:这个是整个代码中最有分量的一个类,这个类的引用的获取方法是用静态方法AffineTransform.getInstance(double theta,double x,double y)这里的x和y指的是锚点也就是整个旋转过程参照的旋转中心,theta是要用弧度制表示的旋转角度。具体使用方法下文中会详细总结。
(4)Timer类是在我接触到的新的控件,和JButton或者JLabel等等不同的是这个空间不要手动加上ActionListener的接口来进行事件驱动。使用方法在代码中有详述。
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.Calendar;
import javax.swing.*; public class Main extends JPanel implements ActionListener
{
// Create a shape for the face of the clock
protected static Ellipse2D face = new Ellipse2D.Float(3, 3, 94, 94); // Create a path that represents a tick mark
protected static GeneralPath tick = new GeneralPath();
static
{
tick.moveTo(100, 100);
tick.moveTo(49, 0);
tick.lineTo(51, 0);
tick.lineTo(51, 6);
tick.lineTo(49, 6);
tick.lineTo(49, 0); } // Create a cool hour hand
protected static GeneralPath hourHand = new GeneralPath();
static
{
hourHand.moveTo(50, 15);
hourHand.lineTo(53, 50);
hourHand.lineTo(50, 53);
hourHand.lineTo(47, 50);
hourHand.lineTo(50, 15);
} // Create a cool minute hand
protected static GeneralPath minuteHand = new GeneralPath();
static
{
minuteHand.moveTo(50, 2);
minuteHand.lineTo(53, 50);
minuteHand.lineTo(50, 58);
minuteHand.lineTo(47, 50);
minuteHand.lineTo(50, 2);
} // And a cool second hand
protected static GeneralPath secondHand = new GeneralPath();
static
{
secondHand.moveTo(49, 5);
secondHand.lineTo(51, 5);
secondHand.lineTo(51, 62);
secondHand.lineTo(49, 62);
secondHand.lineTo(49, 5);
} // Create some colors for the pieces of the clock
protected static Color faceColor = new Color(220, 220, 220);
protected static Color hourColor = Color.red.darker();
protected static Color minuteColor = Color.blue.darker();
protected static Color secondColor = new Color(180, 180, 0);
protected static Color pinColor = Color.gray.brighter(); // Create circles for the pivot and center pin
protected Ellipse2D pivot = new Ellipse2D.Float(47, 47, 6, 6);
protected Ellipse2D centerPin = new Ellipse2D.Float(49, 49, 2, 2); // Create three transforms that center around the pivot point
protected AffineTransform hourTransform =
AffineTransform.getRotateInstance(0, 50, 50);
protected AffineTransform minuteTransform =
AffineTransform.getRotateInstance(0, 50, 50);
protected AffineTransform secondTransform =
AffineTransform.getRotateInstance(0,50,50); // Create a timer that fires once a second and a Calendar
// instance for getting the time values
protected Timer timer = new Timer(1000, this);
protected Calendar calendar = Calendar.getInstance(); // Constructor - hardcode a preferred size of 100x100
public Main()
{
setPreferredSize(new Dimension(100, 100));
} // Invoked when panel is added to a container
public void addNotify()
{
// Call the superclass and start the timer
super.addNotify();
timer.start();
} // Invoked when panel is removed from a container
public void removeNotify()
{
// Call the superclass and stop the timer
timer.stop();
super.removeNotify();
} //
public void actionPerformed(ActionEvent event)
{
// Update the calendar's time
this.calendar.setTime(new java.util.Date()); // Extract the hours minutes and seconds
int hours = this.calendar.get(Calendar.HOUR);
int minutes = this.calendar.get(Calendar.MINUTE);
int seconds = this.calendar.get(Calendar.SECOND); // Using a little trigonometry, set the transforms to rotate
// each hand into the proper position. Center the rotation
// around the pivot point (50, 50) instead of the origin
hourTransform.setToRotation(((double) hours) *
(Math.PI / 6.0), 50, 50);
minuteTransform.setToRotation(((double) minutes) *
(Math.PI / 30.0), 50, 50);
secondTransform.setToRotation(((double) seconds) *
(Math.PI / 30.0), 50, 50); // Force the component to repaint ASAP
repaint();
} // This is an alternative to creating a UI delegate. Since JPanel's
// paint() method only paints the border and backgound, we can just
// override the paint method of the component to do the graphics.
public void paint(Graphics g)
{
// Call the superclass first to paint the border (if one is assigned)
super.paint(g); // Get the graphics context and turn on anti-aliasing
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON); // Set the paint for the clock face and fill it in
g2.setPaint(faceColor);
g2.fill(face); // Set the paint to black and draw the clock's outline
g2.setPaint(Color.black);
g2.draw(face); // Fill in the 12 ticks around the face of the clock
for (double p = 0.0; p < 12.0; p += 1.0)
{
// This is probably terribly inefficient and should be
// done statically or in the constructor - draw the
// tick as a transformed shape that is rotated.
g2.fill(tick.createTransformedShape(
AffineTransform.getRotateInstance((Math.PI / 6.0) * p,
50, 50)));
} // Set the paint and draw the hour hand. It is lowest in the
// 'z-order' so will appear underneath the other hands. Notice
// how each hand is transformed by a different <AffineTransform>.
g2.setPaint(hourColor);
g2.fill(hourHand.createTransformedShape(hourTransform)); // Set the paint and draw the minute hand, the second hand,
// the pivot and the center pin
g2.setPaint(minuteColor);
g2.fill(minuteHand.createTransformedShape(minuteTransform));
g2.setPaint(secondColor);
g2.fill(secondHand.createTransformedShape(secondTransform));
g2.fill(pivot);
g2.setPaint(pinColor);
g2.fill(centerPin);
} // A little test frame to show off our fancy clock
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setLocation(700, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Main());
frame.pack();
frame.show();
}
}
Okay,Here are the points :-P
下面进阶深入探究一下这段代码:
#1#关于每次图形变换之后,变化前图形的擦除?
Ans:是这样的,一般来讲有两种办法实现所谓的擦除效果以完成动画:
1.手动擦除,直接对这个paint出来的geom下的类调用clear**方法,或者直接使用新的颜色/画布将需要擦除的形状删除/覆盖。
2.super.paint(g)这个方法有点神奇,以后需要进一步探讨一下,直接在子类的paint方法之中调用父类的paint的方法来实现“擦除”。
代码中方法其实是第一种,在每次repaint过程中,都会有一次将整个表盘“覆盖”重画的过程,但是不得不说,这么实现的话每秒钟都需要对整个表盘全部的空间进行重画,是很浪费时间/空间的。
#2#怎么递推实现的表盘圆周上的12个标记的绘制?
Ans:这里用了一个新的类叫做AffineTransform。使用这个类的方法是在geom下的几何类中直接调用createTransformedShape(AffineTransform at)返回一个Shape类型,这时匿名调用这个类进行draw(勾勒轮廓)/fill(填充颜色)方法可以完成对转换完成之后的几何体的绘制。关于AffineTransform类的更多用法和数学解释:
http://www.apihome.cn/api/java/AffineTransform.html
#3#怎么实现消除斜线的锯齿状?
Ans:这个我真的不懂,但是我知道是下面这段神奇的代码起到的作用,至于这个“渲染提示”具体的用法还得慢慢学..
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
#4#阿西巴,addNotify方法到底有没有用?
Ans:addNotify方法是在JPanel添加到JComponent上的时候用的,本例中是在添加pane的直接启动计时器(Timer),关于代码最后那个若有若无的getContentPane()为什么会出现,其实还要从JFrame的四层结构开始说起(传送门见下):
http://tieba.baidu.com/p/2004216123
<JAVA图像学习笔记>关于Graphics/Graphics2D以及简单的几何图像制作(一个简单钟表的实现)的更多相关文章
- <JAVA图像学习笔记>十字路口交通模拟--操作系统模拟课后小项目
项目的要求很简单: 模拟出十字路口的交通控制情况: 秒. 当东西(或南北)方向红灯时,所有车辆(除了消防车.救护车.警车)均排队等待,当东西(或南北)方向绿灯时,所有车辆按序行驶(不准超车). 制作这 ...
- Java IO学习笔记五:BIO到NIO
作者:Grey 原文地址: Java IO学习笔记五:BIO到NIO 准备环境 准备一个CentOS7的Linux实例: 实例的IP: 192.168.205.138 我们这次实验的目的就是直观感受一 ...
- Android(java)学习笔记206:利用开源SmartImageView优化网易新闻RSS客户端
1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...
- Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程
1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...
- Android(java)学习笔记204:自定义SmartImageView(继承自ImageView,扩展功能为自动获取网络路径图片)
1.有时候Android系统配置的UI控件,不能满足我们的需求,Android开发做到了一定程度,多少都会用到自定义控件,一方面是更加灵活,另一方面在大数据量的情况下自定义控件的效率比写布局文件更高. ...
- Java IO学习笔记二
Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...
- Android(java)学习笔记149:利用开源SmartImageView优化网易新闻RSS客户端
1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...
- Android(java)学习笔记148:网易新闻RSS客户端应用编写逻辑过程
1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...
- Android(java)学习笔记147:自定义SmartImageView(继承自ImageView,扩展功能为自动获取网络路径图片)
1. 有时候Android系统配置的UI控件,不能满足我们的需求,Android开发做到了一定程度,多少都会用到自定义控件,一方面是更加灵活,另一方面在大数据量的情况下自定义控件的效率比写布局文件更高 ...
随机推荐
- Zookeeper协调分布式节点demo
多台服务器和客户端通过第三方组件Zookeeper管理 public class DistributedServer { private static final String connectStri ...
- EasyUI+zTree实现简单的树形菜单切换
使用easyui_ztree实现简单的树形菜单切换效果 <!DOCTYPE html> <html> <head> <meta charset="U ...
- vue 父子通信过程
1.概述 每个 Vue 实例都实现了事件接口,即: 使用 $on(eventName) 监听事件 使用 $emit(eventName, optionalPayload) 触发事件 2.示例一(未传递 ...
- HDU5294——Tricks Device(最短路 + 最大流)
第一次做最大流的题目- 这题就是堆模板 #include <iostream> #include <algorithm> #include <cmath> #inc ...
- dynamic_cast<const ObjectList&>(msg);
说简单的就是C里面的强制类型转换,只不过C++里面为了类型安全而这么做的.主要用于 基类与继承类之间. C写多了,类型一般都强转,特别是指针.int * a;void * b = (void*)a;c ...
- [转载]UDP丢包率提升
UDP丢包及无序问题 转载自:http://hi.baidu.com/gamedot/item/96cb9bf1a717eb14d6ff8cd5 最近在做一个项目,在这之前,做了个验证程序. 发现客户 ...
- Gson把对象转成json格式的字符串
近期在做一个java web service项目,须要用到jason,本人对java不是特别精通,于是開始搜索一些java平台的json类库. 发现了google的gson.由于之前对于protoco ...
- Shell脚本笔记 1
函数别名 设置别名 alias name="command" alias ll="ls -laS" 取消别名 unalias name 求取数学表达式 valu ...
- CSS3 实现背景透明,文字不透明,兼容所有浏览器
<!DOCTYPE html><html><head><meta charset="utf-8"><title>opac ...
- GenericServlet 、Servlet和httpServler他们之间的关系
1.GenericServlet类是所有Servlet类的祖先类. 2.HttpServlet类继承了GenericServlet类. 3.Servlet有两个非常重要的的对象,可以说是java we ...