zeyuphoenix

愿我爱的人快乐,愿爱我的人快乐,为了这些,我愿意不快乐.

Java的指针时钟

Java的指针时钟最基础的原理和数字时钟其实差不多,也是利用SwingTimer计时,每隔一定时间重新绘制组件,最后重写paintComponent方法来更新界面.和之前介绍的时钟一样,为了保证时钟的正确启动和终止,需要重写组件的addNotifyremoveNotify方法,在方法内加入Timer的启动和终止;最后也要重写组件getPreferredSize方法使组件的大小自动适应.

首先看最终的效果:

 

工程的目录:




其中timespinner包是时间的微调组件,这儿只是为了显示用的,和指针时钟无关,先不介绍了.

Clock包则是显示指针时钟的包,指针时钟的组件类是AnalogClock,它继承于Clock,处理和数字时钟的基本一致,先看Clock的:

/**

 * This bean to define basic properties and behaviors of a clock, concrete

 * instances will be implemented by <code>DigitalClock</code> and others.

 */

publicabstractclass Clock extends JComponent {

属性也是:

    /**

     * Font rendering context - assumes no default transform, anti-aliasing

     * active and fractional metrics allowed.

     */

    publicstaticfinal FontRenderContext frc = new FontRenderContext(null,

           true, true);

    /**

     * The calendar instance for this clock.

     */

    protected Calendar calendar;

    /**

     * @see #getBgImage()

     */

    protected Image bgImage;

和数字时钟完全一样,提供基本属性和文本显示和绘制的信息容器.

再看AnalogClock

/**

 * To implement a analog-type clock.

 */

publicclass AnalogClock extends Clock implements ActionListener {

它有两个属性:

    /**

     * Parts to construct this clock.

     */

    private Parts parts = null;

    /**

     * A timer to run in a independent thread.

     */

    private Timer timer = null;

一个是定时刷新时间的Timer,一个是时钟的样式.

具体方法有,

1.复写addNotifyremoveNotify方法控制Timer的启动和终止.

    /**

     * @see java.awt.Component#addNotify()

     */

    @Override

    publicvoid addNotify() {

       super.addNotify();

       timer.start();

    }

    /**

     * @see java.awt.Component#removeNotify()

     */

    @Override

    publicvoid removeNotify() {

       timer.stop();

       super.removeNotify();

    }

2.复写getPreferredSize方法使组件自动适应大小.

    /**

     */

    @Override

    public Dimension getPreferredSize() {

       Dimension size = getSize();

       size.width = parts.getSize().width;

       size.height = parts.getSize().height + MARGIN;

       return size;

    }

3.复写paintComponent使修正外观

    @Override

    publicvoid paintComponent(Graphics g) {

4.实现Timer必须的actionPerformed方法,做定时任务

    /**

     * Do transformation based on current precise time when display.

     */

    @Override

    publicvoid actionPerformed(ActionEvent e) {

主要操作是取得当前时间,更新组件:

       parts.doTransform(hour, minute, second, millisecond);

       repaint();

       // Resize this clock in time

       setSize(getPreferredSize());

还有最主要的构造函数,组件的外观通过它传入,

    /**

     * Constructor:<br>

     * Creates an analog-type clock by using given parts.

     */

    public AnalogClock(Parts parts) {

并且把Timer初始化:

timer = new Timer(1000, this);

到现在为止,和时间设置相关的已经完成,剩下的就是传入组件的表现Parts,使画面呈现了.

指针时钟的呈现主要使用了PartsRotatePartsBasicPartsMyParts四个类,它们是继承关系.

其中Parts是最基本的,它主要描绘指针时钟最外层的边框、指针时钟颜色和大小,,并且提供了虚的 doTransform方法供子类实现绘制;

RotatePartsParts的基础上提供了圆心和半径把数字时钟最外层的圆的属性提供出来,并提供了画刻度的方法,没有具体的绘制;

BasicParts是主要的绘制类,它完成了指针时钟显示的大部分工作,提供时钟上的数字和时分秒指针以及指针的变换器这些基本属性,并提供了绘制数字和指针在组件上的方法,简单的继承它就可以实现一个指针时钟了,只是不够美观;

MyParts是继承于BasicParts的类,它主要目的是把指针时钟做的更美观,并且定义时钟的基本大小,颜色等,提供了更好的绘制钟面上数字和指针的方法.

现在依次详细看看这些类:

首先是最基本的Parts

/**

 * To represent all modules which a analog-type clock consists of.

 */

publicabstractclass Parts extends JComponent {

再看看它的属性:

    /**

     * Coloring scheme for the parts.

     */

    protected BasicColor colors;

    /**

     * Size of this parts.

     */

    protected Dimension size;

    /**

     * Clock face.

     */

    protected Shape dial;

分别控制时钟的各个颜色,大小,和外观样式.

然后是方法,它提供一个虚方法给具体类实现:

    /**

     * Changes positions of hour hand, minute hand, second hand and         * decisecond hand based on current time.

     */

    publicabstractvoid doTransform(int hour, int minute, int second,

           int millisecond);

这个方法主要是按给定的时间值得出指针在时钟上的位置和角度.

接着是RotateParts类:

/**

  * This class defines a classical clock behavior by using rotation pattern, *as we all know in common sense.

 */

publicabstractclass RotateParts extends Parts {

再看看它的属性:

    /**

     * X coordinate of the center.

     */

    protectedfloatx;

    /**

     * Y coordinate of the center.

     */

    protectedfloaty;

    /**

     * Radius of the clock face.

     */

    protectedfloatradius;

分别给定了指针时钟的圆的圆心和半径,没有提供绘制方面的属性.

然后是方法,它提供了几个给定时间值换算为时钟位置的方法:

    /**

     * a rotation instance from 12 o'clock direction.

     */

    public AffineTransform getTransform() {

       return AffineTransform.getRotateInstance(0, x, y);

    }

这个方法是提供默认的指针的位置,即绕圆心(0,0)点旋转0,12点位置.

接着

    /**

     * Sets rotation algorithm by given value.

     */

    publicvoid setToRotation(AffineTransform af, double value, int grad) {

       af.setToRotation(value * (2 * Math.PI / grad), x, y);

    }

这个方法根据给定的具体值(这里可以理解为当前具体时间的时、分或者秒)和总的时间划分(12或者60)算出需要旋转的角度,然后绕圆心(x,y)旋转.

最后是

    /**

     * Gets a rotation transform by given parameters.

     */

    public AffineTransform getRotateInstance(int grad, int seq) {

       return getRotateInstance(x, y, grad, seq);

    }

    /**

     * Get a rotation transform by given parameters.

     */

    publicstatic AffineTransform getRotateInstance(float x, float y, int grad, int seq) {

return AffineTransform.getRotateInstance((2 * Math.PI / grad) * seq, x, y);

    }

这个是根据指定的值和总值以及中心点取得映射变换的实例.

接着就是重要的BasicParts类了

/**

 * To implement a classical analog-type clock face, except definitely *describing the hands shape.<br>

*/

publicabstractclass BasicParts extends RotateParts {

它是钟表刻度的继承,继承它就可以实现自己的指针钟表了.

先看它的属性:

    /**

     * Hour hand.

     */

    protected Shape hourHand;

    /**

     * Minute hand.

     */

    protected Shape minuteHand;

    /**

     * Second hand.

     */

    protected Shape secondHand;

    /**

     * Hour hand behavior controller.

     */

    protected AffineTransform hourTransform;

    /**

     * Minute hand behavior controller.

     */

    protected AffineTransform minuteTransform;

    /**

     * Second hand behavior controller.

     */

    protected AffineTransform secondTransform;

6个属性提供时分秒三个时针的形状和绘制映射类,通过它们可以对钟表进行绘制.

    /**

     * Moves all parts, to leave some margin.

     */

    protectedtransient AffineTransform trans;

这个属性是在对时分秒指针绘制时提供变换的.

    /**

     * Arabic time punctualities.

     */

    publicstaticfinal String[] ARABIC = { "12", "1", "2", "3", "4", "5", "6","7", "8", "9", "10", "11" };

    /**

     * Roman time punctualities.

     */

    publicstaticfinal String[] ROMAN = { "XII", "I", "II", "III", "IV", "V","VI", "VII", "VIII", "IX", "X", "XI" };

这两个常量是提供表盘的刻度显示的,也可以自己定义一个12位的数组代替.

再看它的构造函数

/**

* Constructor: Joins every parts in a entire analog-type clock.

*/

    protected BasicParts(Shape dial, Shape hourHand, Shape minuteHand,

           Shape secondHand, String[] numbers, BasicColor colors)

           throws Exception {

需要传入外围图形、时分秒图形、刻度数字和各部分颜色.当然可以传入new GeneralPath()

在以后再具体描绘它们.

    /**

     * Initializes hand transformation.

     */

    protectedvoid initTransform() {

       hourTransform = getTransform();

       minuteTransform = getTransform();

       secondTransform = getTransform();

    }

这个是初始化时分秒绘制映射类的.默认让它们都指向12点方向.

    /**

     * Default algorithm for hands's action trace.

     */

    @Override

    publicvoid doTransform(int hour, int minute, int second, int millisecond) {

       if (hourTransform != null && minuteTransform != null

              && secondTransform != null) {

           setToRotation(hourTransform,

                  hour + (minute + second / 60.0) / 60.0, 12);

           setToRotation(minuteTransform, minute + second / 60.0, 60);

           setToRotation(secondTransform, second, 60);

       }

    }

这个是父类的虚函数的实现,根据给定值旋转指定角度呈现给画面.

    /**

     * Draws a number at 12 o'clock.

     */

    protectedvoid drawNumber(Graphics g, String number, Font font) {

       BasicColor c = (BasicColor) colors;

       AttributedString num = new AttributedString(number);

       if (font != null) {

           num.addAttribute(TextAttribute.FONT, font);

       }

       drawNumber(g, num, x, y - radius, c.numbers);

    }

    /**

     * Draws a number at 12 o'clock.

     */

    publicstaticvoid drawNumber(Graphics g, AttributedString number, float x, float y, Color color) {

       if (number != null) {

           Graphics2D g2 = (Graphics2D) g;

           g2.setPaint(color);

           g2.drawString(number.getIterator(), x, y);

       }

    }

是按指定的属性在表盘上画刻度的.

最后是重要的paintComponent方法了

    @Override

    publicvoid paintComponent(Graphics g) {

它按照属性了上面取得的绘制映射类进行绘制

首先是绘制外围界面:

    g2.setPaint(c.dail);

    g2.fill(trans.createTransformedShape(dial));

    g2.setPaint(Color.BLACK);

g2.draw(trans.createTransformedShape(dial));

然后绘制时分秒指针:

// Draw hour hand

g2.setPaint(c.hourHand);

g2.fill(trans.createTransformedShape(hourTransform

                  .createTransformedShape(hourHand)));

分秒基本和时的一样.

最后要看的类就是自己实现的MyParts类了,其实这里简单实现一个SimpleParts也可以的只是界面比较难看,如下图:

所以需要做漂亮点还是要自己去写一部分代码的.

先看继承关系

/**

 * A piece of sample code to show how to develop a nice-looking analog-type

 * clock by using this API.

*/

publicfinalclass MyParts extends BasicParts {

首先还是看它的属性:

    /**

     * Radius of the clock face.

     */

    protectedfloatradius;

这个是定义钟表的半径.

    /**

     * 12 hour ticks.

     */

    protected Shape tick;

    /**

     * Other 48 minute ticks not at time punctualities.

     */

    private GeneralPath smallTick;

2个是定义钟表的刻度,分别代表比较明显的12个整点刻度,和其它48个不明显的刻度.

    /**

     * X coordinate of left top corner.

     */

    privatestaticfloatxNW = 0;

    /**

     * Y coordinate of left top corner.

     */

    privatestaticfloatyNW = 0;

    /**

     * Width of the square.

     */

    privatestaticfloatwidth = 170;

2个属性分别代表距离中心的坐标和表的外围大小.

    /**

     * Additional margin size in proportion of radius by percentage.

     */

    privatestaticfloatmarginOfRadius = 0.1f;

这个属性代表空白区域的百分比.

然后是方法,先看画刻度的方法:

    /**

     * Draws ticks.

     */

    publicstaticvoid drawTicks(Graphics g, Shape tick, int tickNumber,

           float x, float y, AffineTransform trans, Color color) {

首先得到最基本的指针位置,默认指向12点位置:

AffineTransform at = AffineTransform.getRotateInstance(0, x, y);

然后取得偏移的角度:

at = RotateParts.getRotateInstance(x, y, tickNumber, p);

最后是绘制:

g2.fill(trans.createTransformedShape(at

              .createTransformedShape(tick)));

再看绘制指针的方法:

    /**

     * Generate hour hand and minute hand shape.

     */

    privatevoid createHand(Shape hand, float x, float y, float radius,

           float widthPercent, float lengthPercent, float marginPercent,

           float firstWidthPercent, float firstLengthPercent,

           float secondWidthPercent, float secondLengthPercent) {

这个是绘制时针和分针的,形状是尾部粗尖端细

h.moveTo(x, y);

h.curveTo(x - radius * (widthPercent / 2) * (firstWidthPercent / 2), y- radius * marginPercent * (firstLengthPercent / 2), x – radius * (widthPercent / 2) * (secondWidthPercent / 2), y – radius * marginPercent * (secondLengthPercent / 2), x, y – radius * lengthPercent);

    /**

     * Generates concrete hand shape.

     */

publicstaticvoid createHand(Shape hand, float x, float y, float radius, float widthPercent, float lengthPercent, float marginPercent) {

这个是绘制秒针的,粗细均匀,比较简单

h.moveTo(x - radius * (widthPercent / 2), y + radius * marginPercent);

h.lineTo(x + radius * (widthPercent / 2), y + radius * marginPercent);

再看绘制表上数字的方法

    /**

     * An algorithm to locate time punctualities numbers on a round clock *face

     */

 privatevoid drawNumbers(Graphics g, String[] numbers, float marginPercent, Font font) {

3点举例,先算角度:

float cZero1 = (float) Math.cos((2 * Math.PI / 12) * 3);

再把数字转为属性串,取得宽度:

num = new AttributedString(numbers[p]);

num.addAttribute(TextAttribute.FONT, font);

layout = new TextLayout(numbers[p], font, Clock.frc);

float width = layout.getBounds().getBounds().width;

然后算出坐标:

float px = (float) (x + trans.getTranslateX() + radius

                     * (1 + marginPercent) * sin);

最后调用父类绘制方法绘制:

super.drawNumber(g, num, px, py, color);

接着是初始化方法,它把指针和表盘大小,位置都进行了初始化:

    /**

     * To initialize some parameters and every parts shape.

     */

    protectedvoid initialize() {

首先算圆心和半径:

x = xNW + width / 2;

       y = yNW + width / 2;

       radius = width / 2 - 5;

然后画时针:

设定各个百分比位置,然后调用时针方法

float hWidthOfRadius = 0.08f;

float hLengthOfRadius = 0.7f;

createHand(hourHand, x, y, radius, hWidthOfRadius, hLengthOfRadius,

               hMarginOfRadius, fstWidthOfRadius, fstLengthOfRadius,

              sndWidthOfRadius, sndLengthOfRadius);

其它指针也是类似画出.

最后是复写paintComponent方法,当属性变更时重新绘制指针时钟:

    /**

     * Paint ticks and time punctualities.

     */

    @Override

    publicvoid paintComponent(Graphics g) {

在里面进行了指针数字和刻度绘制方法的调用

// Draw 12 numbers by using specific font

      drawNumbers(g, numbers, marginOfRadius, new Font("Ravie", Font.BOLD + Font.ITALIC, 8));

       // Draw 12 hour ticks, here use SimpleParts

       drawTicks(g, tick, max, x, y, trans, c.tick);

       // Draw 48 minute ticks, here use SimpleParts

       drawTicks(g, smallTick, 60, x, y, trans, c.tick);

这个绘制类就完成了.

到此为止,所有的指针时钟的创立工作全部完成.

最后通过

    /**

     * This method shows how to create a user defined analog-type clock

     */

    private AnalogClock getColorfulClock() {

       if (colorfulClock == null) {

           try {

              colorfulClock = new AnalogClock(new MyParts());

           } catch (Exception e) {

              e.printStackTrace();

           }

       }

       returncolorfulClock;

    }

就可以使用了.

posted on 2010-04-06 22:03 zeyuphoenix 阅读(2622) 评论(0)  编辑  收藏 所属分类: Java的时钟

导航

<2010年4月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

统计

常用链接

留言簿(52)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