经过几天的开发,秒表应用终于初具规模了,先看执行效果:

秒表应用开发2

指针式秒表组件

下面是自定义指针式模拟秒表组件的实现代码。具体内容参见注释。

//指针式秒表组件类public class AnalogStopWatch extends Component implements Component.DrawTask {    private long start_time = 0;    //计时开始时刻,毫秒单位    private long millisecond = 0;   //计时时间,毫秒单位    private boolean running = false;//执行状态    //构造函数    public AnalogStopWatch(Context context) {        super(context);        Initialize(null);    }    //构造函数    public AnalogStopWatch(Context context, AttrSet attrSet) {        super(context, attrSet);        Initialize(attrSet);    }    //构造函数    public AnalogStopWatch(Context context, AttrSet attrSet, String styleName) {        super(context, attrSet, styleName);        Initialize(attrSet);    }    //构造函数    public AnalogStopWatch(Context context, AttrSet attrSet, int resId) {        super(context, attrSet, resId);        Initialize(attrSet);    }
    //获取运行状态    public boolean isRunning(){        return running;    }
    //获取当前计时时长    public long getMiliseconds(){        return millisecond;    }
    //根据目前的运行状态,开始或停止计时    public void start_stop(){        if(!running) {            start_time = Calendar.getInstance().getTimeInMillis();            millisecond = 0;            running = true;            onRunTimer();        }        else{            running = false;        }    }
    //计时Timer    void onRunTimer(){        final long delayTime = 100L;  //延时时间,毫秒单位        millisecond = Calendar.getInstance().getTimeInMillis() - start_time;        invalidate();  //更新画面表示        if(running) {            //如果处于运行状态,触发下一次延时执行            TaskDispatcher uiTaskDispatcher = mContext.getUITaskDispatcher();            Revocable revocable = uiTaskDispatcher.delayDispatch(new Runnable() {                @Override                public void run() {                    onRunTimer();                }            }, delayTime);        }    }
    //归零    public void reset(){        if(!running && millisecond > 0) {            onResetTimer();  //启动归零处理        }    }
    //归零处理    void onResetTimer(){        final long delayTime = 10L;        long second_value = millisecond / 1000 % 60;        long minute_value = millisecond / 1000 / 60;        if(second_value > 0){            millisecond -= 1000;   //秒针反转        }        if(minute_value > 0){            millisecond -= 1000 * 60;  //分针反转        }        if(second_value > 0 || minute_value >0) {            //如果反转未到位,触发下次延时处理            TaskDispatcher uiTaskDispatcher = mContext.getUITaskDispatcher();            Revocable revocable = uiTaskDispatcher.delayDispatch(new Runnable() {                @Override                public void run() {                    onResetTimer();                }            }, delayTime);        }        else{            millisecond = 0;        }        invalidate();    }
    @Override    public void onDraw(Component component, Canvas canvas){        Paint paint = new Paint();        RectFloat bound = getBoundRect();        Point main_center = bound.getCenter();        drawScale(canvas, paint, main_center, getRadius(), 1);        drawScaleValue(canvas, paint, main_center, getRadius() * 0.8f,                12, 5, (int)(getRadius() * 0.2f));
        Point center = new Point(main_center.getPointX(),                                bound.top + getRadius() * 0.6f);        float radius = getRadius() / 3;        drawScale(canvas, paint, center, radius, 5);        drawScaleValue(canvas, paint, center, radius * 0.7f,                12, 5, (int)(radius * 0.25f));
        drawDigitalTime(canvas, paint, main_center.getPointX(),main_center.getPointY() + getRadius() * 0.4f);        drawMinute(canvas, center, radius);        drawSecond(canvas, bound);    }
    //描画刻度线    private void drawScale(Canvas canvas, Paint paint, Point center, float radius, int scale_interval){        paint.setColor(Color.WHITE);        float len5sec = radius / 8;        float len1sec = radius / 12;        float len02sec = radius / 20;        paint.setColor(Color.BLACK);        for(int i = 0; i < 300; i+= scale_interval){            float insideRaduis = radius;            if ((i % 25)==0){                insideRaduis -= len5sec;                paint.setStrokeWidth(radius / 60);            }            else if((i % 5)==0){                insideRaduis -= len1sec;                paint.setStrokeWidth(radius / 80);            }            else{                insideRaduis -= len02sec;                paint.setStrokeWidth(radius / 120);            }            drawRadius(canvas, paint, center, insideRaduis, radius, i * Math.PI / 150);        }    }
    //描画全部刻度值    private void drawScaleValue(Canvas canvas, Paint paint, Point center, float radius, int count, int offset, int font_size){        for(int i = 1; i <= count; i++){            drawScaleText(canvas, paint, center, radius, Math.PI * 2 / count * i, offset * i, font_size);        }    }
    //描画秒针    private void drawMinute(Canvas canvas, Point center, float radius){        Paint paint = new Paint();        paint.setColor(Color.RED);        paint.setStrokeWidth(radius / 20);        paint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);        drawRadius(canvas, paint, center, 0, radius * 0.9f, millisecond / 60 * Math.PI * 2 / 60000);        float oval_radius = radius / 10;        canvas.drawOval(new RectFloat(center.getPointX() - oval_radius, center.getPointY() - oval_radius,                        center.getPointX() + oval_radius, center.getPointY() + oval_radius),                paint);    }
    //描画分针    private void drawSecond(Canvas canvas, RectFloat bound){        float radius = getRadius();        Paint paint = new Paint();        paint.setColor(Color.RED);        paint.setStrokeWidth(radius / 40);        paint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);        drawRadius(canvas, paint, bound.getCenter(), 0, radius * 0.9f, millisecond * Math.PI * 2 / 60000);        float oval_radius = radius / 20;        Point center = bound.getCenter();        canvas.drawOval(new RectFloat(center.getPointX() - oval_radius, center.getPointY() - oval_radius,                        center.getPointX() + oval_radius, center.getPointY() + oval_radius),                paint);    }
    //描画径向直线    private void drawRadius(Canvas canvas, Paint paint, Point center, float from, float to, double angle){        double sin = Math.sin(angle);        double cos = Math.cos(angle);        canvas.drawLine(new Point(center.getPointX() + (float)(from * sin),                        center.getPointY() - (float)(from * cos)),                        new Point(center.getPointX() + (float)(to * sin),                        center.getPointY() - (float)(to * cos)),                        paint);    }
    //描画单一刻度值    private void drawScaleText(Canvas canvas, Paint paint, Point center, float radius, double angle, int value, int font_size){        double sin = Math.sin(angle);        double cos = Math.cos(angle);        Font.Builder builder = new Font.Builder("Arial");        builder.setWeight(Font.REGULAR);        paint.setFont(builder.build());        paint.setTextSize(font_size);        String text = String.format("%d", value);        float width = paint.measureText(text);      //计算字符串显示宽度        canvas.drawText(paint, text,                center.getPointX() + (float)(radius * sin) - width / 2,                center.getPointY() - (float)(radius * cos) + font_size / 3);    }
    //描画数字时间    private void drawDigitalTime(Canvas canvas, Paint paint, float x, float y){        Font.Builder builder = new Font.Builder("Arial");        builder.setWeight(Font.REGULAR);        paint.setFont(builder.build());        int font_size = (int)(getRadius() / 4);        paint.setColor(Color.BLUE);        paint.setTextSize(font_size);        String now = String.format("%02d:%02d:%02d",                        millisecond/1000/3600,   //hour                        millisecond/1000/60%60,  //minute                        millisecond/1000%60);     //second        float width = paint.measureText(now);           //计算字符串显示宽度        canvas.drawText(paint, now, x - width / 2 , y);    }
    //获取秒表显示区域    private RectFloat getBoundRect(){        float width = getWidth();        float height = getHeight();        float size = Math.min(width, height);        float x_padding = (width - size) / 2;        float y_padding = (height - size) / 2;        return new RectFloat(x_padding, y_padding, width - x_padding, height - y_padding);    }
    //获取显示半径    private float getRadius(){        return getBoundRect().getWidth() / 2;    }
    //获取圆心位置    private Point getCenter(){        return getBoundRect().getCenter();    }
    //初始化    private void Initialize(AttrSet attrSet){        addDrawTask(this);    }}

