您的位置:首页 > 大数据 > 人工智能

paint()方法中Graphics实例对象是怎么来的

2010-09-02 23:13 435 查看
学了一个多月的J2ME应用,画界面也画了好些,却一直忽视了最常用的paint方法中Graphics实例对象的来源,今天在别人的博客中找了篇文章,希望对大家有用

原文地址:http://blog.csdn.net/crazyerghb/archive/2007/10/10/1818302.aspx

在Canvas中调用repaint后,系统会调用paint(Graphics g),而能给与程序员发挥的空间就在paint里,那么,paint的Graphics从那里来的呢?换句话说,我们paint操作的东西到底是什么呢?他又是怎么反应到手机硬件屏幕上了呢?

1。我们先来看看repaint是怎么实现的。

public final void repaint(int x, int y, int width, int height) {

synchronized (Display.LCDUILock) {

callRepaint(x + viewport[X], y + viewport[Y], width, height, null);

}

}

2。那么callReapint是怎么实现的呢?

在Displable里有一个方法(以final标识),此方法禁止任何子类重写,就是为了保证只有他来控制repaint事件的传递:

final void callRepaint(int x, int y, int width, int height, Object target) {

if (currentDisplay != null) {

// Note: Display will not let anyone but the current

// Displayable schedule repaints

currentDisplay.repaintImpl(paintDelegate, x, y, width, height, target);//target是NULL

}

}

3,在Diaplay里,此方法的实现如下:

void repaintImpl(Displayable d, int x, int y, int w, int h,

Object target) {

synchronized (LCDUILock) {

if (paintSuspended || !hasForeground || d != current) {

return;

}

}

eventHandler.scheduleRepaint(x, y, w, h, target);//把Repaint的请求部署下去

}

4. /**

* Called to schedule a repaint of the current Displayable

* as soon as possible

*

* @param x The x coordinate of the origin of the repaint rectangle

* @param y The y coordinate of the origin of the repaint rectangle

* @param w The width of the repaint rectangle

* @param h The height of the repaint rectangle

* @param target An optional target Object, which may have been the

* original requestor for the repaint

*/

public void scheduleRepaint(int x, int y, int w, int h, Object target) {

eventQueue.push(x, y, w, h, target);//安排后的处理就是往事件队列里push一个消息

}

5.

/**

* Push a repaint

*

* @param x The x origin coordinate

* @param y The y origin coordinate

* @param w The width

* @param h The height

* @param target The optional paint target

*/

public void push(int x, int y, int w, int h, Object target) {

try {

w += x; // convert from width, height to absolute

h += y; // x2, y2

if (x < 0) x = 0;

if (y < 0) y = 0;

synchronized (qLock) {

if (paintX1 == -1) {

// If we have no pending repaint

// just store the region

paintX1 = x;

paintY1 = y;

paintX2 = w;

paintY2 = h;

paintTarget = target;

} else {

// If there is a pending repaint

// union the dirty regions

if (paintX1 > x) {

paintX1 = x;

}

if (paintY1 > y) {

paintY1 = y;

}

if (paintX2 < w) {

paintX2 = w;

}

if (paintY2 < h) {

paintY2 = h;

}

paintTarget = null;

}

} // synchronized

} catch (Throwable t) {

t.printStackTrace();

}

queuedEventHandler.process(); //push消息后,就要立即进行处理,

}

6. 其实,一个事件队列就是一个无限循环的线程,不停的检查队列,看是否有需要处理的消息,有则处理,无则等待。现在既然已经push进去一个消息,就要立即唤醒他去处理这个消息。所以自然就notify了。

/**

* Signal this handler there is a need to process

* a pending event.

*/

public synchronized void process() {

try {

notify();

} catch (Throwable t) {

// TO DO: Do something more useful with this

t.printStackTrace();

}

}

7. 我们来看看事件队列EventQuene的线程是如何定义的,其中repaintScreenEvent是来对repaint事件进行处理的。

/**

* Process events from the EventQueue

*/

