java实现画板(JAVA画板)

http://www.itjxue.com  2023-01-26 17:42  来源:未知  点击次数: 

Java如何实现验证码验证功能

Java如何实现验证码验证功能呢?日常生活中,验证码随处可见,他可以在一定程度上保护账号安全,那么他是怎么实现的呢?

Java实现验证码验证功能其实非常简单:用到了一个Graphics类在画板上绘制字母,随机选取一定数量的字母随机生成,然后在画板上随机生成几条干扰线。

首先,写一个验证码生成帮助类,用来绘制随机字母:

import?java.awt.Color;

import?java.awt.Font;

import?java.awt.Graphics;

import?java.awt.image.BufferedImage;

import?java.io.IOException;

import?java.io.OutputStream;

import?java.util.Random;

import?javax.imageio.ImageIO;

public?final?class?GraphicHelper?{

/**

*?以字符串形式返回生成的验证码,同时输出一个图片

*

*?@param?width

*????????????图片的宽度

*?@param?height

*????????????图片的高度

*?@param?imgType

*????????????图片的类型

*?@param?output

*????????????图片的输出流(图片将输出到这个流中)

*?@return?返回所生成的验证码(字符串)

*/

public?static?String?create(final?int?width,?final?int?height,?final?String?imgType,?OutputStream?output)?{

StringBuffer?sb?=?new?StringBuffer();

Random?random?=?new?Random();

BufferedImage?image?=?new?BufferedImage(width,?height,?BufferedImage.TYPE_INT_RGB);

Graphics?graphic?=?image.getGraphics();

graphic.setColor(Color.getColor("F8F8F8"));

graphic.fillRect(0,?0,?width,?height);

Color[]?colors?=?new?Color[]?{?Color.BLUE,?Color.GRAY,?Color.GREEN,?Color.RED,?Color.BLACK,?Color.ORANGE,

Color.CYAN?};

//?在?"画板"上生成干扰线条?(?50?是线条个数)

for?(int?i?=?0;?i??50;?i++)?{

graphic.setColor(colors[random.nextInt(colors.length)]);

final?int?x?=?random.nextInt(width);

final?int?y?=?random.nextInt(height);

final?int?w?=?random.nextInt(20);

final?int?h?=?random.nextInt(20);

final?int?signA?=?random.nextBoolean()???1?:?-1;

final?int?signB?=?random.nextBoolean()???1?:?-1;

graphic.drawLine(x,?y,?x?+?w?*?signA,?y?+?h?*?signB);

}

//?在?"画板"上绘制字母

graphic.setFont(new?Font("Comic?Sans?MS",?Font.BOLD,?30));

for?(int?i?=?0;?i??6;?i++)?{

final?int?temp?=?random.nextInt(26)?+?97;

String?s?=?String.valueOf((char)?temp);

sb.append(s);

graphic.setColor(colors[random.nextInt(colors.length)]);

graphic.drawString(s,?i?*?(width?/?6),?height?-?(height?/?3));

}

graphic.dispose();

try?{

ImageIO.write(image,?imgType,?output);

}?catch?(IOException?e)?{

e.printStackTrace();

}

return?sb.toString();

}

}?

接着,创建一个servlet,用来固定图片大小,以及处理验证码的使用场景,以及捕获页面生成的验证码(捕获到的二维码与用户输入的验证码一致才能通过)。

import?java.io.OutputStream;

import?javax.servlet.ServletException;

import?javax.servlet.annotation.WebServlet;

import?javax.servlet.http.HttpServlet;

import?javax.servlet.http.HttpServletRequest;

import?javax.servlet.http.HttpServletResponse;

import?javax.servlet.http.HttpSession;

@WebServlet(urlPatterns?=?"/verify/regist.do"?)

