java画监控面板

1722585790528.png

画面板 MonitorPanel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
package com.wwh.test.swing.monitor;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.swing.JPanel;

/**
* <pre>
* 有XY轴的曲线图监控面板
*
* <pre>
*
* @author 313921
* @date 2015-01-28 22:11:04
*/
public class MonitorPanel extends JPanel implements MonitorPanelModelListener {

/**
* 最合适的Y轴线条高度间距
*/
private static final int bastYAxisLineHeightInterval = 26;

/**
* X轴上默认的线与线之间的像素
*/
public static final int DEFAULT_X_AXIS_LINE_INTERVAL = 70;
/**
* 默认的当前值文本宽度
*/
private static final int defaultCurrentTextWidth = 50;

/**
* 最少的Y轴线条数量
*/
private static final int minYAxisLineCount = 3;

private static final long serialVersionUID = 1L;

/**
* 图片
*/
private BufferedImage bufferImage;

/**
* 显示当前值的文本宽度
*/
private int currentTextWidth = 50;

private MonitorPanelModel dataModel;

private SimpleDateFormat dateF = new SimpleDateFormat("HH:mm");

/**
* 是否显示最后一个值
*/
private boolean displayLastValue = true;

private boolean drawEmptyGraph = true;

private Color fontColor = Color.BLACK;

/**
* 曲线区域的背景颜色
*/
private Color graphBackgroundColor = new Color(0, 100, 0);

private Graphics2D graphics;

/**
* 曲线的颜色
*/
private Color graphLineColor = new Color(250, 0, 0);

private String graphTitle = "监控面板";

private Color gridLineColor = new Color(0, 0, 0);
/**
* 图片的高度
*/
private int imgHeight;

/**
* 图片的宽度,整个组件就是一个画上去的图片
*/
private int imgWidth;

/**
* 标题字体
*/
private Font titleFont = new Font("宋体", Font.BOLD, 14);

/**
* 标题颜色
*/
private Color titleFontColor = Color.BLACK;

/**
* 标题的高度
*/
private int titleHeight = 20;

/**
* X轴上的线与线之间的像素
*/
private int xAxisLineInterval = DEFAULT_X_AXIS_LINE_INTERVAL;

/**
* X轴上的一个与像素点的比例
*/
private double xRatio;

/**
* X轴的文本高度
*/
private int XTextHeight = 20;

/**
* Y轴上一个与像素点的比例
*/
private double yRatio;

/**
* Y轴的文本宽度
*/
private int YTextWidth = 40;

public MonitorPanel() {
this(null);
}

public MonitorPanel(MonitorPanelModel model) {
if (model == null) {
model = new MonitorPanelModel();
}
setModel(model);
updateUI();
}

/**
* <pre>
* 计算X轴的比例
* </pre>
*/
private void calcXRatio() {
Long maxXCoords = dataModel.getMaxXCoords();
if (maxXCoords == null) {
return;
}
Long minXCoords = dataModel.getMinXCoords();
if (minXCoords == null) {
return;
}
// 曲线图的宽度
int graphWidth = getGraphWidth();
if (graphWidth <= 0) {
xRatio = 0;
return;
}
long xv = maxXCoords - minXCoords;
// 得到比例
xRatio = (double) graphWidth / (double) xv;
}

/**
* <pre>
* 计算Y轴比例
* 既:值与显示区域的像素的比例
* </pre>
*/
private void calcYRatio() {
Long maxYCoords = dataModel.getMaxYCoords();
if (maxYCoords == null) {
return;
}
Long minYCoords = dataModel.getMinYCoords();
if (minYCoords == null) {
return;
}
// 计算曲线图的高度
int graphHeight = getGraphHeight();

if (graphHeight <= 0) {
yRatio = 0;
return;
}
long yd = maxYCoords - minYCoords;// Y轴
if (yd <= 0) {
yRatio = 0;
return;
}
yRatio = (double) graphHeight / (double) yd;// 得到比例
}

private void clearBufferImage() {
graphics.setBackground(getBackground());
graphics.clearRect(0, 0, imgWidth, imgHeight);
}

private void drawDefaultXAxis() {
int graphWidth = getGraphWidth();
int graphHeight = getGraphHeight();
// 每隔一定的像素画一条竖线
graphics.setColor(gridLineColor);

for (int i = YTextWidth + xAxisLineInterval; i < YTextWidth + graphWidth; i += xAxisLineInterval) {
graphics.drawLine(i, titleHeight, i, titleHeight + graphHeight);
}
}

private void drawDefaultYAxis() {
// 每隔一定的像素画一条线
int graphWidth = getGraphWidth();
int graphHeight = getGraphHeight();

// 画横线
graphics.setColor(gridLineColor);
for (int i = graphHeight; i - bastYAxisLineHeightInterval > 0; i -= bastYAxisLineHeightInterval) {
int yPosition = i - bastYAxisLineHeightInterval + titleHeight;
graphics.drawLine(YTextWidth, yPosition, YTextWidth + graphWidth, yPosition);
}
}

/**
* 绘制曲线
*/
private void drawGraph() {
graphics.setColor(graphLineColor);
List<Coordinate> coordinatelist = dataModel.getCoordinatelist();
int size = coordinatelist.size();
if (size < 2) {
return;
}
int[] xPoints = new int[size];
int[] yPoints = new int[size];

for (int i = 0; i < size; i++) {
xPoints[i] = getXPoint(coordinatelist.get(i).getxValue());
yPoints[i] = getYPoint(coordinatelist.get(i).getyValue());
}

graphics.drawPolyline(xPoints, yPoints, size);

// 画一个当前值
Font font = graphics.getFont();
FontMetrics fm = graphics.getFontMetrics(font);
int descent = fm.getDescent();
graphics.setColor(fontColor);
int _yPoint = yPoints[size - 1];
int graphWidth = getGraphWidth();
int _xPoint = YTextWidth + graphWidth + 1;
long currentValue = coordinatelist.get(coordinatelist.size() - 1).getyValue();
String valueOf = String.valueOf(currentValue);
graphics.drawString(valueOf, _xPoint, _yPoint - descent);
graphics.drawLine(_xPoint, _yPoint, _xPoint + fm.stringWidth(valueOf), _yPoint);
}

/**
* <pre>
* 画背景
* </pre>
*/
private void drawGraphBackgound() {
int graphWidth = getGraphWidth();
int graphHeight = getGraphHeight();

graphics.setColor(graphBackgroundColor);
// big.fill3DRect(YTextWidth, titleHeight, imgWidth - YTextWidth
// - cutlineWidth, imgHeight - titleHeight - XTextHeight, false);

// 填充指定的矩形。该矩形左边缘和右边缘分别位于 x 和 x + width - 1。上边缘和下边缘分别位于 y 和 y + height -
// 1。得到的矩形覆盖 width 像素宽乘以 height 像素高的区域。使用图形上下文的当前颜色填充该矩形。
graphics.fillRect(YTextWidth, titleHeight, graphWidth, graphHeight);

graphics.setColor(gridLineColor);
// 绘制指定矩形的边框。矩形的左边缘和右边缘分别位于 x 和 x + width。上边缘和下边缘分别位于 y 和 y +
// height。使用图形上下文的当前颜色绘制该矩形。
graphics.drawRect(YTextWidth, titleHeight, graphWidth, graphHeight);

}

/**
* <pre>
* 将内容画在图片上
* </pre>
*/
private void drawImage() {
int tw = getWidth();
int th = getHeight();
if (tw != imgWidth || th != imgHeight) {
// 大小有变化,重建一张图片
imgWidth = tw;
imgHeight = th;
bufferImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
graphics = bufferImage.createGraphics();
}
if (bufferImage == null) {
return;
}

// 先清空图片
clearBufferImage();

// 画标题
drawTitle();

// 画内容区域 背景
drawGraphBackgound();

// 获取XY轴上的线
long[] xLine = dataModel.getXAxisLines(getBestXAxisLineCount());
long[] yLine = dataModel.getYAxisLines(getBestYAxisLineCount());

// 计算X 和 Y 的比例
calcYRatio();
calcXRatio();

// 画X坐标
if (xLine != null) {
drawXAxis(xLine);
} else {
if (drawEmptyGraph) {
// 画缺省的X坐标
drawDefaultXAxis();
}
}

// 画Y坐标
if (yLine != null) {
drawYAxis(yLine);
} else if (drawEmptyGraph) {
// 画缺省的Y坐标
drawDefaultYAxis();
}

// 画曲线
drawGraph();
}

/**
* <pre>
* 画标题
* </pre>
*/
private void drawTitle() {
if (graphTitle == null || graphTitle.isEmpty() || titleHeight < 1) {
return;
}
graphics.setColor(titleFontColor);
graphics.setFont(titleFont);
FontMetrics fm = graphics.getFontMetrics(titleFont);
int titleWidth = fm.stringWidth(graphTitle);

int graphWidth = getGraphWidth();
int titleX = (graphWidth - titleWidth) / 2 + YTextWidth;
graphics.drawString(graphTitle, titleX, fm.getAscent());
}

/**
* <pre>
* 画X轴
* </pre>
*/
private void drawXAxis(long[] xLine) {
if (xLine == null) {
return;
}

Font font = graphics.getFont();
FontMetrics fm = graphics.getFontMetrics(font);
int ascent = fm.getAscent();
// int descent = fm.getDescent();
// int fHeight = fm.getHeight();

int graphHeight = getGraphHeight();

// 画X坐标
for (long l : xLine) {
int xPoint = getXPoint(l);

graphics.setColor(gridLineColor);
graphics.drawLine(xPoint, titleHeight, xPoint, titleHeight + graphHeight);

Date date = new Date(l);
String dateStr = dateF.format(date);

graphics.setColor(fontColor);
graphics.drawString(dateStr, xPoint, titleHeight + graphHeight + ascent);

}
}

/**
* <pre>
* 画Y轴
* </pre>
*/
private void drawYAxis(long[] yLine) {
if (yLine == null) {
return;
}

Font font = graphics.getFont();
FontMetrics fm = graphics.getFontMetrics(font);
int descent = fm.getDescent();
// 先设置字体
graphics.setFont(font);

// 画最大值
// String maxYStr = String.valueOf(maxY);
// big.drawString(maxYStr, YTextWidth - fm.stringWidth(maxYStr) - 1,
// titleHeight + descent);
// // 画最小值
// String minYStr = String.valueOf(minY);
// big.drawString(minYStr, YTextWidth - fm.stringWidth(minYStr) - 1,
// titleHeight + graphHeight);

int graphWidth = getGraphWidth();

for (long l : yLine) {
String yNumberStr = String.valueOf(l);
int yPosition = getYPoint(l);
if (YTextWidth > 0) {
// 数字
graphics.setColor(fontColor);
graphics.drawString(yNumberStr, YTextWidth - fm.stringWidth(yNumberStr) - 1, yPosition + descent);
}

// 画横线
graphics.setColor(gridLineColor);
graphics.drawLine(YTextWidth, yPosition, YTextWidth + graphWidth, yPosition);

}
}

private int getBestXAxisLineCount() {
// 在X轴能显示几条线
int lineCount = getGraphWidth() / xAxisLineInterval;
lineCount = lineCount > 1 ? lineCount : 1;
return lineCount;
}

/**
* 根据坐标系高度计算最佳的横线条数
*
* @return
*/
private int getBestYAxisLineCount() {
int lineCount = getGraphHeight() / bastYAxisLineHeightInterval;
lineCount = lineCount > minYAxisLineCount ? lineCount : minYAxisLineCount;
return lineCount;
}

public int getCurrentTextWidth() {
return currentTextWidth;
}

public boolean getDisplayLastValue() {
return displayLastValue;
}

public Color getFontColor() {
return fontColor;
}

public Color getGraphBackgroundColor() {
return graphBackgroundColor;
}

/**
* 获取曲线图区域的高度
*
* @return
*/
public int getGraphHeight() {
if (bufferImage == null) {
return 0;
}
return bufferImage.getHeight() - titleHeight - XTextHeight;
}

public Color getGraphLineColor() {
return graphLineColor;
}

public String getGraphTitle() {
return graphTitle;
}

/**
* 获取曲线图区域的宽度
*
* @return
*/
public int getGraphWidth() {
if (bufferImage == null) {
return 0;
}
return bufferImage.getWidth() - YTextWidth - currentTextWidth;
}

public Color getGridLineColor() {
return gridLineColor;
}

/**
* <pre>
* 获取值对应X点坐标
* </pre>
*
* @param value
* @return
*/
private int getXPoint(long value) {
Long minXCoords = dataModel.getMinXCoords();
long v = value - minXCoords;
int x = (int) (v * xRatio);
return x + YTextWidth;
}

/**
* <pre>
* 获取值对应Y点坐标
* </pre>
*
* @param value
* @return
*/
private int getYPoint(long value) {
Long minYCoords = dataModel.getMinYCoords();
long v = value - minYCoords;
int y = (int) (v * yRatio);
int graphHeight = getGraphHeight();
return graphHeight - y + titleHeight;
}

public boolean isDrawEmptyGraph() {
return drawEmptyGraph;
}

@Override
public void monitorPanelChanged(MonitorPanelModelEvent e) {
repaint();
}

@Override
public void paint(Graphics g) {
super.paint(g);

drawImage();
// 用图片
if (bufferImage != null) {
g.drawImage(bufferImage, 0, 0, this);
}
}

public void setCurrentTextWidth(int currentTextWidth) {
this.currentTextWidth = currentTextWidth;
}

public void setDisplayLastValue(boolean display) {
displayLastValue = display;
if (display) {
currentTextWidth = defaultCurrentTextWidth;
} else {
currentTextWidth = 0;
}
}

public void setDrawEmptyGraph(boolean drawEmptyGraph) {
this.drawEmptyGraph = drawEmptyGraph;
}

public void setFontColor(Color fontColor) {
this.fontColor = fontColor;
}

public void setGraphBackgroundColor(Color graphBackgroundColor) {
this.graphBackgroundColor = graphBackgroundColor;
}

public void setGraphLineColor(Color graphLineColor) {
this.graphLineColor = graphLineColor;
}

public void setGraphTitle(String title) {
this.graphTitle = title;
}

public void setGridLineColor(Color gridLineColor) {
this.gridLineColor = gridLineColor;
}

public void setModel(MonitorPanelModel model) {
if (model == null) {
throw new IllegalArgumentException("不能为空");
}
if (this.dataModel != model) {
MonitorPanelModel old = this.dataModel;
if (old != null) {
old.removeMonitorPanelModelListener(this);
}
this.dataModel = model;
model.addMonitorPanelModelListener(this);
repaint();
}
}

}

