您的位置:首页 > 其它

Swing:高性能控件——MessagePane,可用于显示系列信息

2012-06-09 02:04 399 查看
这一个自上而下依条显示信息的控件

最新的信息显示在最上面一条

正常情况下,刷新数据时,CPU 占用率极低

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class MessagePane<T> extends JComponent {

/**
* 允许最大数据行数
*/
private static final int MAXLINE = 10000;
/**
* 数据队列
*/
private LinkedList<T> datas = new LinkedList<T>();
/**
* 本次绘制,需要绘制的行数,正数向下偏移,负数向上偏移
*/
private int add = 0;
/**
* 每行数据的绘制高度
*/
private int lineH = 1;
/**
* 滚动条
*/
private JScrollBar bar = new JScrollBar(JScrollBar.VERTICAL);
/**
* 滚动条当前值
*/
private int barValue = 0;
/**
* 绘制任务
*/
private Runnable paintRunnable = new Runnable() {

@Override
public void run() {
add = 1;
paintImmediately(0, 0, getWidth(), getHeight());
add = 0;
}
};
/**
* 滚动条更新任务
*/
private Runnable barRunnable = new Runnable() {

@Override
public void run() {
bar.setValues(barValue, getHeight() / lineH, 0,
datas.size() + 1);
}
};

/**
* 构造一个新的 MessagePane
*/
public MessagePane() {
initialize();
}

/**
* 初始化
*/
private void initialize() {
setOpaque(true);
setBackground(Color.WHITE);
setLayout(new BorderLayout());
add(bar, BorderLayout.EAST);
bar.addAdjustmentListener(new AdjustmentListener() {

@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
int newValue = bar.getValue();
if (newValue != barValue) {
add = barValue - newValue;
barValue = newValue;
MessagePane.this.paintImmediately(0, 0,
MessagePane.this.getWidth(),
MessagePane.this.getHeight());
add = 0;
}
}
});
addComponentListener(new ComponentAdapter() {

@Override
public void componentResized(ComponentEvent e) {
//控件大小变化时,更新滚动条
updateBar();
}
});
setFont(UIManager.getFont("Panel.font"));
lineH = getFontMetrics(getFont()).getHeight() + 2;
updateBar();
}

@Override
public void setFont(Font font) {
super.setFont(font);
if (font != null) {
lineH = getFontMetrics(font).getHeight() + 2;
}
}

/**
* 根据当前数据量,更新滚动条属性
*/
private void updateBar() {
if (SwingUtilities.isEventDispatchThread()) {
barRunnable.run();
} else {
SwingUtilities.invokeLater(barRunnable);
}
}

/**
* 标准的添加数据接口,在事件指派线程中调用该方法,可以获得最大的性能优化
*
* @param data - 数据
*/
public synchronized void addData(T data) {
datas.addFirst(data);
if (datas.size() > MAXLINE) {
//数据超出限定范围时,移除最旧的一条数据
datas.pollLast();
} else {
//数据长度变化时,更新滚动条
updateBar();
}
if (SwingUtilities.isEventDispatchThread()) {
paintRunnable.run();
} else {
try {
SwingUtilities.invokeAndWait(paintRunnable);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

/**
* 标准的清除数据接口
*/
public synchronized void clearDate() {
datas.clear();
//清除数据后,滚动条值归零
barValue = 0;
repaint();
//清除数据后,更新滚动条
updateBar();
}

/**
* 获得真实的绘制宽度(减去滚动条宽度)
*
* @return
*/
private int getPaintWidth() {
return getWidth() - bar.getWidth();
}

@Override
protected void paintComponent(Graphics g) {
//当调用 repaint 进入该方法时,add 为 0 ,会将整个控件完整绘制一遍。
//而当设置了 add 然后调用 paintImmediately 进入该方法时,
//会进行 copyArea,然后只绘制 copy 后的脏区域。
int w = getPaintWidth();
int h = getHeight();
if (add > 0) {
int move = lineH * add;
//add 大于 0 表示需要向下整体位移,因此向下 copy
//重新设置 clip,限制需要绘制的区域
g.copyArea(0, 0, w, h - move, 0, move);
g.setClip(0, 0, w, move);
} else if (add < 0) {
int move = -lineH * add;
//add 小于 0 表示需要向上整体位移,因此向上 copy
//重新设置 clip,限制需要绘制的区域
g.copyArea(0, move, w, h - move, 0, -move);
g.setClip(0, h - move, w, move);
}
Rectangle clip = g.getClipBounds();
int clipY = clip.y;
int clipX = clip.x;
int clipH = clip.height;
int y = 0;
g.setColor(getBackground());
g.fillRect(clipX, clipY, w, clipH);
for (int i = barValue; i < datas.size(); i++) {
y += lineH;
if (y < clipY) {
//小于绘制区域上沿时,不需要绘制该条数据
continue;
}
if (y - lineH > clipY + clipH) {
//由于是从上往下绘制,因此大于绘制区域下沿时,终止绘制
break;
}
g.setColor(getForeground());
g.drawString(datas.get(i).toString(), 0, y);
}
}

static long show = 0;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
JFrame frame = new JFrame();
final MessagePane<Long> messagePane = new MessagePane<Long>();
frame.setContentPane(messagePane);
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Runnable runnable = new Runnable() {

@Override
public void run() {
while (true) {
messagePane.addData(show);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
show++;
}
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
});

}

}


运行效果图:



测试用例中,同时起了 4 个线程,刷新数据

在 sleep(10) 并且全屏的模式下,CPU 占用率依然接近于 0

有 BUG 可以报告给我
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: