专栏首页C++核心准则原文翻译自学鸿蒙应用开发(43)- 秒表应用开发(1)

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

从本篇文章开始,边学边练开发一个秒表应用,本文是本系列的第一篇:绘制表盘。先看演示视频:

准备布局

内容很简单,一个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="center">

    <Text
        ohos:id="$+id:gui_thread_time"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:background_element="$graphic:background_ability_main"
        ohos:layout_alignment="horizontal_center"
        ohos:text="22:20:20.123"
        ohos:text_size="50fp"
        ohos:text_color="#0000FF"
        />
    <xwg.harmony.stopwatch.AnalogStopWatch
        ohos:id="$+id:analog_stop_watch"
        ohos:height="400vp"
        ohos:width="match_parent"
        />
</DirectionalLayout>

绘制表盘

代码虽长,但并不难。AnalogStopWath类继承自Component类并实现了Component.DrawTask接口。但目前实现了绘制表盘和秒针功能。

从第3行到第21行重载了Component的构造函数;第24~26行实现了DrawTask的onDraw操作。目前只调用了一个drawPanel方法,将来会调用其他方法。

第28~31的setSecond方法用于指定当前的秒数值。秒数值更新后会调用超类的invalidate方法启动描画过程。

public class AnalogStopWatch extends Component implements Component.DrawTask {
    double second = 0;
    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);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        drawPanel(canvas);
    }

    public void setSecond(double sec) {
        second = sec;
        invalidate();
    }

    private void drawPanel(Canvas canvas){
        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        RectFloat bound = getBoundRect();
        float radius = bound.getWidth() / 2;
        float len5sec = radius / 5;
        float len1sec = radius / 10;
        float len02sec = radius / 20;
        Point center = bound.getCenter();
        canvas.drawOval(bound, paint);
        paint.setColor(Color.BLACK);
        for(int i = 0; i < 360; i++){
            float insideRaduis = radius;
            if ((i % 30)==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, insideRaduis, radius, i);
        }
        paint.setColor(Color.RED);
        paint.setStrokeWidth(radius / 40);
        paint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        drawRadius(canvas, paint, 0, radius * 0.9f, second * 6);
        float oval_radius = radius / 20;
        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, float from, float to, double degree){
        double angle = Math.PI * degree / 180;
        double sin = Math.sin(angle);
        double cos = Math.cos(angle);
        Point center = getBoundRect().getCenter();
        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 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 void Initialize(AttrSet attrSet){
        addDrawTask(this);
    }
}

drawRadius用于绘制各种径向直线,如刻度线,指针等。角度是以12点钟其实都度数单位,这种方式最适合表示时间。

下图是描画结果:

参考代码

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

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

作者著作介绍

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

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

本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-05-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自学鸿蒙应用开发(44)- 秒表应用开发(2)

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

    面向对象思考
  • 自学鸿蒙应用开发(12)- Checkbox

    下面代码中的第20行获取RadioContainer组件后,在第24行根据Checkbox的状态更新TimePicker的形式,然后在第42行~45行为Chec...

    面向对象思考
  • 自学鸿蒙应用开发(13)- ProgressBar

    下面代码中的第18行获取ProgressBar组件后,在第19行根据TimePicker的状态更新Progress的形式,然后在第26行TimerPicker的...

    面向对象思考
  • 自学鸿蒙应用开发(14)- RoundProgressBar

    如下代码中35行~44行所示,在布局中增加RoundProgressBar组件。

    面向对象思考
  • 自学鸿蒙应用开发(15)- ScrollView

    如下代码中第2行~第10行和第137行所示,可以很简单地在布局中增加ScrollView组件。

    面向对象思考
  • 自学鸿蒙应用开发(16)- ListContainer

    在layout目录下的xml文件中创建ListContainer布局,将其命名为page_listcontainer.xml。

    面向对象思考
  • 自学鸿蒙应用开发(30)- 自定义UI组件(1)

    任何一种开发工具也不可能为开发者提供所有的组件,根据现有组件定义自己的组件也就成为必需。接下来的几篇文章我们定义一个多层圆弧形进度条。本文是第一篇。

    面向对象思考
  • 自学鸿蒙应用开发(9)- TimePicker组件

    如下面代码中21行~49行所示,在获取TimePicker组件后,一方面在button的动作响应中计算所选时刻和当前时刻的秒数差之后用小窗口表示出来;另一方面在...

    面向对象思考
  • 自学鸿蒙应用开发(10)- Switch组件

    如下面代码中20行和42行~49行所示,在获取Switch组件后,在Switch响应处理中根据Switch的当前状态为TimePicker设定是否像是为24小时...

    面向对象思考
  • 自学鸿蒙应用开发(11)- RadioButton和RadioContainer

    本文介绍在鸿蒙应用中RadioButton和RadioContainer组件的基本用法。

    面向对象思考
  • 自学鸿蒙应用开发(17)- TabList和Tab

    在layout目录下创建TabList布局,将其命名为ability_tablist.xml。

    面向对象思考
  • 自学鸿蒙应用开发(5)- button组件

    代码中按钮id被指定为hello_button,这个信息会在下面的响应代码中用到。

    面向对象思考
  • 自学鸿蒙应用开发(6)- TextField组件

    如下面代码中20行和22行所示,在按钮组件的响应代码中获取TextField组件后,获取输入的文字信息并弹窗显示。

    面向对象思考
  • 自学鸿蒙应用开发(7)- Picker组件

    如下面代码中30行和36行所示,在获取PIcker组件后,设定表示信息并在动作响应代码将最新表示信息设定到TextField组件上。

    面向对象思考
  • 自学鸿蒙应用开发(8)- DatePicker组件

    如下面代码中21行和50行所示,在获取DatePicker组件后,一方面在button的动作响应中计算所选日期和当前日期的差值之后用小窗口表示出来;另一方面在用...

    面向对象思考
  • 自学鸿蒙应用开发(37)- PageSlider组件

    PageSlider是用于页面之间切换的组件,它通过响应滑动事件完成页面间的切换。

    面向对象思考
  • 自学鸿蒙应用开发(26)- 自定义CommonDialog

    上一篇文章中说过,直接使用鸿蒙系统中的CommonDialog大致是下面的效果:

    面向对象思考
  • 自学鸿蒙应用开发(27)- 自定义ListDialog

    这个效果过于简陋,无法用于实际的产品开发。本文介绍如何定制自己的ListDialog。还是先看演示视频:

    面向对象思考
  • 鸿蒙应用开发环境

    DevEco Studio的编译构建依赖JDK,DevEco Studio预置了Open JDK,版本为1.8,安装过程中会自动安装JDK。

    乐心湖

扫码关注云+社区

领取腾讯云代金券