数据模型 MonitorPanelModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
package com.wwh.test.swing.monitor;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.event.EventListenerList;

// 应该抽象出接口
public class MonitorPanelModel implements Serializable {

public enum XAxisMode {
AUTO, FIXED, KEEP_MOVE
}

public enum YAxisMode {
AUTO, FIXED
}

/**
* 默认的最大坐标点的数量1000
*/
public static final int DEFAULT_MAX_COORDINATE_NUMBER = 1000;
private static final long serialVersionUID = 1L;
/**
* 坐标集合
*/
private List<Coordinate> coordinatelist;
/**
* 使X轴呈现一个向左边移动的效果<br>
* 需要指定一个时间段
*/
private Long keepMoveTimeInterval;
private EventListenerList listenerList = new EventListenerList();
/**
* 最大的坐标点个数
*/
private int maxCoordinateCount = DEFAULT_MAX_COORDINATE_NUMBER;
/**
* 最大X坐标
*/
private Long maxXCoords;
/**
* X轴上的最大值
*/
private Long maxXValue;
/**
* 最大Y坐标
*/
private Long maxYCoords;
/**
* Y轴上的最大值
*/
private Long maxYValue;
/**
* 最小X坐标
*/
private Long minXCoords;
/**
* X轴上的最小值
*/
private Long minXValue;
/**
* 最小Y坐标
*/
private Long minYCoords;

/**
* Y轴上的最小值
*/
private Long minYValue;

/**
* 记录一个开始的时间
*/
private Long startTime;

/**
* X 轴间距
*/
private Long xAxisInterval;

private XAxisMode xAxisMode = XAxisMode.AUTO;

/**
* Y轴间距
*/
private Long yAxisInterval;

private YAxisMode yAxisMode = YAxisMode.AUTO;

public MonitorPanelModel() {
coordinatelist = new ArrayList<Coordinate>();
}

public void addCoordinate(Coordinate coordinate) {
coordinatelist.add(coordinate);
if (coordinatelist.size() > maxCoordinateCount) {
coordinatelist.remove(0);
fireMonitorPanelDataChanged();
} else {
fireMonitorPanelDataAdd(coordinate);
}
}

public void addMonitorPanelModelListener(MonitorPanelModelListener l) {
listenerList.add(MonitorPanelModelListener.class, l);
}

/**
* 根据间隔计算Y轴上的线条数量
*
* @param interval
* @return
*/
private int calcLineCountByInterval(long interval) {
// 计算Y轴的最大值
long tmp = maxYValue / interval;
maxYCoords = tmp * interval;
if (maxYCoords < maxYValue) {
maxYCoords += interval;
}

tmp = minYValue / interval;
minYCoords = tmp * interval;
if (minYCoords > minYValue) {
minYCoords -= interval;
}

long line = (maxYCoords - minYCoords) / interval;

yAxisInterval = interval;

return (int) line;
}

public void clearCoordinate() {
coordinatelist.clear();
fireMonitorPanelDataChanged();
}

private void fireMonitorPanelChanged(MonitorPanelModelEvent e) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == MonitorPanelModelListener.class) {
((MonitorPanelModelListener) listeners[i + 1]).monitorPanelChanged(e);
}
}
}

