java_关于Graphics类
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java_关于Graphics类相关的知识,希望对你有一定的参考价值。
component类及其子类在新生成对象的时候会自动调用paint(Graphics g)方法
那么这个自动调用的代码在什么地方?
自动调用的paint(Graphics g)的对象g是怎么产生的?
专家copy过来的文章我google过了 不是我要的问题;
vanillakids 的回答是最接近我的问题的
不过还是想问 我下到了JDK6的source去看
但是没有在component及子类的构造函数中发现任何调用paint(g)的迹象 也就是这个自动调用的真实代码在什么地方
还有 这个被传入的g 也不知道是在哪被赋值=getGr...的
至于getGr...就是JVM的行为 但是是在哪被赋值的呢
当对于桌面执行了某类操作,改变了桌面上的图象时,jvm收到界面被调整的信息,此时会调用visible是ture的图形组件的repaint()方法对界面重绘。
当然,直接重绘或全部重绘是很亏的,一个是用双缓存技术,另一个是只对桌面上显示的部分重绘。双缓存是指在缓存内先模拟重绘过程,只把最后成形的结果传给显示器显示。部分重绘就是指使用repaint(Dimension area)及类似的方法,只重绘与需调整的部分相关的界面。幸运的是,目前jdk提供的repaint()并不是傻呼呼的就直接重绘所有组件,已经用了上述两种方法优化过了。所以即使反复调用repaint()也不会有效率的损失。
repaint()是重要概念,它是在图形线程后追加一段重绘操作,是安全的!是系统真正调用的重绘!所以如果你需要某个部件刷新一下界面,记得调用repaint(),千万不要直接调用paint()!
paint()是提供给用户编程的,往往声明在接口之中,然后用户实现该接口,以拥有重绘的功能。若要定制某个图形组件的界面,可以重写paint()方法,记得一般习惯这样改写:
void paint(Graphics g)
super.paint(g);
// your code
除了paint(),有的组件会有paintBorder()之类的专门用于某个部分重绘的方法,不过一般是 protected 的,在对该类组件扩展的时候可以重写该方法。
最后,graphics是一个抽象类,其实现大都是平台相关的,所以不容易自己创建一个graphics实例。一般graphics的实例会由依照你所在的桌面环境给出。Graphics类及其子类Graphics2D提供的只是一些基本绘图方法,比如画直线、曲线什么的。
所以做一个图形组件的基本思路可以总结为以下过程:
选择适合的基本图形组件 -> 继承它 -> 重写paint等方法 -> 在需要刷新图形的时候调用repaint等方法!
至于Graphics,先假设它存在,因为真正的Graphics实例只有当程序在jvm上跑的时候才会创建。
---------------------------------------------------------
关于补充问题的补充答案:
如果要找最终调用paint的地方的话,一定是通过repaint()方法。因为直接调用paint(),次数少还行,次数多就会导致该图形进程卡在与显示器的交互上,所以一定只有repaint才是可以被调用来重绘的。然而repiant存在的意义并不是绘界面,而是使绘界面操作更安全。
可以看Component的repaint方法的实现,他将触发重绘的源放在AWTEventQueue里,然后等到图形处理的Queue有空的时候,自动拿出源,此时调用paint()。
而paint的参数Graphics也只有此时能传给paint!因为Graphics与系统相关性太大,只能是通过JNI用底层代码实现,也就是C++代码创建。所以你要找到Graphics创建不能从jdk代码里找,要去找jvm的源代码(jdk6开源,你可以找找试试),可能会找到一些带有 Impl后缀的关于Graphics的实现。
然后,如果深究 paint() 在哪调用, 我可以说,你所找到的jdk里所有带有paint()的代码段都不是最终paint被调用的地方。因为jdk只允许paint被repaint触发,而repaint到传递Event给AWTEventQueue后,所有的代码都变成系统相关,那么就不能从jdk里找到实现了。
系统相关的处理,一般是在jdk只声明接口,然后利用反射机制动态创建。一般类名放在系统的环境变量中,然后反射该类,类的实现不同jvm不同。
另外,你用jdk 5这些新版本的话,可以发觉jdk的swing机制给每个组件加了UI管理器,这样,实质在负责Component的界面的是他的updateUI()方法,使用该方法一定要先setUI()一下。
UI机制有UIManager管理,UIManager你也找不到paint的源头。其本质也是将paint事件放入图形进程的队列。所以最后实现也是平台相关的。 参考技术A 谈谈java.awt.Graphics类
图形环境的概念同在 GUI 平台上开发应用程序紧密相关。虽然通常将窗口和组件本身作为对象来表达,但仍然需要另一个接口来执行实际的绘制、着色以及文本输出操作。Java 语言中提供这些功能的基类称作 java.awt.Graphics。从 java.awt.Component 类(所有窗口对象的基类)继承的类提供了一个名为 paint() 的方法,在需要重新绘制组件时,调用该方法。
paint() 方法只有一个参数,该参数是 Graphics 类的实例。
Graphics 类支持几种确定图形环境状态的特性。以下列出了部分特性:
1.Color:当前绘制颜色,它属于 java.awt.Color 类型。所有的绘制、着色和纯文本输出都将以指定的颜色显示。
2.Font:当前字体,它属于 java.awt.Font 类型。它是将用于所有纯文本输出的字体。
3.Clip:java.awt.Shape 类型的对象,它充当用来定义几何形状的接口。该特性包含的形状定义了图形环境的区域,绘制将作用于该区域。通常情况下,这一形状与整个图形环境相同,但也并不一定如此。
4.ClipBounds:java.awt.Rectangle 对象,它表示将包围由 Clip 特性定义的 Shape 的最小矩形。它是只读特性。
5.FontMetrics:java.awt.FontMetrics 类型的只读特性。该对象含有关于图形环境中当前起作用的 Font 的信息。
6.Paint Mode:该特性控制环境使用当前颜色的方式。如果调用了 setPaintMode() 方法,那么所有绘制操作都将使用当前颜色。如果调用了 setXORMode() 方法(该方法获取一个 Color 类型的参数),那么就用指定的颜色对像素做“XOR”操作。XOR 具有在重新绘制时恢复初始位模式的特性,因此它被用作橡皮擦除和动画操作。
Graphics 类方法:
可以将 java.awt.Graphics 支持的非特性方法划分为三个常规类别之下:
1.跟踪形状轮廓的绘制方法:
draw3DRect() drawArc() drawBytes() drawChars()
drawImage() drawLine() drawOval() drawPolygon()
drawPolyline() drawRect() drawRoundRect() drawString()
2.填充形状轮廓的绘制方法:
fill3DRect() fillArc() fillOval()
fillPolygon() fillRect() fillRoundRect()
3.诸如 translate() 之类的杂项方法,它们将图形环境的起点从其缺省值 (0,0) 变成其它值。
请注意,没有对任意形状进行操作的操作。直到 Java 2D 出现以前,图形操作一直都是很有局限性的。还需注意的是,对于渲染具有属性的文本也没有直接支持;显示格式化文本是一项费事的任务,需要手工完成。
下面我们来看一些具体的实例(注:所有的实例都没有加上关闭窗口的事件驱动,只能用Ctrl+C来中断程序):
1.画线程序:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class Main extends Frame
Vector points = new Vector();
int lastDrawnPoint = 0;
Main()
super("drawLine Example");
setSize(200, 200);
addMouseListener(new MouseEventHandler());
addMouseMotionListener(new MouseMotionEventHandler());
show();
public void paint(Graphics g)
Point curPt = null;
for (int i=0; i Point pt = (Point)points.elementAt(i);
if (curPt != null)
g.drawLine(curPt.x, curPt.y, pt.x, pt.y);
curPt = pt;
lastDrawnPoint = points.size();
public void update(Graphics g)
Point curPt = null;
lastDrawnPoint = Math.max(0, lastDrawnPoint-1);
for (int i=lastDrawnPoint; i Point pt = (Point)points.elementAt(i);
if (curPt != null)
g.drawLine(curPt.x, curPt.y, pt.x, pt.y);
curPt = pt;
lastDrawnPoint = points.size();
class MouseEventHandler extends MouseAdapter
public void mousePressed(MouseEvent evt)
points.addElement(evt.getPoint());
repaint();
class MouseMotionEventHandler extends MouseMotionAdapter
public void mouseDragged(MouseEvent evt)
points.addElement(evt.getPoint());
repaint();
static public void main(String[] args)
new Main();
Graphics.drawLine(int x,int y,int x2,int y2);是从一个点的坐标到另一个点的坐标。
这个实例的运行结果也就是可以任意的画线。可以任意的拖动鼠标,我们知道,线都是由点构成的。我们的主程序是继承java.awt.Frame这个类,重载了paint()和update()这两个方法。而在主类添加了两个事件new MouseEventHandler()和MouseMotionEventHandler()。当我们运行程序结果时就能知道,paint()只是最初的调用,以后都是调用update(),而在MouseEventHandler()和MouseMotionEventHandler()中调用repaint()也是sun的程序员在内部已经嵌入的,所以这个不是我们需要担心的,程序的原理我想大家都明白的,用一个向量来保存鼠标的点,lastDrawnPoint是一个重要的变量,然后就不说了。
2.调用图片程序:
import java.awt.*;
class Main extends Frame
Image image;
Main(String filename)
super("drawImage Example");
try
image = getToolkit().getImage(filename);
setIconImage(image);
catch (Exception e)
e.printStackTrace();
setSize(400, 200);
show();
public void paint(Graphics g)
Insets insets = getInsets();
int x = insets.left, y = insets.top;
int w = image.getWidth(this);
int h = image.getHeight(this);
// original
g.drawImage(image, x, y, this);
// shrinken
g.drawRect(x, y, w/4+1, h/4+1);
g.drawImage(image, x+1, y+1, w/4, h/4, this);
// horizontally flipped
g.drawImage(image, x+w, y, x+2*w, y+h, w, 0, 0, h, this);
// vertically flipped
g.drawImage(image, x+2*w, y, x+3*w, y+h, 0, h, w, 0, this);
// enlarged; use -1 to indicate proportional height
g.drawImage(image, x+3*w, y, 2*w, -1, this);
static public void main(String[] args)
if (args.length == 1)
new Main(args[0]);
else
System.err.println("usage: java Main ");
相比较上面的程序,这个程式就简单的多,它就是调用你本地的图片,进行多角度显示。java.awt.Frame.getToolkit().getImage(filename)和Graphics.drawImage()是重要的方法。
3。画圆程式:
import java.awt.*;
import java.util.*;
import java.io.*;
class Main extends Frame
Vector points = new Vector();
Main()
super("drawOval Example");
setSize(200, 200);
show();
void addPoint(Point p)
points.addElement(p);
repaint();
public void paint(Graphics g)
Insets insets = getInsets();
int x = insets.left, y = insets.top;
for (int i=0; i Point p = (Point)points.elementAt(i);
g.drawOval(x+p.x-5, y+p.y-5, 10, 10);
static public void main(String[] args)
Main m = new Main();
BufferedReader dis =
new BufferedReader(new InputStreamReader(System.in));
while (true)
try
m.addPoint(new Point(
Integer.parseInt(dis.readLine()),
Integer.parseInt(dis.readLine())));
catch (Exception e)
e.printStackTrace();
System.exit(1);
这个程式也比较简单,不过他调用了个System.in来限制画圆的坐标。
4.画三角形:
import java.awt.*;
import java.awt.event.*;
class Main extends Frame
Polygon polygon = new Polygon();
Main()
super("drawPolygon Example");
addMouseListener(new MouseEventHandler());
addMouseMotionListener(new MouseMotionEventHandler());
setSize(200, 200);
show();
public void paint(Graphics g)
System.out.println("paint:");
g.drawPolygon(polygon);
// The default update method clears the screen which causes
// flicker. This override avoids this.
public void update(Graphics g)
System.out.println("update:");
paint(g);
class MouseEventHandler extends MouseAdapter
public void mousePressed(MouseEvent evt)
System.out.println("mousePressed:");
polygon.addPoint(evt.getX(), evt.getY());
repaint();
class MouseMotionEventHandler extends MouseMotionAdapter
public void mouseDragged(MouseEvent evt)
System.out.println("mouseDragged:");
polygon.addPoint(evt.getX(), evt.getY());
repaint();
static public void main(String[] args)
new Main();
画三角形是一个很有意思的东东。它注意是确定三个点了。第一个是最重要的。以后所有的三角形也这个为中心,如果你拖动鼠标的话,就会出现N多。
5.写字:
import java.awt.*;
import java.awt.event.*;
class Main extends Frame implements ItemListener
MainCanvas cv = new MainCanvas();
Choice choice = new Choice();
Main()
super("drawString Example");
for (int i=4; i<60; i += 4)
choice.addItem(""+i);
choice.select(0);
choice.addItemListener(this);
cv.setFontSize(4);
cv.setSize(300, 100);
add(cv, BorderLayout.CENTER);
add(choice, BorderLayout.SOUTH);
pack();
show();
public void itemStateChanged(ItemEvent evt)
String what = (String)(evt.getItem());
cv.setFontSize(Integer.parseInt(what));
static public void main(String[] args)
new Main();
class MainCanvas extends Canvas
void setFontSize(int size)
Font f = getFont();
if (f == null)
f = new Font("Serif", Font.PLAIN, size);
else
f = new Font(getFont().getName(), getFont().getStyle(), size);
setFont(f);
repaint();
public void paint(Graphics g)
String s = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
FontMetrics fontM = g.getFontMetrics();
g.setColor(Color.white);
g.fillRect(0, 0, fontM.stringWidth(s), fontM.getHeight());
g.setColor(Color.black);
g.drawString(s, 0, fontM.getAscent());
参考资料:http://www.zwwr.com/article/show.asp?id=15545
参考技术B 大家上面写的好象都是在介绍Graphics,嘿嘿嘿,有意思所谓的自动调用,代码应该在component类的构造函数中
g=this.getGraphics ()
paint(g);
不过这些代码应该都是被封装好的,用户不可见,就是d2tools.jar
(名字记不清了,好象是这个)
子类生成时,必然要先调用父类的构造函数.从而给人感觉是自动调用,其实就是java本身的类执行了一次paint()函数.
其实这是JAVA类编写最常用的一种机制
应该就是这样,以前特地研究过这个问题,不过可能时间久远了,描述可能有不恰当之处,见量!
至于getGraphics() 怎样形成图形编写环境的,这是个大问题了.因为java 代码我也没全看过,好象是通过一个图形生成类生成的,具体实现代码不详,不好意思
那你去找一下component类继承的类和所实现的接口类(ImagerObserver什么的,好象还有几个,好常时间没变过桌应了,记不太清楚了,自己找一下),必然是在代码中,类似的代码肯定有(只是时间久远了,实在记不清在哪儿了).有一点是肯定的JDK不是servlet,没有定义在xml所谓的自动调用什么的机制,所有的jdk中的实现必然是根据java现有的面向对象语法规则进行实现的,只要你根据现有的语法规则(父类与子类的调用次序等)去查找必然能找到答案.另外注意一切有可能的代码.
你既然有代码,只要在java编程环境中(eclipse ,jb)按住ctrl 点鼠标就可以找到相应的类代码(这个应该知道的哦,可能我多说了). 参考技术C 这个是在JVM里面,你只需要按你的需要重写Paint方法就可以了。当你new的时候,新产生的这个窗口(如果需要重写Paint方法,一般含Image等)就自动作为参数传给(Graphics g)。其实你只要会使用就行了。 参考技术D 真厉害啊
java生成图片验证码实现
两种图片样式分别是用Graphics类和Graphics2D
类实现(Graphics2D扩展了Graphics类),可以参考资料,画出自己想要的图片
Graphics2D 参考api地址:http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html
Graphics 参考api地址:http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html
代码实现一 :
java部分:
import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ImageCreate2 extends HttpServlet { private static final long serialVersionUID = 1L; private int imageWidth = 70; private int imageHeight = 20; private int codeNumber = 4; private int backGround = 30; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { createCode(request, response); } /** * @param request * @param response * @throws IOException */ private void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("image/jpeg"); BufferedImage bufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_BGR); Graphics2D g = bufferedImage.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, imageWidth - 1, imageHeight - 1); //g.setStroke(new BasicStroke(3.0f));//干扰线 Random random = new Random(); for (int i = 0; i < backGround; i++) { int red = random.nextInt(256); int green = random.nextInt(256); int blue = random.nextInt(256); g.setColor(new Color(red, green, blue)); int start_X = random.nextInt(imageWidth); int start_Y = random.nextInt(imageHeight); if (random.nextInt(5) % 3 == 0) { int r = random.nextInt(10); int startAngle = random.nextInt(360); int arcAngle = random.nextInt(360); g.drawArc(start_X, start_Y, r, r, startAngle, arcAngle); } else { int change_X = random.nextInt() % 6; int change_Y = random.nextInt() % 6; g.drawLine(start_X, start_Y, start_X + change_X, start_Y + change_Y); } } StringBuilder ValidateCode = new StringBuilder(); g.setFont(new Font("Blackoak Std", Font.BOLD, imageHeight-2)); g.setFont(new Font("Tahoma", Font.BOLD, imageHeight - 3)); for (int i = 0; i < codeNumber; i++) { int red = random.nextInt(100) + 50; int green = random.nextInt(100) + 50; int blue = random.nextInt(100) + 50; g.setColor(new Color(red, green, blue)); String code = null; int type = random.nextInt(3); if (type == 1) { code = String.valueOf((char) (random.nextInt(26) + 65)); } else if (type == 2) { code = String.valueOf((char) (random.nextInt(26) + 97)); } else { code = String.valueOf(random.nextInt(10)); } ValidateCode.append(code); g.drawString(code, imageWidth / codeNumber * i + 4, 16); } System.out.println("vcode="+ValidateCode.toString());
//存于session中,以待验证 request.getSession().setAttribute("validateCode", ValidateCode.toString()); ServletOutputStream sos = response.getOutputStream(); ImageIO.write(bufferedImage, "jpeg", sos); bufferedImage = null; sos.close(); random = null; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response);
jsp页面部分:
<div class="form-group"> <div class="col-sm-10" style="width:40%;"> <input style="height: 40px" type="text" class="form-control" id="tpyzm" flag="0" placeholder="请输入验证码"> </div> <div class="col-sm-2 control-label" style="padding:7px; text-align: center;width: 80px;background: #fefefe;border:solid 1px #c9c9c9 "> <img src="<%=request.getContextPath()%>/ImageCreate2.do" id="codes2" onclick="reloadImg2(‘<%=request.getContextPath()%>‘)" class="vcodeCss"></img> </div> <div style="height: 40px;padding-top: 10px"> <span style="color: #333333;font-size: 16px;margin-left: 10px"> 看不清?</span> <a style="font-size: 16px;color: #0089d2" onclick="reloadImg2(‘<%=request.getContextPath()%>‘)">换一张</a> </div> <div class="regist_til col-sm-2"> <span class="" aria-hidden="true" id="tpyzmSpan" ></span> </div> </div>
<script>
//图片验证码
function reloadImg2(url) {
document.getElementById("codes2").src = url+"/ImageCreate2.do?" + "radom="
+ Math.random();
$("#vcode2").focus();
}
</script>
代码实现二 :
java部分:
import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ImageCreate extends HttpServlet{ private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { create(request, response); } private void create(HttpServletRequest request, HttpServletResponse response) throws IOException { int []a = null; // 大小 int width = 60; int height = 30; // 声明一个图片类型rgb BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); // 获取画笔 Graphics g = bi.getGraphics(); // 背景色 g.setColor(Color.WHITE); // 画 g.fillRect(0, 0, width, height); // 字体 g.setFont(new Font("黑体", Font.BOLD, 18)); // 写一个字符到bi Random r = new Random(); for (int i = 0; i < 4; i++) { // 生成随机数 int code = r.nextInt(10);// 0---9之间 // 画笔随机色 g.setColor(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256))); // 写出字符 g.drawString("" + code, i * 15, 10 + r.nextInt(20)); } //干扰线 for(int i=0;i<12;i++){ g.setColor(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256))); //画线 g.drawLine(r.nextInt(60), r.nextInt(30), r.nextInt(60), r.nextInt(30)); } g.dispose();//图片生成 //ImageIO.write(bi,"JPEG", new FileOutputStream("e:/a.jpg"));//设置路径为e:盘下的a.jpg ServletOutputStream sos = response.getOutputStream(); ImageIO.write(bi, "jpeg", sos); sos.close(); bi = null; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
jsp页面部分:同上,只需改下url则可;
以上是关于java_关于Graphics类的主要内容,如果未能解决你的问题,请参考以下文章