public?class?VerifyCodeServlet?extends?HttpServlet?{

private?static?final?long?serialVersionUID?=?3398560501558431737L;

@Override

protected?void?service(HttpServletRequest?request,?HttpServletResponse?response)

throws?ServletException,?IOException?{

//?获得?当前请求?对应的?会话对象

HttpSession?session?=?request.getSession();

//?从请求中获得?URI?(?统一资源标识符?)

String?uri?=?request.getRequestURI();

System.out.println("hello?:?"?+?uri);

final?int?width?=?180;?//?图片宽度

final?int?height?=?40;?//?图片高度

final?String?imgType?=?"jpeg";?//?指定图片格式?(不是指MIME类型)

final?OutputStream?output?=?response.getOutputStream();?//?获得可以向客户端返回图片的输出流

//?(字节流)

//?创建验证码图片并返回图片上的字符串

String?code?=?GraphicHelper.create(width,?height,?imgType,?output);

System.out.println("验证码内容:?"?+?code);

//?建立?uri?和?相应的?验证码?的关联?(?存储到当前会话对象的属性中?)

session.setAttribute(uri,?code);

System.out.println(session.getAttribute(uri));

}

}?

接着写一个HTML注册页面用来检验一下:

html

head

meta?charset="UTF-8"

title注册/title

link?rel="stylesheet"?href="styles/general.css"

link?rel="stylesheet"?href="styles/cell.css"

link?rel="stylesheet"?href="styles/form.css"

script?type="text/javascript"?src="js/ref.js"/script

style?type="text/css"?

.logo-container?{

margin-top:?50px?;

}

.logo-container?img?{

width:?100px?;

}

.message-container?{

height:?80px?;

}

.link-container?{

height:?40px?;

line-height:?40px?;

}

.link-container?a?{

text-decoration:?none?;

}

/style

/head

body

div?class="container?form-container"

form?action="/wendao/regist.do"?method="post"

div?class="form"?!--?注册表单开始?--

div?class="form-row"

span?class="cell-1"

i?class="fa?fa-user"/i

/span

span?class="cell-11"?style="text-align:?left;"

input?type="text"?name="username"?placeholder="请输入用户名"

/span

/div

div?class="form-row"

span?class="cell-1"

i?class="fa?fa-key"/i

/span

span?class="cell-11"?style="text-align:?left;"

input?type="password"?name="password"?placeholder="请输入密码"

/span

/div

div?class="form-row"

span?class="cell-1"

i?class="fa?fa-keyboard-o"/i

/span

span?class="cell-11"?style="text-align:?left;"

input?type="password"?name="confirm"?placeholder="请确认密码"

/span

/div

div?class="form-row"

span?class="cell-7"

input?type="text"?name="verifyCode"?placeholder="请输入验证码"

/span

span?class="cell-5"?style="text-align:?center;"

img?src="/demo/verify/regist.do"?onclick="myRefersh(this)"

/span

/div

div?class="form-row"?style="border:?none;"

span?class="cell-6"?style="text-align:?left"

input?type="reset"?value="重置"

/span

span?class="cell-6"??style="text-align:right;"

input?type="submit"?value="注册"

/span

/div

/div?!--?注册表单结束?--

/form

/div

/body

/html

效果如下图:

在控制台接收到的图片中验证码的变化如下:

当点击刷新页面的时候,验证码也会随着变化,但我们看不清验证码时,只要点击验证码就会刷新,这样局部的刷新可以用JavaScript来实现。

在img

src="/demo/verify/regist.do"中,添加一个问号和一串后缀数字,当刷新时让后缀数字不断改变,那么形成的验证码也会不断变化,我们可以采用的一种办法是后缀数字用date代替,date获取本机时间,时间是随时变的,这样就保证了刷新验证码可以随时变化。

代码如下:

function?myRefersh(?e?)?{

const?source?=?e.src?;?//?获得原来的?src?中的内容

//console.log(?"source?:?"?+?source??)?;

var?index?=?source.indexOf(?"?"?)?;??//?从?source?中寻找???第一次出现的位置?(如果不存在则返回?-1?)

//console.log(?"index?:?"?+?index??)?;

if(?index??-1?)?{?//?如果找到了????就进入内部

var?s?=?source.substring(?0?,?index?)?;?//?从?source?中截取?index?之前的内容?(?index?以及?index?之后的内容都被舍弃?)

//console.log(?"s?:?"?+?s??)?;

var?date?=?new?Date();?//?创建一个?Date?对象的?一个?实例

var?time?=?date.getTime()?;?//?从?新创建的?Date?对象的实例中获得该时间对应毫秒值

e.src?=?s?+?"?time="?+?time?;?//?将?加了?尾巴?的?地址?重新放入到?src?上

//console.log(?e.src?)?;

}?else?{

var?date?=?new?Date();

e.src?=?source?+?"?time="?+?date.getTime();

}

}

如回答不详细可追问

java中的canvas类有什么作用?

从词源可以看出\x0d\x0aCanvas,麻布-画布-画油画的画板。\x0d\x0aPanel 小块布-块版-面板、墙板、地板\x0d\x0aCanvas,直接继承自Component组件,主要用于绘图,没有控件,更原始\x0d\x0aPanel,继承自Container容器,主要用于镶嵌在其他控件里面当面板。\x0d\x0a\x0d\x0a由于Java SE版的Canvas和Panel都继承自Component,共用Component的paint(Graphics g)\x0d\x0a方式绘制自己的内容。由于使用同一个Graphic类,所以那些drawXXX都一致。\x0d\x0a\x0d\x0a绘制的方式一样,2者的绘制速度就没大区别,\x0d\x0aCanvas更适合画全屏的、没有控件的情形。像手机上JavaME就主要用Canvas\x0d\x0aPanel适合嵌入到其他控件中使用。

用java编写了一个模拟计算器的界面设计,怎么实现运算功能呢

view sourceprint?

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.GridLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JTextField;

/**

* 一个计算器,与Windows附件自带计算器的标准版功能、界面相仿。 但还不支持键盘操作。

*/