private void fireMonitorPanelDataAdd(Coordinate coordinate) {
refreshValue(coordinate);
fireMonitorPanelChanged(new MonitorPanelModelEvent(this));
}

private void fireMonitorPanelDataChanged() {
// 刷新数据
refreshAllValue();
fireMonitorPanelChanged(new MonitorPanelModelEvent(this));
}

private void fireMonitorPanelPropertyChanged(String property, Object oldValue, Object newValue) {
// 属性先不管
fireMonitorPanelChanged(new MonitorPanelModelEvent(this));
}

private long[] getAutoXAxisLines(int bestXLineCount) {
if (maxXValue == null || minXValue == null) {
return null;
}
long differ = maxXValue - minXValue;
if (differ == 0) {
return null;
}
minXCoords = minXValue;
maxXCoords = maxXValue;
xAxisInterval = differ / bestXLineCount;
if (xAxisInterval < 1) {
return null;
}
List<Long> l = new ArrayList<>();
long tmp = maxXCoords;
while (tmp > minXCoords) {
l.add(tmp);
tmp -= xAxisInterval;
}
long[] ret = new long[l.size()];
for (int i = 0; i < ret.length; i++) {
ret[i] = l.get(i);
}
return ret;
}

private long[] getAutoYAxisLines(int bestYLineCount) {
if (maxYValue == null || minYValue == null) {
return null;
}
long differValue = maxYValue - minYValue;
if (differValue == 0) {
// 此时可能是一条横线
yAxisInterval = 1L;
long absMaxYValue = Math.abs(maxYValue);
if (absMaxYValue > 10) {
double log = Math.log10(absMaxYValue / 10);
// 四舍五入取整
log = Math.round(log);
// 间距
yAxisInterval = (long) Math.pow(10, log);
}
maxYCoords = ((maxYValue / yAxisInterval) + 1) * yAxisInterval;
minYCoords = ((maxYValue / yAxisInterval) - 1) * yAxisInterval;
long middleValue = (maxYValue / yAxisInterval) * yAxisInterval;
return new long[] { minYCoords, middleValue, maxYCoords };
}

long interval = 1;
if (differValue > 10) {
double log = Math.log10(differValue / 10);
// 四舍五入取整
log = Math.round(log);
// 间距
interval = (long) Math.pow(10, log);
}
readjustYAxis(interval, bestYLineCount);

// 返回Y轴上的坐标线
return getFixedYAxisLines();
}