在布局中布置指针式秒表组件

在本应用的布局中,我们使用了指针式秒表组件、两个操作按钮和一个表示计时结果的Text组件:​​​​​​​

<?xml version="1.0" encoding="utf-8"?><DirectionalLayout    xmlns:ohos="http://schemas.huawei.com/res/ohos"    ohos:height="match_parent"    ohos:width="match_parent"    ohos:orientation="vertical"    ohos:alignment="top">    <xwg.harmony.stopwatch.AnalogStopWatch        ohos:id="$+id:analog_stop_watch"        ohos:height="300vp"        ohos:width="match_parent"        />    <DirectionalLayout        ohos:height="match_content"        ohos:width="match_parent"        ohos:orientation="horizontal"        ohos:alignment="center"        ohos:top_margin="5vp">        <Component            ohos:height="match_parent"            ohos:width="0"            ohos:weight="1"/>        <Button            ohos:id="$+id:reset_lap"            ohos:height="match_content"            ohos:width="0"            ohos:weight="5"            ohos:text_size="27fp"            ohos:text="$string:Reset"            ohos:background_element="$graphic:background_button"            />        <Component            ohos:height="match_parent"            ohos:width="0"            ohos:weight="1"/>        <Button            ohos:id="$+id:start_stop"            ohos:height="match_content"            ohos:width="0"            ohos:weight="5"            ohos:text_size="27fp"            ohos:text="$string:Start"            ohos:background_element="$graphic:background_button"            />        <Component            ohos:height="match_parent"            ohos:width="0"            ohos:weight="1"/>    </DirectionalLayout>    <Text        ohos:id="$+id:lap_times"        ohos:width="match_parent"        ohos:height="match_parent"        ohos:text_alignment="horizontal_center"        ohos:text_size="30fp"        ohos:multiple_lines="true"        ohos:scrollable="true"        ohos:top_margin="5vp"        ohos:left_margin="5vp"        ohos:right_margin="5vp"        /></DirectionalLayout>

