操作系统  办公  实用知识  设计  开发  WEB开发  移动开发  数据库  软件工程  网管  安全  管理  信息化  答疑  渠道 

paint中的Graphics从何而来

2007-10-30 网友评论 0 条 点击进入论坛

  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等等。

已有 0 位对此文章感兴趣的网友发布了看法    
我来评两句 登录邮箱: 密码:
  匿名发表
今日推荐
技术文库(共有 46430 篇文章)
操作系统
办公软件
实用知识
网络管理
软件开发
WEB开发
软件工程
数据库
设计在线
信息安全
行业信息化
管理信息化
重点推荐
电子杂志订阅
点击电子杂志名称查看样刊
输入E-mail地址即可订阅
E-mail