public List<Coordinate> getCoordinatelist() {
return Collections.unmodifiableList(coordinatelist);
}

public int getCoordinateSize() {
return coordinatelist.size();
}

private long[] getFixedXAxisLines() {
if (maxXCoords == null || minXCoords == null || xAxisInterval == null) {
throw new IllegalArgumentException("固定模式下,最大值、最小值、间距都不能为空");
}
List<Long> l = new ArrayList<>();
long tmp = minXCoords;
while (tmp <= maxXCoords) {
l.add(tmp);
tmp += xAxisInterval;
}
long[] ret = new long[l.size()];
for (int i = 0; i < ret.length; i++) {
ret[i] = l.get(i);
}
return ret;
}

private long[] getFixedYAxisLines() {
if (maxYCoords == null || minYCoords == null || yAxisInterval == null) {
throw new IllegalArgumentException("固定模式下,最大值、最小值、间距都不能为空");
}
List<Long> l = new ArrayList<>();
long tmp = minYCoords;
while (tmp <= maxYCoords) {
l.add(tmp);
tmp += yAxisInterval;
}
long[] ret = new long[l.size()];
for (int i = 0; i < ret.length; i++) {
ret[i] = l.get(i);
}
return ret;
}

public Long getKeepMoveTimeInterval() {
return keepMoveTimeInterval;
}

