1. /**
  2. * <p>Copyright: Copyright (c) 2002-2004</p>
  3. * <p>Company: JavaResearch(http://www.javaresearch.org)</p>
  4. * <p>最后更新日期:2003年4月30日
  5. * @author cherami
  6. */
  7. package org.jr.swing;
  8. import javax.swing.*;
  9. import java.awt.*;
  10. import java.awt.event.*;
  11. import java.util.Random;
  12. /**
  13. * 走马灯。
  14. * 以指定的时间间隔、步长以及滚动方向、滚动模式以及顺序显示文字。
  15. * 目前可以以循环、反弹以及随机模式滚动;方向则支持水平滚动和垂直滚动,顺序则支持升序和倒序。
  16. * 需要说明的还有步长,它可以为负值,因此如果滚动模式为循环、顺序为升序而步长为负数,则实际的效果应该是倒序。
  17. * @since 0.6
  18. */
  19. public class HorseLight
  20. extends JLabel {
  21. /**
  22. * 滚动方向,如果是水平滚动则表示从左到右,如果是垂直滚动则表示从上到下。
  23. */
  24. public static final int ASCENT = 1;
  25. /**
  26. * 滚动方向,如果是水平滚动则表示从右到左,如果是垂直滚动则表示从下到上。
  27. */
  28. public static final int REVERSE = -1;
  29. /**
  30. * 停止滚动。可以用于滚动方向也可以用于滚动模式。
  31. * 在此状态下虽然并没有变化,但是在指定的时间间隔中组件还是会进行重新绘制。
  32. */
  33. public static final int HALT = 0;
  34. /**
  35. * 循环滚动模式。
  36. */
  37. public static final int CIRCLE = 1;
  38. /**
  39. * 反弹滚动模式。<b>在反弹滚动模式下滚动方向没有意义。</b>
  40. */
  41. public static final int REFLECT = 2;
  42. /**
  43. * 随机滚动模式。<b>在随机滚动模式下滚动方向没有意义。</b>
  44. */
  45. public static final int RANDOM = 8;
  46. private int endX, endY; //终点坐标
  47. int spaceInterval; //行走间距
  48. int interval; //刷新速度
  49. String text; //显示文本
  50. private int currentX, currentY; //当前位置
  51. int order = ASCENT; //变化顺序
  52. int model = CIRCLE; //滚动模式
  53. int direction = HORIZONTAL; //滚动方向
  54. int scope; //滚动方向的长度
  55. Timer timer; //定时器
  56. private int directionFixer = 1;
  57. private static final String TEXT = "走马灯";
  58. private static final int INTERVAL = 100;
  59. private static final int SPACE_INTERVAL = 5;
  60. /**
  61. * 构造一个缺省的HorseLight。
  62. */
  63. public HorseLight() {
  64. this(TEXT, SPACE_INTERVAL, INTERVAL, -1);
  65. }
  66. /**
  67. * 以指定的文本构造一个HorseLight。
  68. * @param text 显示文本
  69. */
  70. public HorseLight(String text) {
  71. this(text, SPACE_INTERVAL, INTERVAL, -1);
  72. }
  73. /**
  74. * 以指定的文本以及时间间隔构造一个HorseLight。
  75. * @param text 显示文本
  76. * @param interval 时间间隔
  77. */
  78. public HorseLight(String text, int interval) {
  79. this(text, SPACE_INTERVAL, interval, -1);
  80. }
  81. /**
  82. * 以指定的文本以及时间间隔构造一个HorseLight。
  83. * @param text 显示文本
  84. * @param spaceInterval 间距
  85. * @param interval 时间间隔
  86. */
  87. public HorseLight(String text, int spaceInterval, int interval) {
  88. this(text, spaceInterval, interval, -1);
  89. }
  90. /**
  91. * 以指定的文本、间距以及时间间隔构造一个HorseLight。
  92. * @param text 显示文本
  93. * @param spaceInterval 间距
  94. * @param interval 时间间隔
  95. * @param scope 滚动范围
  96. */
  97. public HorseLight(String text, int spaceInterval, int interval, int scope) {
  98. this.text = text;
  99. this.spaceInterval = spaceInterval;
  100. if (interval > 0) {
  101. this.interval = interval;
  102. }
  103. else {
  104. this.interval = INTERVAL;
  105. }
  106. if (scope > 0) {
  107. this.scope = scope;
  108. }
  109. else {
  110. this.scope = -1;
  111. }
  112. currentX = 0;
  113. currentY = 0;
  114. timer = new Timer(interval, new ActionListener() {
  115. public void actionPerformed(ActionEvent e) {
  116. repaint();
  117. }
  118. });
  119. timer.start();
  120. }
  121. /**
  122. * 设置循环模式。
  123. * 运动位置会恢复到从最开始开始。
  124. * @param model 滚动模式
  125. */
  126. public void setModel(int model) {
  127. currentX = 0;
  128. currentY = 0;
  129. this.model = model;
  130. }
  131. /**
  132. * 设置显示文本。
  133. * 运动位置会恢复到从最开始开始。
  134. * @param text 文本
  135. */
  136. public void setText(String text) {
  137. currentX = 0;
  138. currentY = 0;
  139. this.text = text;
  140. }
  141. /**
  142. * 设置滚动的时候间隔。
  143. * @param interval 时间间隔
  144. */
  145. public void setInterval(int interval) {
  146. if (interval > 0) {
  147. this.interval = interval;
  148. timer.setDelay(interval);
  149. }
  150. }
  151. /**
  152. * 设置滚动的间距。
  153. * @param spaceInterval 间距
  154. */
  155. public void setSpaceInterval(int spaceInterval) {
  156. this.spaceInterval = spaceInterval;
  157. }
  158. /**
  159. * 设置滚动的方向。
  160. * 运动位置会恢复到从最开始开始。
  161. * @param direction 滚动方向
  162. */
  163. public void setDirection(int direction) {
  164. currentX = 0;
  165. currentY = 0;
  166. this.direction = direction;
  167. }
  168. /**
  169. * 设置滚动顺序。
  170. * @param order 滚动顺序
  171. */
  172. public void setOrder(int order) {
  173. this.order = order;
  174. }
  175. /**
  176. * 得到组件的最佳大小。
  177. * 如果滚动方向是水平滚动而又没有设置滚动范围则最佳宽度为文本的宽度加上步长的五倍。
  178. * 如果滚动方向是垂直滚动而又没有设置滚动范围则最佳高度为文本的高度加上步长的五倍。
  179. * @return 组件的最佳大小
  180. */
  181. public Dimension getPreferredSize() {
  182. Graphics g = getGraphics();
  183. FontMetrics fontMetrics = g.getFontMetrics();
  184. int height = fontMetrics.getHeight() + 8;
  185. int width = fontMetrics.stringWidth(text) + 10;
  186. switch (direction) {
  187. case HORIZONTAL:
  188. if (scope > 0) {
  189. return new Dimension(scope, height);
  190. }
  191. else {
  192. return new Dimension(width + spaceInterval * 5, height);
  193. }
  194. case VERTICAL:
  195. if (scope > 0) {
  196. return new Dimension(width, scope);
  197. }
  198. else {
  199. return new Dimension(width, height + spaceInterval * 5);
  200. }
  201. default:
  202. if (scope > 0) {
  203. return new Dimension(scope, height);
  204. }
  205. else {
  206. return new Dimension(width + spaceInterval * 5, height);
  207. }
  208. }
  209. }
  210. /**
  211. * 绘制组件。
  212. * @param g 图形设备
  213. */
  214. protected void paintComponent(Graphics g) {
  215. int width, height;
  216. width = getSize().width;
  217. height = getSize().height;
  218. FontMetrics fontMetrics = g.getFontMetrics();
  219. int fontHeight = fontMetrics.getHeight();
  220. int textWidth = fontMetrics.stringWidth(text);
  221. g.drawString(text, currentX, currentY);
  222. switch (direction) {
  223. case HORIZONTAL:
  224. endX = width - textWidth;
  225. break;
  226. case VERTICAL:
  227. endY = height;
  228. break;
  229. default:
  230. endX = width - textWidth;
  231. break;
  232. }
  233. setNextPosition();
  234. }
  235. /**
  236. * 设置下一次绘制的位置。
  237. */
  238. private void setNextPosition() {
  239. switch (model) {
  240. case HALT:
  241. return;
  242. case CIRCLE:
  243. switch (direction) {
  244. case HORIZONTAL:
  245. switch (order) {
  246. case HALT:
  247. return;
  248. case ASCENT:
  249. currentX += spaceInterval;
  250. if (currentX > endX || currentX < 0) {
  251. currentX = 0;
  252. }
  253. return;
  254. case REVERSE:
  255. currentX -= spaceInterval;
  256. if (currentX > endX || currentX < 0) {
  257. currentX = endX;
  258. }
  259. return;
  260. default:
  261. currentX += spaceInterval;
  262. if (currentX > endX || currentX < 0) {
  263. currentX = 0;
  264. }
  265. return;
  266. }
  267. case VERTICAL:
  268. switch (order) {
  269. case HALT:
  270. return;
  271. case ASCENT:
  272. currentY += spaceInterval;
  273. if (currentY > endY || currentY < 0) {
  274. currentY = 0;
  275. }
  276. System.out.println(spaceInterval + ",currentY:" + currentY +
  277. ",endY:" + endY);
  278. return;
  279. case REVERSE:
  280. currentY -= spaceInterval;
  281. if (currentY > endY || currentY < 0) {
  282. currentY = endY;
  283. }
  284. return;
  285. default:
  286. currentY += spaceInterval;
  287. if (currentY > endY || currentY < 0) {
  288. currentY = 0;
  289. }
  290. return;
  291. }
  292. default:
  293. switch (order) {
  294. case HALT:
  295. return;
  296. case ASCENT:
  297. currentX += spaceInterval;
  298. if (currentX > endX || currentX < 0) {
  299. currentX = 0;
  300. }
  301. return;
  302. case REVERSE:
  303. currentX -= spaceInterval;
  304. if (currentX > endX || currentX < 0) {
  305. currentX = endX;
  306. }
  307. return;
  308. default:
  309. currentX += spaceInterval;
  310. if (currentX > endX || currentX < 0) {
  311. currentX = 0;
  312. }
  313. return;
  314. }
  315. }
  316. case REFLECT:
  317. switch (direction) {
  318. case HORIZONTAL:
  319. currentX += (directionFixer * spaceInterval);
  320. if (currentX > endX || currentX < 0) {
  321. directionFixer = -directionFixer;
  322. }
  323. return;
  324. case VERTICAL:
  325. currentY += (directionFixer * spaceInterval);
  326. if (currentY > endY || currentY < 0) {
  327. directionFixer = -directionFixer;
  328. }
  329. return;
  330. default:
  331. currentX += (directionFixer * spaceInterval);
  332. if (currentX > endX || currentX < 0) {
  333. directionFixer = -directionFixer;
  334. }
  335. return;
  336. }
  337. case RANDOM:
  338. Random random = new Random();
  339. switch (direction) {
  340. case HORIZONTAL:
  341. currentX = random.nextInt(endX);
  342. return;
  343. case VERTICAL:
  344. currentY = random.nextInt(endY);
  345. return;
  346. default:
  347. currentX = random.nextInt(endX);
  348. return;
  349. }
  350. default:
  351. switch (direction) {
  352. case HORIZONTAL:
  353. switch (order) {
  354. case HALT:
  355. return;
  356. case ASCENT:
  357. currentX += spaceInterval;
  358. if (currentX > endX || currentX < 0) {
  359. currentX = 0;
  360. }
  361. return;
  362. case REVERSE:
  363. currentX -= spaceInterval;
  364. if (currentX > endX || currentX < 0) {
  365. currentX = endX;
  366. }
  367. return;
  368. default:
  369. currentX += spaceInterval;
  370. if (currentX > endX || currentX < 0) {
  371. currentX = 0;
  372. }
  373. return;
  374. }
  375. case VERTICAL:
  376. switch (order) {
  377. case HALT:
  378. return;
  379. case ASCENT:
  380. currentY += spaceInterval;
  381. if (currentY > endY || currentY < 0) {
  382. currentY = 0;
  383. }
  384. System.out.println(spaceInterval + ",currentY:" + currentY +
  385. ",endY:" + endY);
  386. return;
  387. case REVERSE:
  388. currentY -= spaceInterval;
  389. if (currentY > endY || currentY < 0) {
  390. currentY = endY;
  391. }
  392. return;
  393. default:
  394. currentY += spaceInterval;
  395. if (currentY > endY || currentY < 0) {
  396. currentY = 0;
  397. }
  398. return;
  399. }
  400. default:
  401. switch (order) {
  402. case HALT:
  403. return;
  404. case ASCENT:
  405. currentX += spaceInterval;
  406. if (currentX > endX || currentX < 0) {
  407. currentX = 0;
  408. }
  409. return;
  410. case REVERSE:
  411. currentX -= spaceInterval;
  412. if (currentX > endX || currentX < 0) {
  413. currentX = endX;
  414. }
  415. return;
  416. default:
  417. currentX += spaceInterval;
  418. if (currentX > endX || currentX < 0) {
  419. currentX = 0;
  420. }
  421. return;
  422. }
  423. }
  424. }
  425. }
  426. /**
  427. * 重新开始运动显示。
  428. */
  429. public void start() {
  430. currentX = 0;
  431. currentY = 0;
  432. timer.start();
  433. }
  434. /**
  435. * 停止运动。
  436. */
  437. public void stop() {
  438. timer.stop();
  439. }
  440. }