黑马程序员技术交流社区

标题: 张老师GUI视频教程留的练习题—Stopwatch [打印本页]

作者: 鲁柯    时间: 2013-1-30 10:38
标题: 张老师GUI视频教程留的练习题—Stopwatch
本帖最后由 鲁柯 于 2013-1-30 10:40 编辑

张老师Java高级教程GUI部分视频教程最后留的练习题,费了很长时间,功能是实现了,但应该有其他简单的方式吧,请知道的补充一下,谢谢。
5、 为前边讲的自定义计时器组件增加功能:允许程序设置计时器显示文本的颜色,
        时间文本的字体大小随组件大小的改变而改变。
  
  * 思路:
* 自定义一个组件类实现题目要求的功能
*  a 要设置文本颜色,需要提供一个设置方法,可以用列表选择框或单选按钮等来实现
*   这里用一组单选按钮实现,将它们放在一个单独的Panel里便于管理
*  b 要让字体大小随组件大小的改变而改变,根据张老师视频中的方法就需要不断调整填充区域的大小
*   这里换一种方法,用布局管理器来实现大小的自动调整,所以要用一个组件来实现计时器功能
*  c 要将计时器组件和一组单选按钮Panel放在同一个组件里边实现题目要求的功能,
*   为了方便将类设计为Panel的子类,就可以随意添加组件了
*  d  要处理一组单选框按钮产生的事件,为了集中管理,让自定义的类实现ItemListener接口
*   组件重绘过程中需要不断刷新时间值,需要一个单独的线程来执行这个任务,所以让类
*   实现Runnable接口,让功能更加集中
*
* 已经实现的功能:
*   计时功能已实现,在指定区域内(单选按钮左边的区域)按下鼠标开始计时,松开结束
*   动态显示时间信息,每过去一秒都可以看得见
*   通过点选指定的颜色按钮,动态改变计时器的文本颜色
*   
* 设置字体大小很容易,但根据窗口大小动态改变还真有点难住了。
* 先试试看哪个可以监听到大小改变事件,下面的main函数中注释有编码时的思考过程
* 最后发现给组件添加HierarchyBoundsListener,使用它的ancestorResized方法可以处理窗口大小改变的事件
* 但是问题又来了,字体大小到底怎么根据窗口大小决定呢?
* 用哪个方法获取到窗口大小,然后用一个固定数字除一下得到的值作为字体大小应该可以吧,试试看
* 找到方法了,组将的getHeight和getWidth可以得到组件的宽高
* 要动态设置字体大小,和动态改变颜色时方法差不多,所以要增加一个变量记录字体大小



代码放下边吧,字数有限制
  1. /*
  2. * 张老师Java高级教程GUI部分视频教程最后留的练习题
  3. *
  4. * 5、 为前边讲的自定义计时器组件增加功能:允许程序设置计时器显示文本的颜色,
  5. * 时间文本的字体大小随组件大小的改变而改变。
  6. *
  7. * 思路:
  8. * 自定义一个组件类实现题目要求的功能
  9. * a 要设置文本颜色,需要提供一个设置方法,可以用列表选择框或单选按钮等来实现
  10. * 这里用一组单选按钮实现,将它们放在一个单独的Panel里便于管理
  11. * b 要让字体大小随组件大小的改变而改变,根据张老师视频中的方法就需要不断调整填充区域的大小
  12. * 这里换一种方法,用布局管理器来实现大小的自动调整,所以要用一个组件来实现计时器功能
  13. * c 要将计时器组件和一组单选按钮Panel放在同一个组件里边实现题目要求的功能,
  14. * 为了方便将类设计为Panel的子类,就可以随意添加组件了
  15. * d 要处理一组单选框按钮产生的事件,为了集中管理,让自定义的类实现ItemListener接口
  16. * 组件重绘过程中需要不断刷新时间值,需要一个单独的线程来执行这个任务,所以让类
  17. * 实现Runnable接口,让功能更加集中
  18. *
  19. * 已经实现的功能:
  20. * 计时功能已实现,在指定区域内(单选按钮左边的区域)按下鼠标开始计时,松开结束
  21. * 动态显示时间信息,每过去一秒都可以看得见
  22. * 通过点选指定的颜色按钮,动态改变计时器的文本颜色
  23. *
  24. * 设置字体大小很容易,但根据窗口大小动态改变还真有点难住了。
  25. * 先试试看哪个可以监听到大小改变事件,下面的main函数中注释有编码时的思考过程
  26. * 最后发现给组件添加HierarchyBoundsListener,使用它的ancestorResized方法可以处理窗口大小改变的事件
  27. * 但是问题又来了,字体大小到底怎么根据窗口大小决定呢?
  28. * 用哪个方法获取到窗口大小,然后用一个固定数字除一下得到的值作为字体大小应该可以吧,试试看
  29. * 找到方法了,组将的getHeight和getWidth可以得到组件的宽高
  30. * 要动态设置字体大小,和动态改变颜色时方法差不多,所以要增加一个变量记录字体大小
  31. *
  32. * 大功告成,呵呵
  33. *
  34. */
复制代码

作者: 鲁柯    时间: 2013-1-30 10:39

  1. package test.gui.stopwatch;

  2. import java.awt.BorderLayout;
  3. import java.awt.Checkbox;
  4. import java.awt.CheckboxGroup;
  5. import java.awt.Color;
  6. import java.awt.Dimension;
  7. import java.awt.Font;
  8. import java.awt.Frame;
  9. import java.awt.Graphics;
  10. import java.awt.GridLayout;
  11. import java.awt.Label;
  12. import java.awt.Panel;
  13. import java.awt.event.HierarchyBoundsAdapter;
  14. import java.awt.event.HierarchyEvent;
  15. import java.awt.event.ItemEvent;
  16. import java.awt.event.ItemListener;
  17. import java.awt.event.MouseAdapter;
  18. import java.awt.event.MouseEvent;
  19. import java.awt.event.WindowAdapter;
  20. import java.awt.event.WindowEvent;
  21. import java.awt.event.WindowStateListener;
  22. import java.text.SimpleDateFormat;
  23. import java.util.Date;

  24. public class MyStopWatch extends Panel implements ItemListener, Runnable
  25. {
  26. //用Label作为计时器的主体,直接设置里边的文本即可
  27. Label label = new Label();
  28. //记录开始结束时间,默认为0
  29. long startTime, endTime;
  30. //记录计时器字体大小,便于动态调整字体大小,增加这个变量的原因下边注释有说明
  31. int fontSize = 25;
  32. //构造函数,让类对象一创建出来就具备想要的一些效果和功能
  33. public MyStopWatch()
  34. {
  35. //让自定义组件使用边界布局
  36. setLayout(new BorderLayout());
  37. //再用一个Label组件显示提示信息
  38. Label notic = new Label("在黑色区域按下鼠标开始计时,松开停止,单选按钮改变文字颜色");
  39. //设置提示文字颜色
  40. notic.setForeground(Color.RED);
  41. //设置提示文字对齐方式,居中
  42. notic.setAlignment(Label.CENTER);
  43. //设置提示文字字体
  44. notic.setFont(new Font("楷体_GB2312", Font.BOLD, 12));
  45. //将提示文字Label加入到自定义组件上方
  46. add(notic, "North");
  47. //将计时器主体Label加到自定义组件的中间
  48. add(label, "Center");
  49. //设置计时器的文本对齐方式
  50. label.setAlignment(Label.CENTER);
  51. //定义一个选择框组,管理用于改变颜色的一组单选按钮
  52. CheckboxGroup checkgroup = new CheckboxGroup();
  53. //创建3个颜色单选按钮,添加到同一个组中
  54. Checkbox cb_green = new Checkbox("绿色", false, checkgroup);
  55. Checkbox cb_red = new Checkbox("红色", true, checkgroup);
  56. Checkbox cb_white = new Checkbox("白色", false, checkgroup);
  57. //定义一个单独的Panel,集中存放单选按钮
  58. Panel checks = new Panel();
  59. //设置单选按钮区域的前景色和背景色
  60. checks.setBackground(Color.BLACK);
  61. checks.setForeground(Color.GREEN);
  62. //设置单选按钮区域的字体
  63. checks.setFont(new Font("楷体_GB2312", Font.BOLD, 15));
  64. //设置单选按钮面板的布局方式为网格布局,添加一列三行按钮
  65. checks.setLayout(new GridLayout(3, 1));
  66. checks.add(cb_green);
  67. checks.add(cb_red);
  68. checks.add(cb_white);
  69. //将颜色选择面板加入到自定义组件的右边
  70. add(checks, "East");
  71. //为3个单选按钮添加事件监听器,自定义组件本身已经实现了ItemListener接口
  72. //其本类对象就可以作为监听器对象使用
  73. cb_green.addItemListener(this);
  74. cb_red.addItemListener(this);
  75. cb_white.addItemListener(this);

  76. //**为计时器添加一个监听器,用来检测大小改变,以实现动态改变文字大小
  77. //查来查去发现好像这个才能实现吧。试过后确实可以,开工了
  78. label.addHierarchyBoundsListener(new HierarchyBoundsAdapter()
  79. {
  80. public void ancestorResized(HierarchyEvent e)
  81. {
  82. //System.out.println("大小变了");
  83. //看看哪个方法可以获取到窗口大小,找到了,第一次width:321,
  84. //字体大小也需要定义一个变量进行保存,和设置颜色类似,初始为25
  85. //int height = label.getHeight();
  86. //得到计时器的宽度,这里根据宽度计算字体大小
  87. int width = label.getWidth();
  88. //System.out.println("Width:"+width+"--Height:"+height);
  89. fontSize = width/12;
  90. //字体大小改变后,进行重绘
  91. repaint();
  92. }
  93. });

  94. //为计时器添加鼠标监听器,实现计时功能
  95. label.addMouseListener(new MouseAdapter()
  96. {
  97. //鼠标按下时的处理方式
  98. public void mousePressed(MouseEvent e)
  99. {
  100. //记录一下,表示鼠标已经按下,可以刷新时间值了
  101. isPressed = true;
  102. //开启线程,不断刷新时间值,以便实现时间动态显示的效果
  103. new Thread(MyStopWatch.this).start();
  104. //开始计时
  105. startTime = endTime = System.currentTimeMillis();
  106. //重绘组件,以免上次的时间值遗留在组件表面
  107. repaint();
  108. }
  109. //鼠标释放时的处理方式
  110. public void mouseReleased(MouseEvent e)
  111. {
  112. //将结束时间置为当前时间
  113. endTime = System.currentTimeMillis();
  114. //改变标记值,停止刷新
  115. isPressed = false;
  116. //再次重绘,将最后的时间值刷一下
  117. repaint();
  118. }
  119. });
  120. }
  121. //定义一个记录颜色值的变量,默认红色,便于颜色的动态改变
  122. Color color = Color.RED;
  123. //覆盖paint方法,实现自定义的重绘效果
  124. public void paint(Graphics g)
  125. {
  126. //定义SimpleDateFormat对象,便于将时间值进行格式化
  127. SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
  128. //计算鼠标按下到释放耗用的时间,因为计算机内部时间表示方式的差异,
  129. //与我们想要的效果(刚开始显示00:00:00)相差了8个小时,所以减掉8个小时
  130. Date costTime = new Date(endTime-startTime-8*60*60*1000);
  131. //将耗用的时间值格式化为我们想要的字符串形式
  132. String strTime = sdf.format(costTime);
  133. //设置计算器的背景色为黑色,前景色动态获取
  134. label.setBackground(Color.BLACK);
  135. label.setForeground(color);
  136. //设置计时器表面字体
  137. label.setFont(new Font("楷体_GB2312", Font.BOLD, fontSize));
  138. //将时间值显示在计算器表面
  139. label.setText(strTime);
  140. }
  141. //实现ItemListener接口中的方法,让自定义组件具有监听器功能
  142. public void itemStateChanged(ItemEvent e)
  143. {
  144. //定义一个Checkbox变量,记录产生事件的具体对象
  145. Checkbox cb = (Checkbox)e.getItemSelectable();
  146. //获取事件源表面的文字信息,便于对比和进行相应设置
  147. String strColor = cb.getLabel();
  148. if (strColor!=null)//如果获取到的信息不为空,应该不会空吧
  149. {
  150. //根据单选按钮表面的文字信息,动态修改计时器的前景色
  151. if (strColor.equals("绿色"))
  152. {
  153. color = Color.GREEN;
  154. label.setForeground(Color.GREEN);
  155. }
  156. else if (strColor.equals("白色"))
  157. {
  158. color = Color.WHITE;
  159. label.setForeground(Color.WHITE);
  160. }
  161. else
  162. {
  163. color = Color.RED;
  164. label.setForeground(Color.RED);
  165. }
  166. }
  167. }
  168. //定义标记变量,记录鼠标按下状态,默认没有按下
  169. boolean isPressed = false;
  170. //实现Runnable接口,复写其中的run方法,让自定义组件具有自动运行指定任务的功能
  171. public void run()
  172. {
  173. //判断鼠标状态,如果按下就开始执行任务
  174. while (isPressed)
  175. {
  176. //让线程稍微休息一会,不能休息太长时间了,不然就不能实现较好的动态效果了
  177. try
  178. {
  179. Thread.sleep(500);
  180. } catch (InterruptedException e)
  181. {
  182. e.printStackTrace();
  183. }
  184. //将结束时间修改为当前时间,以便重绘后达到动态显示时间的效果
  185. endTime = System.currentTimeMillis();
  186. //对组件进行重绘,更新时间值
  187. repaint();
  188. }
  189. }

  190. //主函数,测试使用,也可以直接新建一个类,在Frame中加入一个此类对象
  191. public static void main(String[] args)
  192. {
  193. Frame frame = new Frame("My own StopWatch");
  194. //在窗口中加入一个上边的自定义组件
  195. frame.add(new MyStopWatch());
  196. frame.setSize(380, 180);
  197. //设置窗口最小值,以免用户调的太小,组件都挤不下难看死了
  198. frame.setMinimumSize(new Dimension(380, 180));
  199. //固定大小,用户不可调整
  200. //frame.setResizable(false);
  201. //给窗口添加窗口监听器,处理窗口事件
  202. frame.addWindowListener(new WindowAdapter()
  203. {
  204. //指定默认关闭事件处理方式
  205. public void windowClosing(WindowEvent e)
  206. {
  207. System.exit(0);
  208. }
  209. //想通过这个事件来动态调整计时器内的文本大小,发现这个好像没什么效果,
  210. //不知道具体有什么用,文档中只是简单地说状态i改变,到底什么状态呢
  211. public void windowStateChanged(WindowEvent e)
  212. {
  213. System.out.println("窗口大小改变了");
  214. }
  215. });
  216. //发现这个只有在最大化、最小化时起效果
  217. frame.addWindowStateListener(new WindowStateListener()
  218. {
  219. public void windowStateChanged(WindowEvent e)
  220. {
  221. System.out.println("窗口状态改变了");
  222. }
  223. });
  224. //设置窗口左上角的小图标
  225. //frame.setIconImage(frame.getToolkit().createImage("c:/logo.jpg"));
  226. frame.setVisible(true);
  227. }

  228. }


复制代码





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2