private long[] getKeepMoveXAxisLines(int bestXLineCount) {
if (keepMoveTimeInterval == null || keepMoveTimeInterval < 1) {
throw new IllegalArgumentException("移动模式下,固定的时间段不能为空或小于1");
}
if (startTime == null) {
startTime = System.currentTimeMillis();
}
maxXCoords = System.currentTimeMillis();
minXCoords = maxXCoords - keepMoveTimeInterval;

xAxisInterval = keepMoveTimeInterval / bestXLineCount;
if (xAxisInterval < 1) {
return null;
}
List<Long> l = new ArrayList<>();
long tmp = startTime;
while (tmp <= maxXCoords) {
if (tmp >= minXCoords) {
l.add(tmp);
}
tmp += xAxisInterval;
}
long[] ret = new long[l.size()];
for (int i = 0; i < ret.length; i++) {
ret[i] = l.get(i);
}
return ret;
}

public int getMaxCoordinateCount() {
return maxCoordinateCount;
}

public Long getMaxXCoords() {
return maxXCoords;
}

public Long getMaxXValue() {
return maxXValue;
}

public Long getMaxYCoords() {
return maxYCoords;
}

public Long getMaxYValue() {
return maxYValue;
}

public Long getMinXCoords() {
return minXCoords;
}

public Long getMinXValue() {
return minXValue;
}

public Long getMinYCoords() {
return minYCoords;
}

public Long getMinYValue() {
return minYValue;
}

public MonitorPanelModelListener[] getMonitorPanelModelListeners() {
return listenerList.getListeners(MonitorPanelModelListener.class);
}

public long getXAxisFirstValue() {
if (coordinatelist.isEmpty()) {
return 0;
}
return coordinatelist.get(0).getxValue();
}