public class Calculator extends JFrame implements ActionListener {

/** 计算器上的键的显示名字 */

private final String[] KEYS = { "7", "8", "9", "/", "sqrt", "4", "5", "6",

"*", "%", "1", "2", "3", "-", "1/x", "0", "+/-", ".", "+", "=" };

/** 计算器上的功能键的显示名字 */

private final String[] COMMAND = { "Backspace", "CE", "C" };

/** 计算器左边的M的显示名字 */

private final String[] M = { " ", "MC", "MR", "MS", "M+" };

/** 计算器上键的按钮 */

private JButton keys[] = new JButton[KEYS.length];

/** 计算器上的功能键的按钮 */

private JButton commands[] = new JButton[COMMAND.length];

/** 计算器左边的M的按钮 */

private JButton m[] = new JButton[M.length];

/** 计算结果文本框 */

private JTextField resultText = new JTextField("0");

// 标志用户按的是否是整个表达式的第一个数字,或者是运算符后的第一个数字

private boolean firstDigit = true;

// 计算的中间结果。

private double resultNum = 0.0;

// 当前运算的运算符

private String operator = "=";

// 操作是否合法

private boolean operateValidFlag = true;

/**

* 构造函数

*/

public Calculator() {

super();

// 初始化计算器

init();

// 设置计算器的背景颜色

this.setBackground(Color.LIGHT_GRAY);

this.setTitle("计算器");

// 在屏幕(500, 300)坐标处显示计算器

this.setLocation(500, 300);

// 不许修改计算器的大小

this.setResizable(false);

// 使计算器中各组件大小合适

this.pack();

}

/**

* 初始化计算器

*/

private void init() {

// 文本框中的内容采用右对齐方式

resultText.setHorizontalAlignment(JTextField.RIGHT);

// 不允许修改结果文本框

resultText.setEditable(false);

// 设置文本框背景颜色为白色

resultText.setBackground(Color.WHITE);

// 初始化计算器上键的按钮,将键放在一个画板内

JPanel calckeysPanel = new JPanel();

// 用网格布局器,4行,5列的网格,网格之间的水平方向间隔为3个象素,垂直方向间隔为3个象素

calckeysPanel.setLayout(new GridLayout(4, 5, 3, 3));

for (int i = 0; i KEYS.length; i++) {

keys[i] = new JButton(KEYS[i]);

calckeysPanel.add(keys[i]);

keys[i].setForeground(Color.blue);

}

// 运算符键用红色标示,其他键用蓝色表示

keys[3].setForeground(Color.red);

keys[8].setForeground(Color.red);

keys[13].setForeground(Color.red);

keys[18].setForeground(Color.red);

keys[19].setForeground(Color.red);

// 初始化功能键,都用红色标示。将功能键放在一个画板内

JPanel commandsPanel = new JPanel();

// 用网格布局器,1行,3列的网格,网格之间的水平方向间隔为3个象素,垂直方向间隔为3个象素

commandsPanel.setLayout(new GridLayout(1, 3, 3, 3));

for (int i = 0; i COMMAND.length; i++) {

commands[i] = new JButton(COMMAND[i]);

commandsPanel.add(commands[i]);

commands[i].setForeground(Color.red);

}

// 初始化M键,用红色标示,将M键放在一个画板内

JPanel calmsPanel = new JPanel();

// 用网格布局管理器,5行,1列的网格,网格之间的水平方向间隔为3个象素,垂直方向间隔为3个象素

calmsPanel.setLayout(new GridLayout(5, 1, 3, 3));

for (int i = 0; i M.length; i++) {

m[i] = new JButton(M[i]);

calmsPanel.add(m[i]);

m[i].setForeground(Color.red);

}

// 下面进行计算器的整体布局,将calckeys和command画板放在计算器的中部,

// 将文本框放在北部,将calms画板放在计算器的西部。

// 新建一个大的画板,将上面建立的command和calckeys画板放在该画板内

JPanel panel1 = new JPanel();

// 画板采用边界布局管理器,画板里组件之间的水平和垂直方向上间隔都为3象素

panel1.setLayout(new BorderLayout(3, 3));

panel1.add("North", commandsPanel);

panel1.add("Center", calckeysPanel);

// 建立一个画板放文本框

JPanel top = new JPanel();

top.setLayout(new BorderLayout());

top.add("Center", resultText);

// 整体布局

getContentPane().setLayout(new BorderLayout(3, 5));

getContentPane().add("North", top);

getContentPane().add("Center", panel1);

getContentPane().add("West", calmsPanel);

// 为各按钮添加事件侦听器

// 都使用同一个事件侦听器,即本对象。本类的声明中有implements ActionListener

for (int i = 0; i KEYS.length; i++) {

keys[i].addActionListener(this);

}

for (int i = 0; i COMMAND.length; i++) {

commands[i].addActionListener(this);

}

for (int i = 0; i M.length; i++) {

m[i].addActionListener(this);

}

}

/**

* 处理事件

*/

public void actionPerformed(ActionEvent e) {

// 获取事件源的标签

String label = e.getActionCommand();

if (label.equals(COMMAND[0])) {

// 用户按了"Backspace"键

handleBackspace();

} else if (label.equals(COMMAND[1])) {

// 用户按了"CE"键

resultText.setText("0");

} else if (label.equals(COMMAND[2])) {

// 用户按了"C"键

handleC();

} else if ("0123456789.".indexOf(label) = 0) {

// 用户按了数字键或者小数点键

handleNumber(label);

// handlezero(zero);

} else {

// 用户按了运算符键

handleOperator(label);

}

}

/**

* 处理Backspace键被按下的事件

*/

private void handleBackspace() {

String text = resultText.getText();

int i = text.length();

if (i 0) {

// 退格,将文本最后一个字符去掉

text = text.substring(0, i - 1);

if (text.length() == 0) {

// 如果文本没有了内容,则初始化计算器的各种值

resultText.setText("0");

firstDigit = true;

operator = "=";

} else {

// 显示新的文本

resultText.setText(text);

}

}

}

/**

* 处理数字键被按下的事件

*

* @param key

*/

private void handleNumber(String key) {

if (firstDigit) {

// 输入的第一个数字

resultText.setText(key);

} else if ((key.equals(".")) (resultText.getText().indexOf(".") 0)) {

// 输入的是小数点,并且之前没有小数点,则将小数点附在结果文本框的后面

resultText.setText(resultText.getText() + ".");

} else if (!key.equals(".")) {

// 如果输入的不是小数点,则将数字附在结果文本框的后面

resultText.setText(resultText.getText() + key);

}

// 以后输入的肯定不是第一个数字了

firstDigit = false;

}

/**

* 处理C键被按下的事件

*/

private void handleC() {

// 初始化计算器的各种值

resultText.setText("0");

firstDigit = true;

operator = "=";

}

/**

* 处理运算符键被按下的事件

*

* @param key

*/

private void handleOperator(String key) {

if (operator.equals("/")) {

// 除法运算

// 如果当前结果文本框中的值等于0

if (getNumberFromText() == 0.0) {

// 操作不合法

operateValidFlag = false;

resultText.setText("除数不能为零");

} else {

resultNum /= getNumberFromText();

}

} else if (operator.equals("1/x")) {

// 倒数运算

if (resultNum == 0.0) {

// 操作不合法

operateValidFlag = false;

resultText.setText("零没有倒数");

} else {

resultNum = 1 / resultNum;

}

} else if (operator.equals("+")) {

// 加法运算

resultNum += getNumberFromText();

} else if (operator.equals("-")) {

// 减法运算

resultNum -= getNumberFromText();

} else if (operator.equals("*")) {

// 乘法运算

resultNum *= getNumberFromText();

} else if (operator.equals("sqrt")) {

// 平方根运算

resultNum = Math.sqrt(resultNum);

} else if (operator.equals("%")) {

// 百分号运算,除以100

resultNum = resultNum / 100;

} else if (operator.equals("+/-")) {

// 正数负数运算

resultNum = resultNum * (-1);

} else if (operator.equals("=")) {

// 赋值运算

resultNum = getNumberFromText();

}

if (operateValidFlag) {

// 双精度浮点数的运算

long t1;

double t2;

t1 = (long) resultNum;

t2 = resultNum - t1;

if (t2 == 0) {

resultText.setText(String.valueOf(t1));

} else {

resultText.setText(String.valueOf(resultNum));

}

}

// 运算符等于用户按的按钮

operator = key;

firstDigit = true;

operateValidFlag = true;

}

/**

* 从结果文本框中获取数字

*

* @return

*/

private double getNumberFromText() {

double result = 0;

try {

result = Double.valueOf(resultText.getText()).doubleValue();

} catch (NumberFormatException e) {

}

return result;

}

public static void main(String args[]) {

Calculator calculator1 = new Calculator();

calculator1.setVisible(true);

calculator1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

}

java 双缓冲,消除闪烁 的问题

[转]双缓冲在画板程序中的应用

1.用双缓冲解决画板程序中的刷新问题

我们用Java编制画板程序的时候,总是存在一个刷新的问题:当Canvas所在的窗口最小化或者被其他应用程序遮挡后,再次恢复,Canvas上的图形

数据将被部分或者完全擦除掉.

通常解决这个问题的方法是在Canvas的paint()函数中重绘图形,但是由于在绘图的过程中产生了大量的数据,重新在Canvas上绘制这些数据将导

致大量的系统开销,还会产生闪烁,故该方法可行但并不可取.

利用双缓冲技术可以很好的解决这个问题,其主要原理是开辟两个图形缓冲区,一个是前台的显示缓冲(也就是Canvas),一个是后台的图形缓冲(

通常是Image).用户在绘制图形时,我们对这两个缓冲区进行同步更新,相当于为前台的数据作了一个后台备份.当前台的图形被遮盖需要恢复的

时候,我们就可以用这个后台备份来恢复,具体方法是重写paint()函数,将备份好的图像一次性的画到屏幕上去.

为什么是paint()?这里需要先了解一下有关Java处理AWT绘图的基础知识:Java的显示更新是由一个AWT线程来控制完成的.该线程主要负责两种

与显示更新相关的情况.第一种情况称为曝光,表示部分显示区域毁坏或需要清除.这种情况发生时,系统直接调用paint()方法;第二种情况是程

序决定重画显示区域,添加一些新的内容,此时需要程序调用成员的repaint()方法,repaint()方法调用成员的update(),update()再调用paint()

方法.显然我们所说的就是第一种.只要Canvas组件所在的窗口最小化或者被其他应用程序遮挡住,系统就会调用paint()对画布进行重绘.如果我

们在paint()方法中什么都不做,就只能眼睁睁的看着辛辛苦苦画的线条一旦被覆盖就再也看不见了.

作为起点,请先看一个最简单的画板程序,请注意,以下程序使用的是j2sdk1.4.1版本,在Win98的IE下(不是Tencent Explorer)全部测试通过:

//:WBApplet.java

import java.applet.*;

public class WBApplet extends Applet{

final static int DEFAULT_BOARDWIDTH=700;

final static int DEFAULT_BOARDHEIGHT=400;

public void init(){

super.init();

setLayout(null);

whiteBoard = new WhiteBoard(this);

whiteBoard.reshape(0,0,DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);

add(whiteBoard);

}

WhiteBoard whiteBoard;

}

///:~

//:WhiteBoard.java

java.awt.*;

import java.awt.event.*;

public class WhiteBoard extends Canvas implements MouseMotionListener,MouseListener{

final static int DEFAULT_BOARDWIDTH=700;

final static int DEFAULT_BOARDHEIGHT=400;

int x0,y0,x1,y1;

WhiteBoard(WBApplet WBApplet1){

parent = WBApplet1;

addMouseMotionListener(this);

addMouseListener(this);

}

synchronized public void update_buffer(Graphics g,DrawItem data) {

g.drawLine(data.x0,data.y0,data.x1,data.y1);

}

public void mouseReleased(MouseEvent e){}

public void mouseEntered(MouseEvent e){}

public void mouseExited(MouseEvent e){}

public void mouseClicked(MouseEvent e){}

public void mouseMoved(MouseEvent e){}

public void mouseDragged(MouseEvent e){

x1=e.getX();

y1=e.getY();

Graphics g = getGraphics();

update_buffer(g,new DrawItem(x0,y0,x1,y1));

g.dispose();

x0=x1;

y0=y1;

}

public void mousePressed(MouseEvent e){

x0 =e.getX();

y0 =e.getY();

}

WBApplet parent;

}

class DrawItem{

DrawItem(int x0,int y0,int x1,int y1){

this.x0=x0;

this.y0=y0;

this.x1=x1;

this.y1=y1;

}

int x0;

int y0;

int x1;

int y1;

}

///:~

我们将白板需完成的所有逻辑操作封装在了一个WhiteBoard类中,以方便主程序的Applet调用.同时,定义了一个绘图的数据类DrawItem,用来封

装图形数据.绘图的操作都写在update_buffer中.显然,这个程序无法实现刷新后的恢复,我们需要使用双缓冲技术.

为了实现双缓冲,首先定义图形缓冲区如下

private Image off_screen_buf;

private Graphics off_screen_gc;

并在WhiteBoard类的构造函数中对其进行初始化

off_screen_buf =parent.createImage(DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);

off_screen_gc = off_screen_buf.getGraphics();

在处理用户绘制图形的函数中,我们使用update_buffer对显示缓冲和图形缓冲同时进行更新

Graphics g = getGraphics();

update_buffer(g,new DrawItem(x0,y0,x1,y1));//前台更新画布

update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));//后台更新缓冲

g.dispose();

显然,后台的更新操作是不可视的,所以是off-screen.

最后,重写paint()方法,调用copy_from_offscreen_buf(g)将图形缓冲区的图像画到屏幕上去.

public void paint(Graphics g){

copy_from_offscreen_buf(g);

}

void copy_from_offscreen_buf(Graphics g){

if(g != null)

g.drawImage(off_screen_buf, 0, 0, null);

}

就是这么简单的几行代码,就可以让我们完全的避免图形不能恢复的问题.下面是WhiteBoard经改进后的完整代码.

//:WhiteBoard.java

import java.awt.*;

import java.awt.event.*;

public class WhiteBoard extends Canvas implements MouseMotionListener,MouseListener{

final static int DEFAULT_BOARDWIDTH=700;

final static int DEFAULT_BOARDHEIGHT=400;

int x0,y0,x1,y1;

WhiteBoard(WBApplet WBApplet1){

parent = WBApplet1;

off_screen_buf =parent.createImage(DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);

off_screen_gc = off_screen_buf.getGraphics();

addMouseMotionListener(this);

addMouseListener(this);

}

synchronized public void update_buffer(Graphics g,DrawItem data) {

g.drawLine(data.x0,data.y0,data.x1,data.y1);

}

public void mouseMoved(MouseEvent e){}

public void mouseReleased(MouseEvent e){}

public void mouseEntered(MouseEvent e){}

public void mouseExited(MouseEvent e){}

public void mouseClicked(MouseEvent e){}

public void mouseDragged(MouseEvent e){

x1=e.getX();

y1=e.getY();

Graphics g = getGraphics();

update_buffer(g,new DrawItem(x0,y0,x1,y1));

update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));

g.dispose();

x0=x1;

y0=y1;

}

public void mousePressed(MouseEvent e){

x0 =e.getX();

y0 =e.getY();

}

public void paint(Graphics g){

copy_from_offscreen_buf(g);//把这句话屏蔽掉,就不能恢复用户绘制的图形了

}

void copy_from_offscreen_buf(Graphics g){

if(g != null)

g.drawImage(off_screen_buf, 0, 0, null);

}

private Image off_screen_buf;

private Graphics off_screen_gc;

WBApplet parent;

}

class DrawItem{

DrawItem(int x0,int y0,int x1,int y1){

this.x0=x0;

this.y0=y0;

this.x1=x1;

this.y1=y1;

}

int x0;

int y0;

int x1;

int y1;

}

///:~

运行一下,看是不是不一样了.这一次你想让你画的东西消失都不可能了.为了将这个原理说清楚,以上的代码我都没有编写的太复杂,下一次我们

会创建更加复杂,更加完善的画板程序.

2.用双缓冲实现各种图形的绘制

在一个画板程序中,用户应该能够用画笔绘制各种图形,除了上一节实现的自由画法(Freehand)外,还应该可以画直线,长方体,椭圆等等.以绘制

直线为例,我们都知道,只有在松开鼠标键之后,直线才实实在在的显示在了画布上,而在拖拽鼠标的过程中,直线在画布中的显示是随着鼠标的箭