使用指针式秒表组件

以下是在AbilitySlice中使用指针式秒表组件的示例代码:​​​​​​​

public class MainAbilitySlice extends AbilitySlice {    AnalogStopWatch stopwatch = null;    Text lap_time = null;    int record_count = 0;    @Override    public void onStart(Intent intent) {        super.onStart(intent);        super.setUIContent(ResourceTable.Layout_ability_main);        //秒表组件        stopwatch = (AnalogStopWatch)findComponentById(ResourceTable.Id_analog_stop_watch);        //计时结果Text组件        lap_time = (Text)findComponentById(ResourceTable.Id_lap_times);        //开始或停止按钮        Button start_stop = (Button)findComponentById(ResourceTable.Id_start_stop);        //清零或保存结果按钮        Button reset_lap = (Button)findComponentById(ResourceTable.Id_reset_lap);        start_stop.setClickedListener(new Component.ClickedListener() {            @Override            public void onClick(Component component) {                stopwatch.start_stop();                if(stopwatch.isRunning()){                    start_stop.setText(ResourceTable.String_Stop);                    reset_lap.setText(ResourceTable.String_Lap);                    clearTime();                }                else{                    start_stop.setText(ResourceTable.String_Start);                    reset_lap.setText(ResourceTable.String_Reset);                    recordTime();                }            }        });
        reset_lap.setClickedListener(new Component.ClickedListener() {            @Override            public void onClick(Component component) {                if (stopwatch.isRunning()){                    recordTime();                }                else{                    stopwatch.reset();                    clearTime();                }            }        });    }    //清除计时结果    private void clearTime(){        lap_time.setText("");        record_count = 0;    }
    //记录当前时间    private void recordTime(){        String lap_string = lap_time.getText();        long milliseconds = stopwatch.getMiliseconds();        String current_time = String.format("Lap%02d %02d:%02d:%02d.%03d",                record_count,                milliseconds / 1000 / 60 / 60 % 60,  //hour                milliseconds / 1000 / 60 % 60,       //minute                milliseconds / 1000 % 60,            //second                milliseconds % 1000);                //milisecond        lap_time.setText(lap_string + "\n" + current_time);        record_count++;    }}

我们没有在指针式秒钟组件中处理按钮动作,其目的是为了给使用者提供更大的灵活性。

执行结果如下:

参考代码

完整代码可以从以下链接下载:

https://github.com/xueweiguo/Harmony/tree/master/StopWatch

作者著作介绍

《实战Python设计模式》是作者去年3月份出版的技术书籍,该书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。

对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。


觉得本文有帮助?请分享给更多人。

关注微信公众号【面向对象思考】轻松学习每一天!

面向对象开发,面向对象思考!

自学鸿蒙应用开发(44)- 秒表应用开发(2)相关推荐

  1. 自学鸿蒙应用开发(43)- 秒表应用开发(1)

    从本篇文章开始,边学边练开发一个秒表应用,本文是本系列的第一篇:绘制表盘.先看演示视频: 秒表应用开发(1) 准备布局 内容很简单,一个Text组件加上自定义的秒表组件. <?xml versi ...

  2. 自学python还是报班-转行Python开发自学还是报班?老男孩全日制学习