public Long getxAxisInterval() {
return xAxisInterval;
}

public long getXAxisLastValue() {
if (coordinatelist.isEmpty()) {
return 0;
}
return coordinatelist.get(coordinatelist.size() - 1).getxValue();
}

/**
* X 轴上要画的竖线
*
* @param bestXLineCount 最佳的竖线条数
* @return
*/
public long[] getXAxisLines(int bestXLineCount) {

switch (xAxisMode) {
case AUTO:
return getAutoXAxisLines(bestXLineCount);
case FIXED:
return getFixedXAxisLines();
case KEEP_MOVE:
return getKeepMoveXAxisLines(bestXLineCount);
default:
return null;
}
}

public XAxisMode getxAxisMode() {
return xAxisMode;
}

public Long getyAxisInterval() {
return yAxisInterval;
}

/**
* Y 轴上要画的横线
*
* @param bestYLineCount 最佳的横线条数
* @return
*/
public long[] getYAxisLines(int bestYLineCount) {
switch (yAxisMode) {
case AUTO:
return getAutoYAxisLines(bestYLineCount);
case FIXED:
return getFixedYAxisLines();
default:
return null;
}
}

public YAxisMode getyAxisMode() {
return yAxisMode;
}

/**
* 重新计算Y轴
*
* @param interval
*/
private void readjustYAxis(long interval, int bestYLineCount) {
// 先根据目前的间距计算一次Y轴的行数
int lineCount = calcLineCountByInterval(interval);

// 再尝试调整为最佳的
long newInterval = 0;
if (bestYLineCount > lineCount) {
// 减小间距
float f = (float) bestYLineCount / lineCount;
int p = Math.round(f);
if (p == 1) {
return;
} else {
for (int i = p; i > 1; i--) {
if (interval % i == 0) {
newInterval = interval / i;
break;
}
}
}
} else {
// 增加间距
float f = (float) lineCount / bestYLineCount;
int p = Math.round(f);
if (p == 1) {
return;
} else {
newInterval = interval * p;
}
}

if (newInterval != 0) {
// 根据调整后的间距再算一次
lineCount = calcLineCountByInterval(newInterval);
}
}

private void refreshAllValue() {
minXValue = null;
maxXValue = null;
minYValue = null;
maxYValue = null;
for (Coordinate coordinate : coordinatelist) {
refreshValue(coordinate);
}
}

private void refreshValue(Coordinate coordinate) {
long y = coordinate.getyValue();
long x = coordinate.getxValue();
if (maxYValue == null || y > maxYValue) {
maxYValue = y;
}
if (minYValue == null || y < minYValue) {
minYValue = y;
}
if (maxXValue == null || x > maxXValue) {
maxXValue = x;
}
if (minXValue == null || x < minXValue) {
minXValue = x;
}
}

public void removeMonitorPanelModelListener(MonitorPanelModelListener l) {
listenerList.remove(MonitorPanelModelListener.class, l);
}

public void setCoordinatelist(List<Coordinate> coordinatelist) {
if (coordinatelist == null) {
throw new IllegalArgumentException("不能为空");
}
this.coordinatelist = coordinatelist;
fireMonitorPanelDataChanged();
}

public void setKeepMoveTimeInterval(Long keepMoveTimeInterval) {
fireMonitorPanelPropertyChanged(null, null, null);
this.keepMoveTimeInterval = keepMoveTimeInterval;
}

public void setMaxCoordinateCount(int maxCoordinateCount) {
fireMonitorPanelPropertyChanged(null, null, null);
this.maxCoordinateCount = maxCoordinateCount;
}

public void setMaxXCoords(Long maxXCoords) {
fireMonitorPanelPropertyChanged(null, null, null);
this.maxXCoords = maxXCoords;
}

public void setMaxYCoords(Long maxYCoords) {
fireMonitorPanelPropertyChanged(null, null, null);
this.maxYCoords = maxYCoords;
}

public void setMinXCoords(Long minXCoords) {
fireMonitorPanelPropertyChanged(null, null, null);
this.minXCoords = minXCoords;
}

public void setMinYCoords(Long minYCoords) {
fireMonitorPanelPropertyChanged(null, null, null);
this.minYCoords = minYCoords;
}

public void setxAxisInterval(Long xAxisInterval) {
fireMonitorPanelPropertyChanged(null, null, null);
this.xAxisInterval = xAxisInterval;
}

public void setxAxisMode(XAxisMode xAxisMode) {
fireMonitorPanelPropertyChanged(null, null, null);
this.xAxisMode = xAxisMode;
}

public void setyAxisInterval(Long yAxisInterval) {
fireMonitorPanelPropertyChanged(null, null, null);
this.yAxisInterval = yAxisInterval;
}