头方位的变化而不断更新的.体现在程序中,这是一个不断擦除,显示,再擦除,再显示的过程.擦除的是箭头上一个点和起点间的直线,显示的是箭

头当前点和起点间的的直线.这个显示的过程由update_buffer负责,而这个擦除的工作则和上一节出理刷新一样,由copy_from_offscreen_buf来

完成.实际上,所谓擦除,也就是将画板恢复到某一个原来的时刻.

这一个过程在下面一个修改后的拖拽操作的处理程序中完成:

public void mouseDragged(MouseEvent e){

Graphics g = getGraphics();

copy_from_offscreen_buf(g);

x1=e.getX();

y1=e.getY();

update_buffer(g,new DrawItem(x0,y0,x1,y1));

g.dispose();

}

注意,在该方法中,我们没有对后台缓冲进行更新,这是因为鼠标在拖拽的时候,虽然画板上会显示线条,但是这条直线并没有真正的画下去.那么

在什么时候应该对后台缓冲更新呢?显然,是在鼠标松开的时候.我们需要在mouseReleased方法中做这个工作.

public void mouseReleased(MouseEvent e){

Graphics g = getGraphics();

copy_from_offscreen_buf(g);

x1=e.getX();

y1=e.getY();

update_buffer(g,new DrawItem(x0,y0,x1,y1));

update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));

g.dispose();

}

可以看到,只有在鼠标松开的时候,画到画板上的直线才最后确定了,我们才能够将这一条线备份到缓冲区里面去.

下面是升级后的完整的WhiteBoard.java程序.

//:WhiteBoard.java

import java.awt.*;

import java.awt.event.*;

public class WhiteBoard extends Canvas implements MouseMotionListener,MouseListener{

final static int DEFAULT_BOARDWIDTH=700;

final static int DEFAULT_BOARDHEIGHT=400;

int x0,y0,x1,y1;

WhiteBoard(WBApplet WBApplet1){

parent = WBApplet1;

off_screen_buf =parent.createImage(DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);

off_screen_gc = off_screen_buf.getGraphics();

addMouseMotionListener(this);

addMouseListener(this);

draw_mode=2;

}

synchronized public void update_buffer(Graphics g,DrawItem data) {

g.drawLine(data.x0,data.y0,data.x1,data.y1);

}

public void mouseMoved(MouseEvent e){}

public void mouseReleased(MouseEvent e){

switch(draw_mode){

case 2:

Graphics g = getGraphics();

copy_from_offscreen_buf(g);

x1=e.getX();

y1=e.getY();

update_buffer(g,new DrawItem(x0,y0,x1,y1));

update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));

g.dispose();

}

}

public void mouseEntered(MouseEvent e){}

public void mouseExited(MouseEvent e){}

public void mouseClicked(MouseEvent e){}

public void mouseDragged(MouseEvent e){

switch(draw_mode){

case 1:

x1=e.getX();

y1=e.getY();

Graphics g = getGraphics();

update_buffer(g,new DrawItem(x0,y0,x1,y1));

update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));

g.dispose();

x0=x1;

y0=y1;

break;

case 2:

Graphics g1 = getGraphics();

copy_from_offscreen_buf(g1);

x1=e.getX();

y1=e.getY();

update_buffer(g1,new DrawItem(x0,y0,x1,y1));

g1.dispose();

}

}

public void mousePressed(MouseEvent e){

x0 =e.getX();

y0 =e.getY();

}

public void paint(Graphics g){

copy_from_offscreen_buf(g);

}

void copy_from_offscreen_buf(Graphics g){

if(g != null)

g.drawImage(off_screen_buf, 0, 0, null);

}

private int draw_mode;

private Image off_screen_buf;

private Graphics off_screen_gc;

WBApplet parent;

}

class DrawItem{

DrawItem(int x0,int y0,int x1,int y1){

this.x0=x0;

this.y0=y0;

this.x1=x1;

this.y1=y1;

}

int x0;

int y0;

int x1;

int y1;

}

///:~

注意到,在这个程序里面我们创建了一个新的私有变量draw_mode,用来存储绘图模式的代号.在这里,我们使用1来代表自由绘画,2来代表画直线.

在构造函数中为draw_mode定义初值可以使我们对不同种类图形绘制的调试很方便,在上面的程序中,我们定义的是2,如果赋值为1,则又回到自由

绘画的模式.事实上,我们应该在这样的一个框架上把程序不断的扩充和完善.

(责任编辑:IT教学网)

更多

推荐图片影音文章