    很多朋友在转行时非常慎重,在很多人眼里学Python开发就是敲敲代码而已,这样的想法磨灭了很多想转行人的热情.其实Python工程师并不是一份枯燥工作,它有多种机会去做很多事情,比如游戏开发.人工智能 ...

  3. 梅科尔工作室——鸿蒙设备开发实战004:内核开发

    目录 华为云14天鸿蒙设备开发培训Day4:内核开发 任务管理 任务管理的简介 任务相关的概念 任务的调度机制 实现任务的管理 实验结果与扩展实验 软件定时器 软件定时器的基本概念 软件定时器的运作机 ...

  4. 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十五)驱动程序基石

    文章目录 1.1 休眠与唤醒 1.1.1 适用场景 1.1.2 内核函数 1.1.2.1 休眠函数 1.1.2.2 唤醒函数 19.1.3 驱动框架 1.1.4 编程 1.1.4.1 驱动程序关键代码 ...

  5. 鸿蒙OS Hi3861的芯片实际开发中遇到的问题

    鸿蒙OS Hi3861的芯片实际开发中遇到的问题 优点: 比较适合开发者快速开发,上面的硬件操作的函数封装很简单,非常适合初学者使用封装好的函数调用硬件管脚,比如I2c gpio spi sdio 都 ...

  6. python测试开发自学教程-自动化平台测试开发:Python测试开发实战_PDF电子书

    因资源下载地址容易失效,请加微信号359049049直接领取,直接发最新下载地址. 前言 ======================================================= ...

  7. 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(一) 嵌入式Linux开发基本概念以及开发流程介绍

    文章目录 1.linux开发初了解 1.1 嵌入式Linux开发的基本概念 1.1.1关于Git的背景介绍 1.1.2关于repo的背景介绍 1.1 3 一些关于此背景知识的介绍 1.1.4关于Lin ...

  8. 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(八)IMX6ULL开发板编译第一个程序以及驱动

    文章目录 1. IMX6ULL开发板初次操作 1.1 100ask_imx6ull开发板硬件资源简介 1.1.1 100ask imx6ull mini开发板 1.2 100ask_imx6ull开发 ...

  9. HarmonyOS应用如何开发,使用什么开发工具及安装使用教程说明!

    本篇文章主要讲解鸿蒙os的开发工具安装使用教程.鸿蒙os的应用开发说明指引.鸿蒙os对于开发者的机遇以及鸿蒙os是否会成为主流操作系统的问题刨析. 日期:2021-6-30 作者:csdn 任聪聪 一 ...

最新文章

  1. 如何将光标定位到行尾
  2. RecyclerView + SnapHelper实现炫酷ViewPager效果
  3. BUCK/BOOST电路原理分析
  4. RK3288 make otapackage 出错的问题【转】
  5. varnish基本配置(二)
  6. C# Aes CryptoStream Specified padding mode is not valid for this algorithm的解決方法
  7. bootstrap grid php,bootstrap grid用法
  8. python进入内置函数文件_python基础知识-set、函数、内置函数、文件操作
  9. 将之前写完的猜数字游戏改为通过javabean_【沃德英语】好玩的单词游戏
  10. Android自定义选座,Android实现电影院选座效果
  11. Django接口文档
  12. ZZULIOJ 1047: 对数表,Java
  13. 2022爱分析・人工智能应用实践报告
  14. 三角色:程序员、技术主管与架构师
  15. ffmpeg安装及在java中的使用案例
  16. C语言源程序作业完成系统,C语言源程序的自动评判系统
  17. 拉格朗日乘数法求可能极值
  18. 关于include的正确理解和用法
  19. 【学习总结】VIO初始化学习1:Monocular Visual–Inertial State Estimation With Online Initialization and Camera–IMU
  20. 音频编辑软件哪个好用

热门文章

  1. 如何使用premiere制作分屏画面
  2. python函数嵌套调用的实现
  3. es7学习笔记 cpu负载不均衡、超长fullGC、大量400报错
  4. 报错Unsatisfied dependency expressed through field ‘baseMapper‘解决
  5. 代码随想录二战day2
  6. Unity——读取Excel中的数据
  7. STM32F4与STM32F1的区别
  8. 饥荒联机版连不上服务器_饥荒联机版不搜索房间直连服务器教程 搜索不到房间怎么办-游侠网...
  9. 刚上岸字节测试开发岗,全网最真实的大厂面试真题
  10. 鸿蒙空间最高级,鸿蒙天帝-第4406章:神秘的敌人