public void setyAxisMode(YAxisMode yAxisMode) {
fireMonitorPanelPropertyChanged(null, null, null);
this.yAxisMode = yAxisMode;
}

}

坐标 Coordinate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.wwh.test.swing.monitor;

/**
* <pre>
* xy坐标
* X轴 为 具体的值
* Y轴 为 时间
* </pre>
*
* @author wwh
*/
public class Coordinate {

private long xValue;
private long yValue;

public Coordinate(long x, long y) {
this.xValue = x;
this.yValue = y;
}

public long getxValue() {
return xValue;
}

public void setxValue(long xValue) {
this.xValue = xValue;
}

public long getyValue() {
return yValue;
}

public void setyValue(long yValue) {
this.yValue = yValue;
}

}

事件 MonitorPanelModelEvent

1
2
3
4
5
6
7
8
9
10
11
package com.wwh.test.swing.monitor;

public class MonitorPanelModelEvent extends java.util.EventObject {
private static final long serialVersionUID = 1L;

public MonitorPanelModelEvent(Object source) {
super(source);
}

}

监听器接口 MonitorPanelModelListener

1
2
3
4
5
6
7
package com.wwh.test.swing.monitor;

public interface MonitorPanelModelListener extends java.util.EventListener {

public void monitorPanelChanged(MonitorPanelModelEvent e);
}

测试 MonitorPanelTest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
package com.wwh.test.swing.monitor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.wwh.test.swing.monitor.MonitorPanelModel.XAxisMode;
import com.wwh.test.swing.monitor.MonitorPanelModel.YAxisMode;

public class MonitorPanelTest extends JFrame {

private static final long serialVersionUID = 1L;

/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MonitorPanelTest frame = new MonitorPanelTest();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

private JButton btn_clear1;
private JButton btn_clear2;
private JButton btn_clear3;
private JButton btn_clear4;

private JButton btn_start2;
private JButton btn_start3;
private JButton btn_start4;

private JButton btn_stop1;
private JButton btn_stop2;
private JButton btn_stop3;
private JButton btn_stop4;

private JPanel contentPane;
private MonitorPanel mPanel1;
private MonitorPanel mPanel2;
private MonitorPanel mPanel3;
private MonitorPanel mPanel4;

private MonitorPanelModel mpModel1;
private MonitorPanelModel mpModel2;
private MonitorPanelModel mpModel3;
private MonitorPanelModel mpModel4;
private JPanel panel_1;
private JPanel panel_2;
private JPanel panel_2_btn;
private JPanel panel_3;
private JPanel panel_3_btn;
private JPanel panel_4;
private JPanel panel_4_btn;
private boolean runFlag1 = false;
private boolean runFlag2 = false;
private boolean runFlag3 = false;
private boolean runFlag4 = false;

/**
* Create the frame.
*/
public MonitorPanelTest() {
setTitle("监控面板测试");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 1183, 681);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new GridLayout(2, 2, 0, 0));

createMonitorPanel1();

createMonitorPanel2();

createMonitorPanel3();

createMonitorPanel4();

}

private void createMonitorPanel1() {
panel_1 = new JPanel();
contentPane.add(panel_1);
panel_1.setLayout(new BorderLayout(0, 0));
mpModel1 = new MonitorPanelModel();
mPanel1 = new MonitorPanel(mpModel1);
panel_1.add(mPanel1);

mpModel1.setMaxCoordinateCount(200);

mPanel1.setGraphTitle("0到10000之间的随机数");

JPanel panel_1_btn = new JPanel();
panel_1.add(panel_1_btn, BorderLayout.SOUTH);

JButton btn_start1 = new JButton("启 动");
btn_start1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start1();
}
});
panel_1_btn.add(btn_start1);

btn_stop1 = new JButton("停 止");
btn_stop1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runFlag1 = false;
}
});
panel_1_btn.add(btn_stop1);

btn_clear1 = new JButton("清 空");
btn_clear1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mpModel1.clearCoordinate();
}
});
panel_1_btn.add(btn_clear1);
}

private void createMonitorPanel2() {
panel_2 = new JPanel();
contentPane.add(panel_2);
panel_2.setLayout(new BorderLayout(0, 0));

mpModel2 = new MonitorPanelModel();
mpModel2.setxAxisMode(XAxisMode.KEEP_MOVE);
mpModel2.setKeepMoveTimeInterval(10 * 60 * 1000L);

mpModel2.setMaxCoordinateCount(600);

mPanel2 = new MonitorPanel(mpModel2);

mPanel2.setGraphTitle("内存图测试");
mPanel2.setGraphBackgroundColor(new Color(38, 38, 38));
mPanel2.setGridLineColor(new Color(0, 225, 34));
mPanel2.setGraphLineColor(Color.YELLOW);

panel_2.add(mPanel2);

panel_2_btn = new JPanel();
panel_2.add(panel_2_btn, BorderLayout.SOUTH);

btn_start2 = new JButton("启 动");
btn_start2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start2();
}
});
panel_2_btn.add(btn_start2);

