从本篇文章开始,边学边练开发一个秒表应用,本文是本系列的第一篇:绘制表盘。先看演示视频:
准备布局
内容很简单,一个Text组件加上自定义的秒表组件。
代码语言:javascript复制<?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方法启动描画过程。
代码语言:javascript复制
代码语言:javascript复制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点钟其实都度数单位,这种方式最适合表示时间。
下图是描画结果:
代码语言:javascript复制
参考代码
完整代码可以从以下链接下载:
代码语言:javascript复制https://github.com/xueweiguo/Harmony/tree/master/StopWatch
作者著作介绍
《实战Python设计模式》是作者去年3月份出版的技术书籍,该书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。
对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。