| 操作系统 办公 实用知识 设计 开发 WEB开发 移动开发 数据库 软件工程 网管 安全 管理 信息化 答疑 渠道 |
paint中的Graphics从何而来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等等。
今日推荐
|
重点推荐
领军企业技术文库
+更多领军技术文库
最新专题
电子杂志订阅
| ||||||||