public void run() {

int x1, y1, x2, y2, type = 0;

Display parentOfNextScreen = null;

Displayable nextScreen = null;

boolean call = false;

........

if (x1 != -1) {

repaintScreenEvent(x1, y1, x2, y2, target);

x1 = y1 = x2 = y2 = -1;

target = null;

}

8. 那么,我们再来看看是如何实现repaintScreenEvent的,

/**

* Process a repaint event

*

* @param x1 The x origin coordinate

* @param y1 The y origin coordinate

* @param x2 The lower right x coordinate

* @param y2 The lower right y coordinate

* @param target The optional paint target

*/

void repaintScreenEvent(int x1, int y1, int x2, int y2, Object target) {

try {

synchronized (eventLock) {

displayManager.repaint(x1, y1, x2, y2, target);//调用了屏幕管理器的reapint,请注意,这是5个参数的repaint

}

} catch (Throwable t) {

handleThrowable(t);

}

}

9. 这个5个参数的实现方法如下:

/*

* SYNC NOTE: this method performs its own locking of

* LCDUILock and calloutLock. Therefore, callers

* must not hold any locks when they call this method.

*/

void repaint(int x1, int y1, int x2, int y2, Object target) {

Displayable currentCopy = null;

synchronized (LCDUILock) {

if (paintSuspended || !hasForeground) {

return;

}

currentCopy = current;

}

if (currentCopy == null) {

return;

}

screenGraphics.reset(x1, y1, x2, y2);

current.callPaint(screenGraphics, target);

refresh(x1, y1, x2, y2);

}

10. 可见,screenGraphcis当作了参数传递给了callPaint,在需要知道screenGraphics如何来的之前,我们先看看callPaint方法。

/**

* Paint this Canvas

*

* @param g the Graphics to paint to

* @param target the target Object of this repaint

*/

void callPaint(Graphics g, Object target) {

super.callPaint(g, target);

if (g.getClipY() + g.getClipHeight() <= viewport[Y]) {

return;

}

// We prevent the Canvas from drawing outside of the

// allowable viewport - such as over the command labels

g.clipRect(viewport[X], viewport[Y],

viewport[WIDTH],

viewport[HEIGHT]);

synchronized (Display.calloutLock) {

g.translate(viewport[X], viewport[Y]);

try {

paint(g); //这就是调用后repaint方法后,系统会调用到paint(g),这个g就是调用callPaint方法传递过来的screenGraphcis,请看下面分析

} catch (Throwable t) {

Display.handleThrowable(t);

}

g.translate(-viewport[X], -viewport[Y]);

}

}

11,现在我们来看看screenGraphics是从那里来的,在Display类里,有一段静态数据初始化的代码

./*

* ************* Static initializer, constructor

*/

static {

/* done this way because native access to static fields is hard */

DeviceCaps c = new DeviceCaps();

WIDTH = c.width;

HEIGHT = c.height;

ADORNEDHEIGHT = c.adornedHeight;

………

c = null; // let the DeviceCaps instance be garbage collected

/* Let com.sun.midp classes call in to this class. */

displayManagerImpl = new DisplayManagerImpl();//初始化屏幕管理器

DisplayManagerFactory.SetDisplayManagerImpl(displayManagerImpl); //得到屏幕管理器,这个一个工厂方法,为以后使用做准备,此处不赘叙

deviceAccess = new DisplayDeviceAccess(); //猜测可能是KVM和DEVICE的桥接器

eventHandler = getEventHandler(); //事件处理器

screenGraphics = Graphics.getGraphics(null);//哈哈,screenGraphics找到了

}

12. 在Graphics类里有个方法,getGraphics,但是,这个方法是默认的package级别的访问权限。所以我们不会看到。

static Graphics getGraphics(Image img) {

if (img == null) {

return new Graphics(Display.WIDTH, Display.HEIGHT);

} else {

return new ImageGraphics(img); //猜猜它是为谁服务的?

}

}

13. Graphics的构造方法如下:

Graphics(int w, int h) {

destination = null;

maxWidth = (short) (w & 0x7fff);

maxHeight = (short) (h & 0x7fff);

init();

reset();

}

14. 天哪,终于找到了,native的一个接口,init,是它来初始化了Graphics

/**

* Intialize the native peer of this Graphics context

*/

private native void init();

15.其实,找到这里,还不能说挖掘到了最根本的实现,但是,我们暂且就挖到这里,所以,总结一下,paint中的Graphics,其实是Display来提供的,只是,这个变量不会让我们看到。那么KVM的底层实现,也就是native的init(),会把这个Graphics给对接到某个具体的平台,比如win32的GDI,Symbian的Graphics GDI,还有LINUX常见的QT平台的GDI等等。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/crazyerghb/archive/2007/10/10/1818302.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: