JAVA.AWT.CANVAS使用
2015-07-26 16:21
435 查看
该组件继承自java.awt.Component组件,在这点上与JPanel上是一致的.所以在JPanel拥有与Canvas一样的功能是很很正常的.
之前的一直无法理解为啥有了JPanel还要Canvas组件干啥,毕竟JPanel功能有Canvas的功能.现在的个人理解是JPanel更多的是一个容器的概念,更多的是充当容器的作用,其中有很多是在作图中是不需要的.
在网上看到一个关于Canvas与JPanel的区别评论如下:
Canvas: AWT JPanel: Swing
Swing is based on AWT, so Canvas can be more lightweight and in lower layer.
If canvas meet all your requirements, just use Canvas.
PS: I don't think there would be too much expensive to use JPanel, just choose the one you like.
Canvas是AWT组件,JPanel是Swing组件,Swing组件是以AWT组件为基础的,从理论上来说,Canvas要比JPanel更轻量些.如果canvas能满足需求,就用canvas.
但是作者认为两者并没有太大的性能差异,所以,想用哪个就用哪个,开心就好.
上述评论链接:
http://stackoverflow.com/questions/29476468/difference-between-canvas-and-jpanel
Canvas使用.
1.paint方法
一般来说,我们是不能直接使用该类的,需要继承Canvas并重写其paint方法.因为Canvas源码是这么写的:
可以看到,源码是直接做清屏操作,如果不重写,啥也干不了.
paint方法的具体使用,JDK 文档是这么写的:
调用此方法响应对 repaint 的调用。首先通过使用背景色填充 canvas 来清理它,然后通过调用此 canvas 的 paint 方法重绘它。注:重写此方法的应用程序应该调用 super.update(g),或者将上述功能合并到其自身的代码中。
2.update方法
与paint方法极其类似,源码如下:
所以本质上update方法也是啥也没干,冏
3.repaint方法
该方法继承自Component组件.
JDK文档是这么说该方法的:
重绘此组件。
如果此组件是轻量级组件,则此方法会尽快调用此组件的 paint 方法。否则此方法会尽快调用此组件的 update 方法。
至此update、paint、repaint三个方法之间的默认调用关系很清楚了:
repaint---》update---》paint
repaint方式去调用update方式是通过EventQueue调用,也就是说通过AWT-EventQueue去调用.即开启了线程去重绘canvas.
repaint作用源码如下:
在对一个不断变化的动画来说,就会容易出现线程同步问题.
示例:
在上述例子中,粒子是在不断的变化的,存储在List中,但是由于没有重写repaint方法,在重绘粒子的时候是使用的是单独的线程去绘制粒子,而此时createGrain()与moveGrain()已经更改了存储粒子的List的了,此时会抛出如下异常.
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
异常
如果想要简单的解决该问题,重写repaint方法即可:
此外,如果不重写repaint方法,在绘制大量图案时,还容易造成界面假死的情况.
造成假死的原因个人理解是由于awt-EventQueue是负责创建用户界面和绘制图形类方法的,此时在该线程中绘制图像,导致程序没有空闲的时间去处理其他的界面事件,因此造成了假死.
完整使用示例:
CanvasTest.java:
Grain.java
WorldCanvas.java
貌似排版有点渣.......
经过一番折腾,好多了,看样子是使用的姿势不对呃~
距离上次更新博客差不多是一年前的事情了,一年来,却没有什么大的长进,真是个悲伤的故事~
之前的一直无法理解为啥有了JPanel还要Canvas组件干啥,毕竟JPanel功能有Canvas的功能.现在的个人理解是JPanel更多的是一个容器的概念,更多的是充当容器的作用,其中有很多是在作图中是不需要的.
在网上看到一个关于Canvas与JPanel的区别评论如下:
Canvas: AWT JPanel: Swing
Swing is based on AWT, so Canvas can be more lightweight and in lower layer.
If canvas meet all your requirements, just use Canvas.
PS: I don't think there would be too much expensive to use JPanel, just choose the one you like.
Canvas是AWT组件,JPanel是Swing组件,Swing组件是以AWT组件为基础的,从理论上来说,Canvas要比JPanel更轻量些.如果canvas能满足需求,就用canvas.
但是作者认为两者并没有太大的性能差异,所以,想用哪个就用哪个,开心就好.
上述评论链接:
http://stackoverflow.com/questions/29476468/difference-between-canvas-and-jpanel
Canvas使用.
1.paint方法
一般来说,我们是不能直接使用该类的,需要继承Canvas并重写其paint方法.因为Canvas源码是这么写的:
public void paint(Graphics g) { g.clearRect(0, 0, width, height); }
可以看到,源码是直接做清屏操作,如果不重写,啥也干不了.
paint方法的具体使用,JDK 文档是这么写的:
调用此方法响应对 repaint 的调用。首先通过使用背景色填充 canvas 来清理它,然后通过调用此 canvas 的 paint 方法重绘它。注:重写此方法的应用程序应该调用 super.update(g),或者将上述功能合并到其自身的代码中。
2.update方法
与paint方法极其类似,源码如下:
public void update(Graphics g) { g.clearRect(0, 0, width, height); paint(g); }
所以本质上update方法也是啥也没干,冏
3.repaint方法
该方法继承自Component组件.
JDK文档是这么说该方法的:
重绘此组件。
如果此组件是轻量级组件,则此方法会尽快调用此组件的 paint 方法。否则此方法会尽快调用此组件的 update 方法。
至此update、paint、repaint三个方法之间的默认调用关系很清楚了:
repaint---》update---》paint
repaint方式去调用update方式是通过EventQueue调用,也就是说通过AWT-EventQueue去调用.即开启了线程去重绘canvas.
repaint作用源码如下:
if (isVisible() && (this.peer != null) &&(width > 0) && (height > 0)) { PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE, new Rectangle(x, y, width, height)); Toolkit.getEventQueue().postEvent(e); }
在对一个不断变化的动画来说,就会容易出现线程同步问题.
示例:
class Roll extends Thread { @Override public void run() { while (true) { //创建粒子 createGrain(); //移动粒子 moveGrain(); //重绘粒子 canvas.repaint(); } } }
在上述例子中,粒子是在不断的变化的,存储在List中,但是由于没有重写repaint方法,在重绘粒子的时候是使用的是单独的线程去绘制粒子,而此时createGrain()与moveGrain()已经更改了存储粒子的List的了,此时会抛出如下异常.
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
异常
如果想要简单的解决该问题,重写repaint方法即可:
public void repaint() { Graphics g = this.getGraphics(); update(g); }
此外,如果不重写repaint方法,在绘制大量图案时,还容易造成界面假死的情况.
造成假死的原因个人理解是由于awt-EventQueue是负责创建用户界面和绘制图形类方法的,此时在该线程中绘制图像,导致程序没有空闲的时间去处理其他的界面事件,因此造成了假死.
完整使用示例:
CanvasTest.java:
package test; import java.awt.Graphics; public class CanvasTest { final int CREATE_GRAIN_NUMBER = 5000; JFrame frame; WorldCanvas canvas; List<Grain> grains; /** * @wbp.parser.entryPoint */ public CanvasTest() { init(); Graphics g = canvas.getGraphics(); g.fillOval(20, 20, 20, 20); canvas.paint(g); } public void init() { grains = new ArrayList<Grain>(); frame = new JFrame(); JPanel panel = new JPanel(); frame.getContentPane().add(panel, BorderLayout.NORTH); JButton startBtn = new JButton("开始"); panel.add(startBtn); startBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub new Roll().start(); } }); canvas = new WorldCanvas(); canvas.setGrains(grains); frame.getContentPane().add(canvas); frame.setSize(400, 400); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private void createGrain() { for (int i = 0; i < CREATE_GRAIN_NUMBER; i++) { grains.add(new Grain()); } } private void moveGrain() { Grain grain; for (int i = 0; i < grains.size(); i++) { grain = grains.get(i); grain.move(); if (grain.isDead()) { grains.remove(i); } } } class Roll extends Thread { @Override public void run() { while (true) { createGrain(); moveGrain(); canvas.repaint(); } } } public static void main(String args[]) { new CanvasTest(); } }
Grain.java
package test; public class Grain{ private final int SPEED_X = 1; private final int SPEED_Y = 1; private final int MAX_X = 300; private final int MAX_Y = 300; private int pos_x; private int pos_y; private int radius = 5; private int time = 1; private boolean isDead = false; public Grain() { pos_x = generateRandom(0, 350); pos_y = generateRandom(0, 350); } /* * (non-Javadoc) * * @see test.IGrain#move() */ public void move() { pos_x = pos_x + SPEED_X * time; pos_y = pos_y + SPEED_Y * time; if (pos_x > MAX_X || pos_y > MAX_Y) { setDead(true); } } private int generateRandom(int min, int max) { int random = (int) (Math.random() * (max - min)) + min; return random; } public int getPos_x() { return pos_x; } public void setPos_x(int pos_x) { this.pos_x = pos_x; } public int getPos_y() { return pos_y; } public void setPos_y(int pos_y) { this.pos_y = pos_y; } public boolean isDead() { return isDead; } private void setDead(boolean b) { isDead = true; } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; } }
WorldCanvas.java
package test; import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; import java.util.List; public class WorldCanvas extends Canvas { /** * */ private static final long serialVersionUID = 1L; private List<Grain> grains; // public void update(Graphics g) { // g.clearRect(0, 0, 350, 350); // super.update(g); // } @Override public void repaint() { Graphics g = this.getGraphics(); update(g); } @Override public void paint(Graphics g) { g.setColor(Color.blue); for (Grain grain : grains) { g.fillOval(grain.getPos_x(), grain.getPos_y(), grain.getRadius(), grain.getRadius()); } } public List<Grain> getGrains() { return grains; } public void setGrains(List<Grain> grains) { this.grains = grains; } }
貌似排版有点渣.......
经过一番折腾,好多了,看样子是使用的姿势不对呃~
距离上次更新博客差不多是一年前的事情了,一年来,却没有什么大的长进,真是个悲伤的故事~
相关文章推荐
- 解决问题:Ubuntu 14.04下Eclipse CDT菜单栏失效
- Java程序员从笨鸟到菜鸟之(七十四)细谈Spring(六)spring之AOP基本概念和配置详解
- 复杂计算器的实现,没有界面
- 类A是公共的,应在名为A.java的文件中声明错误
- 算法(第四版)学习笔记之java实现堆排序
- JAVA基础之理解JNI原理
- 【java】this()与super()使用详解
- java中复制文本文件的方式我总结为14种(按字符读取4中,按字节读取8种!??)
- java中的同步与异步
- Java基础查漏补缺:(开篇)为什么要在即将找工作的时候还在看Java基础
- spring bean 生命周期
- Learn Gradle - CH 3 Java 快速入门
- 关于Java集合的总结
- 用枚举方法编写的星期几 并输出相应文字(Java)
- Java对象的equals方法分析与重写
- JAVA深入研究——Method的Invoke方法
- SPRING技术内幕-笔记(十二)spring驱动ibatis的设计与实现
- JavaWeb学习笔记:Servlet
- java Ip地址的获取 简单代码
- 冒泡,简单选择,直接插入排序比较(Java版)