Graphics对象只能从Control.CreateGraphics()、OnPaint事件的e.Graphics或Graphics.FromImage(bitmap)获取,不可直接new;需注意坐标系、抗锯齿、资源释放及双缓冲启用。

Graphics 对象从哪来:别直接 new
直接调用 new Graphics() 会抛出 NotSupportedException——Graphics 不是普通类,它必须绑定到一个有效的绘图表面。常见合法来源只有三个:Control.CreateGraphics()、重写 OnPaint 中的 e.Graphics、或从 Bitmap 创建。
-
Control.CreateGraphics()返回的Graphics是临时的,不参与双缓冲,窗口重绘时内容会丢失;仅适合调试或一次性绘制 - 真正稳定的绘图入口是重写控件的
OnPaint方法,使用参数PaintEventArgs e中的e.Graphics - 离屏绘制(如生成图片)要用
Graphics.FromImage(bitmap),记得之后调用graphics.Dispose()
DrawLine 和 FillRectangle 的坐标系陷阱
GDI+ 默认以控件左上角为原点 (0, 0),X 向右递增,Y 向下递增——这和数学坐标系相反,但和 Windows 窗口坐标一致。容易出错的是:线条宽度影响实际绘制范围,且 FillRectangle 填充的是「内部」,而 DrawRectangle 描边是以边线中心为基准。
-
DrawLine(pen, 0, 0, 100, 100):线段起点在 (0,0),终点在 (100,100),但若pen.Width = 3,实际像素会覆盖从 y=−1 到 y=+2 的区域 -
FillRectangle(brush, 10, 10, 50, 30):填充区域为 x∈[10,60), y∈[10,40) —— 宽高是「内尺寸」,不包含右/下边界像素 - 抗锯齿开关:用
graphics.SmoothingMode = SmoothingMode.AntiAlias可柔化斜线和圆弧,但会轻微降低性能
Pen 和 Brush 必须手动释放
Pen 和 Brush 类型都实现了 IDisposable,内部持有 GDI 句柄。不释放会导致句柄泄漏,程序运行一段时间后可能抛出 OutOfMemoryException 或绘图失败。
本文档主要讲述的是Android游戏开发之旅;今天Android123开始新的Android游戏开发之旅系列,主要从控制方法(按键、轨迹球、触屏、重力感应、摄像头、话筒气流、光线亮度)、图形View(高效绘图技术如双缓冲)、音效(游戏音乐)以及最后的OpenGL ES(Java层)和NDK的OpenGL和J2ME游戏移植到Android方法,当然还有一些游戏实现惯用方法,比如地图编辑器,在Android OpenGL如何使用MD2文件,个部分讲述下Android游戏开发的过程最终实现一个比较完整的游戏引擎
- 避免在循环中反复
new SolidBrush(Color.Red);应复用或用using包裹 - 系统预定义画刷(如
Brushes.Red)是静态只读对象,不用释放;但自定义new LinearGradientBrush(...)必须Dispose() - 典型安全写法:
using (var pen = new Pen(Color.Blue, 2f)) { g.DrawLine(pen, 0, 0, 100, 100); }
双缓冲没开?闪烁就是它
在 OnPaint 中直接绘图却没启用双缓冲,控件在重绘时会出现明显闪烁,尤其当背景清空和图形绘制分两步进行时。
- WinForms 中最简单开启方式:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true),建议在构造函数末尾调用 - 不要手动调用
g.Clear(Color.White)再画内容——Clear 本身就会触发一次屏幕刷新;应让背景绘制和前景绘制在同一个缓冲区完成 - 若使用
CreateGraphics(),双缓冲对其无效,因为它绕过了控件的绘制管道
Dispose 的 Pen,可能让程序跑半天才崩;一次没走 OnPaint 的 CreateGraphics,会让动画看起来像幻灯片。









