代码基本模仿
https://www.jb51.net/article/46069.htm
但是运行的时候,内存呈现阶梯式的上涨,内存占用越来越大,请教大神们这种问题怎么处理
源码:
public partial class LoadingPhoto : UserControl
{
private int count = -1;
private ArrayList images = new ArrayList();
public Bitmap[] bitmap = new Bitmap[8];
private int _value = 1;
private Color _circleColor = System.Drawing.Color.FromArgb(((int)(((byte)(156)))), ((int)(((byte)(121)))), ((int)(((byte)(58)))));
private Color _textColor = System.Drawing.Color.FromArgb(((int)(((byte)(156)))), ((int)(((byte)(121)))), ((int)(((byte)(58)))));
private float _circleSize = 0.8f;
private int width = 200;//设置圆的宽
private int height = 200;////设置圆的高
public LoadingPhoto()
{
InitializeComponent();
this.Paint += new PaintEventHandler(UserControl1_Paint);
}
[Category("TestGroup")]
public bool StartOrStop
{
get => false;
set
{
if (value)
{
StartWaiting();
}
else
{
StopWaiting();
}
}
}
[Category("TestGroup")]
public Color CirCleColor
{
get => _circleColor;
set => _circleColor = value;
}
[Category("TestGroup")]
public Color TextColor
{
get => _textColor;
set => label1.ForeColor = value;
}
public Bitmap DrawCircle(int j)
{
const float angle = 360.0F / 8;
Bitmap map = new Bitmap(150, 150);
Graphics g = Graphics.FromImage(map);
g.TranslateTransform(width / 2.0F, height / 2.0F);
g.RotateTransform(angle * _value);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
int[] a = new int[8] { 25, 50, 75, 100, 125, 150, 175, 200 };
for (int i = 1; i <= 8; i++)
{
int alpha = a[(i + j - 1) % 8];
Color drawColor = Color.FromArgb(alpha, _circleColor);
using (SolidBrush brush = new SolidBrush(drawColor))
{
float sizeRate = 3.5F / _circleSize;
float size = width / (6 * sizeRate);
float diff = (width / 10.0F) - size;
float x = (width / 80.0F) + diff;
float y = (height / 80.0F) + diff;
g.FillEllipse(brush, x, y, size, size);
g.RotateTransform(angle);
}
}
return map;
}
public void Draw()
{
for (int j = 0; j < 8; j++)
{
bitmap[7 - j] = DrawCircle(j);
}
}
protected override void OnResize(EventArgs e)
{
SetNewSize();
base.OnResize(e);
}
protected override void OnSizeChanged(EventArgs e)
{
SetNewSize();
base.OnSizeChanged(e);
}
private void SetNewSize()
{
int size = Math.Max(width, height);
pictureBox.Size = new Size(size, size);
}
public void set()
{
for (int i = 0; i < 8; i++)
{
Draw();
Bitmap map = new Bitmap((bitmap[i]), new Size(250, 250));
images.Add(map);
}
pictureBox.Image = (Image)images[0];
pictureBox.Size = pictureBox.Image.Size;
}
private void Timer_Tick(object sender, EventArgs e)
{
set();
count = (count + 1) % 8;
pictureBox.Image = (Image)images[count];
}
private void StartWaiting()
{
timer1.Start();
pictureBox.Visible = true;
}
private void StopWaiting()
{
timer1.Stop();
pictureBox.Visible = false;
}
private void timer1_Tick(object sender, EventArgs e)
{
set();
count = (count + 1) % 8;
pictureBox.Image = (Image)images[count];
}
private void LoadingPhoto_Load(object sender, EventArgs e)
{
}
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
// 创建GraphicsPath对象以定义圆角矩形的路径
using (System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath())
{
int radius = 30;
path.AddArc(0, 0, radius, radius, 180, 90); // 左上角
path.AddArc(this.Width - radius, 0, radius, radius, 270, 90); // 右上角
path.AddArc(this.Width - radius, this.Height - radius, radius, radius, 0, 90); // 右下角
path.AddArc(0, this.Height - radius, radius, radius, 90, 90); // 左下角
// 设置Region属性,但这一步在Paint事件中不是必需的,因为Region已经在Load或构造函数中设置过了
// this.Region = new System.Drawing.Region(path);
// 使用Graphics对象绘制边框
//using (Pen pen = new Pen(Color.Black, 2)) // 可以自定义颜色和宽度
//{
// e.Graphics.DrawPath(pen, path);
//}
}
}
}
你跑一会暂停,看看images数组的长度.
多谢大神指点,这images数组的长度很大,我clear后,内存涨幅减缓了不少,但是还是缓慢增长
开始从800MB 涨到826MB,还有继续上涨的趋势。 请问代码还有哪里可能出现没有释放内存的地方呢
@xiaozhuBJZ: 缓慢上涨可能是new的对象没来得及gc,应该不会持续一直涨的.把需要的对象在构造函数里全部new完,需要时候复用,内存就不会怎么变化了.
你不应该在计时器里面一直去循环创建bitmap,而且每次都创建8张,它是你内存上涨的主要原因
我看了你的需求,感觉你不应该使用image,和bitmap,
而是直接用UserControl1_Paint里面的e.Graphics,直接绘制小圆点就行,控制一下每个小球的颜色
public void set()
{
for (int i = 0; i < 8; i++)
{
Draw();
Bitmap map = new Bitmap((bitmap[i]), new Size(250, 250));
images.Add(map);
}
pictureBox.Image = (Image)images[0];
pictureBox.Size = pictureBox.Image.Size;
}
额,我不知道你为啥需要在定时期里调用这个,这相当于个周期新增8个。 这个没必要。
传统游戏是一次生成8个,或者按规律一次生成一个,游戏上叫精灵图。如果只有8个,你只是定时器取模加载就好
如果是生成类似游戏精灵图的,只是按规则截取区域,下面我随便问问chatgpt,这是chatgpt的回复
public class SpriteAnimation : Form
{
private Bitmap spriteSheet; // 精灵图
private int currentFrame = 0; // 当前帧
private const int FRAME_COUNT = 8; // 总帧数
private int frameWidth; // 单帧宽度
private int frameHeight; // 单帧高度
private Timer animationTimer; // 动画计时器
public SpriteAnimation()
{
// 加载精灵图
spriteSheet = new Bitmap("spritesheet.png");
// 计算单帧尺寸
frameWidth = spriteSheet.Width / FRAME_COUNT;
frameHeight = spriteSheet.Height;
// 设置窗体
this.DoubleBuffered = true;
this.Size = new Size(frameWidth, frameHeight);
// 设置动画计时器
animationTimer = new Timer();
animationTimer.Interval = 100; // 动画间隔(毫秒)
animationTimer.Tick += AnimationTimer_Tick;
animationTimer.Start();
}
private void AnimationTimer_Tick(object sender, EventArgs e)
{
// 更新当前帧
currentFrame = (currentFrame + 1) % FRAME_COUNT;
this.Invalidate(); // 触发重绘
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
// 计算当前帧在精灵图上的位置
Rectangle sourceRect = new Rectangle(
currentFrame * frameWidth, // X坐标
0, // Y坐标
frameWidth, // 宽度
frameHeight // 高度
);
// 绘制当前帧
g.DrawImage(
spriteSheet,
new Rectangle(0, 0, frameWidth, frameHeight), // 目标区域
sourceRect, // 源区域
GraphicsUnit.Pixel
);
}
}
当然还有其他方式,就是直接gdi+进行局部的擦除和更新,我同样问问chatgpt,下面依旧是chatgpt的回复(我个人认为局部擦除和更新,可能要好于生成)
public class RotatingObject : Form
{
private float angle = 0;
private Timer animationTimer;
private Point center;
private Rectangle updateRect; // 需要更新的区域
// 用于双缓冲的缓存
private Bitmap backBuffer;
private Graphics backGraphics;
public RotatingObject()
{
this.DoubleBuffered = true;
this.Size = new Size(400, 400);
center = new Point(200, 200);
// 创建后台缓冲
backBuffer = new Bitmap(Width, Height);
backGraphics = Graphics.FromImage(backBuffer);
// 设置高质量绘图
backGraphics.SmoothingMode = SmoothingMode.AntiAlias;
backGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// 初始化定时器
animationTimer = new Timer();
animationTimer.Interval = 16; // 约60FPS
animationTimer.Tick += AnimationTimer_Tick;
animationTimer.Start();
}
private void AnimationTimer_Tick(object sender, EventArgs e)
{
// 计算需要更新的区域
updateRect = CalculateUpdateRect();
// 更新角度
angle = (angle + 1) % 360;
// 触发重绘
Invalidate(updateRect);
}
private Rectangle CalculateUpdateRect()
{
// 计算对象的边界框
int size = 100; // 对象大小
int padding = 10; // 额外的边距,防止抗锯齿效果被裁剪
// 计算旋转后的包围盒
Rectangle bounds = new Rectangle(
center.X - size/2 - padding,
center.Y - size/2 - padding,
size + padding * 2,
size + padding * 2
);
return bounds;
}
protected override void OnPaint(PaintEventArgs e)
{
// 清除上一帧
backGraphics.SetClip(updateRect);
backGraphics.Clear(BackColor);
// 绘制新帧
using (Matrix transform = new Matrix())
{
transform.RotateAt(angle, center);
backGraphics.Transform = transform;
// 绘制对象
using (Pen pen = new Pen(Color.Blue, 2))
using (Brush brush = new SolidBrush(Color.LightBlue))
{
Rectangle rect = new Rectangle(
center.X - 50,
center.Y - 50,
100,
100
);
backGraphics.FillRectangle(brush, rect);
backGraphics.DrawRectangle(pen, rect);
}
}
// 将后台缓冲复制到屏幕
e.Graphics.DrawImage(backBuffer, updateRect, updateRect, GraphicsUnit.Pixel);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
// 重新创建后台缓冲
if (backBuffer != null)
{
backBuffer.Dispose();
backGraphics.Dispose();
}
backBuffer = new Bitmap(Width, Height);
backGraphics = Graphics.FromImage(backBuffer);
backGraphics.SmoothingMode = SmoothingMode.AntiAlias;
backGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// 更新中心点
center = new Point(Width/2, Height/2);
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
// 清理资源
backBuffer?.Dispose();
backGraphics?.Dispose();
animationTimer?.Dispose();
}
}