btn_stop2 = new JButton("停 止");
btn_stop2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runFlag2 = false;
}
});
panel_2_btn.add(btn_stop2);

btn_clear2 = new JButton("清 空");
btn_clear2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mpModel2.clearCoordinate();
}
});
panel_2_btn.add(btn_clear2);
}

private void createMonitorPanel3() {
panel_3 = new JPanel();
contentPane.add(panel_3);
panel_3.setLayout(new BorderLayout(0, 0));

mpModel3 = new MonitorPanelModel();
mpModel3.setxAxisMode(XAxisMode.KEEP_MOVE);
mpModel3.setKeepMoveTimeInterval(10 * 60 * 1000L);
mpModel3.setMaxCoordinateCount(1200);

mPanel3 = new MonitorPanel(mpModel3);
mPanel3.setGraphTitle("递增递减");

panel_3.add(mPanel3, BorderLayout.CENTER);

panel_3_btn = new JPanel();
panel_3.add(panel_3_btn, BorderLayout.SOUTH);

btn_start3 = new JButton("启 动");
btn_start3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start3();
}
});
panel_3_btn.add(btn_start3);

btn_stop3 = new JButton("停 止");
btn_stop3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runFlag3 = false;
}
});
panel_3_btn.add(btn_stop3);

btn_clear3 = new JButton("清 空");
btn_clear3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mpModel3.clearCoordinate();
}
});
panel_3_btn.add(btn_clear3);

}

private void createMonitorPanel4() {

panel_4 = new JPanel();
contentPane.add(panel_4);
panel_4.setLayout(new BorderLayout(0, 0));

mpModel4 = new MonitorPanelModel();
mpModel4.setxAxisMode(XAxisMode.KEEP_MOVE);
mpModel4.setKeepMoveTimeInterval(360 * 1000L);

mpModel4.setMaxCoordinateCount(720);

mpModel4.setyAxisMode(YAxisMode.FIXED);
mpModel4.setMaxYCoords(100L);
mpModel4.setMinYCoords(-100L);
mpModel4.setyAxisInterval(20L);

mPanel4 = new MonitorPanel(mpModel4);
panel_4.add(mPanel4, BorderLayout.CENTER);

mPanel4.setGraphTitle("正弦函数");
mPanel4.setGraphBackgroundColor(new Color(38, 38, 38));
mPanel4.setGridLineColor(new Color(0, 225, 34));
mPanel4.setGraphLineColor(Color.YELLOW);

panel_4_btn = new JPanel();
panel_4.add(panel_4_btn, BorderLayout.SOUTH);

btn_start4 = new JButton("启 动");
btn_start4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start4();
}
});
panel_4_btn.add(btn_start4);

btn_stop4 = new JButton("停 止");
btn_stop4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runFlag4 = false;
}
});
panel_4_btn.add(btn_stop4);

btn_clear4 = new JButton("清 空");
btn_clear4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
mpModel4.clearCoordinate();
}
});
panel_4_btn.add(btn_clear4);

}

private void start1() {
runFlag1 = true;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
int rint;
Random r = new Random();
while (runFlag1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
rint = r.nextInt(10000);
mpModel1.addCoordinate(new Coordinate(System.currentTimeMillis(), rint + 1));
}
}
});
t.start();
}

private void start2() {
if (runFlag2) {
return;
}
runFlag2 = true;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Runtime rt = Runtime.getRuntime();

while (runFlag2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mpModel2.addCoordinate(new Coordinate(System.currentTimeMillis(), rt.freeMemory() / 1024 / 1024));
}
}
});
t.start();
}

private void start3() {

if (runFlag3) {
return;
}
runFlag3 = true;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
boolean b = true;
int i = 0;
while (runFlag3) {
if (b) {
if (i < 100) {
i++;
} else {
b = !b;
}
} else {
if (i > 0) {
i--;
} else {
b = !b;
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
mpModel3.addCoordinate(new Coordinate(System.currentTimeMillis(), i));
}
}
});
t.start();

}

private void start4() {
if (runFlag4) {
return;
}
runFlag4 = true;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
double d;
long l;
while (runFlag4) {
if (i == 360) {
i = 0;
}
d = Math.sin(Math.toRadians(i));
i++;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
l = (long) (d * 100);
mpModel4.addCoordinate(new Coordinate(System.currentTimeMillis(), l));
}
}
});
t.start();
}
}