10分钟鸿蒙应用实战开发:鸿蒙手绘板 (含源代码)
前言:
今天(2020.12.16)是鸿蒙的手机beta发布活动,很荣幸受邀来到现场,一会儿可以给大家上个靓照~。
本篇旨在通过实践一些样例,让开发者们快速提高肾上腺素,欢乐的加入鸿蒙应用开发之旅。整篇就是一个完整的实操样例,我也尽量在一片中把内容都讲清楚。
基础的一些知识点,可以访问我另一个系列:《鸿蒙OS应用开发实践》
正文
(一)创建项目
1.创建一个新的TV项目:
2.创建一个新的Java类:
命名为Draw:
这个作为我们的绘画的核心组件,所以我们让他继承Component,方便后面的调用。需要注意的是,这里导入包名的时候,我们选择第一个:ohos.agp.components包。
完成后,依然会报错,提示我们需要创建构造函数:
同样默认会有很多构造方法,我们选择第一个(单个参数)即可。
(二)实现绘画工具
这样一个基础的组件类就创建好了,接着我们构思下一画板工具里需要哪些元素:
画笔:用于画出各种点和线。
画板:用于展现我们到底花了什么,它是内容的载体。
所以,根据以上这些元素,在接下来我们需要在代码里定义和创建一些内容:
Path mPath = new Path();
Paint mPaint;
Point mPrePoint = new Point();
Point mPreCtrlPoint = new Point();
Canvas : 画布的意思,属于渲染组件,一般用于渲染各种界面元素,这里需要 import ohos.agp.render.Canvas;包
Path : 路径的意思,也属于渲染组件,用于描述绘制的路径。需要import ohos.agp.render.Path;
Paint : 表示绘制,属于渲染组件,用于一些绘制操作,需要import ohos.agp.render.Paint;
Point : 表示一个点,通常由二维坐标(x,y)组成,需要import ohos.agp.utils.Point;
所以上面的代码,我们先定义了一些等待使用的工具变量。
现在我们缺少了一个东西,那就是如何交互?一般的,绘图这样的,我们要么鼠标,要么触屏,要么就是电子绘笔等。这里我们使用鸿蒙触摸组件来实现。
在代码中去实现Component.TouchEventListener方法:
实现onTouchEvent()方法:
onTouchEvent包含两个参数:Component表示当前接收的组件,TouchEvent表示当前的触摸事件。
通过getAction实例方法可以获取TouchEvent的状态:
TouchEvent.PRIMARY_POINT_DOWN : 按下状态
TouchEvent.PRIMARY_POINT_UP :点按状态抬起
TouchEvent.POINT_MOVE: 点按拖动
我们需要在按下的时候开始记录点的位置,拖动的时候记录下整个轨迹,而抬起的时候则不做任何事情。
所以,在onTouchEvent事件函数中,我们的代码这样写:
switch (touchEvent.getAction()) {
case TouchEvent.PRIMARY_POINT_DOWN: {
MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
mPath.moveTo(point.getX(), point.getY());
mPrePoint.position[0] = point.getX();
mPrePoint.position[1] = point.getY();
mPreCtrlPoint.position[0] = point.getX();
mPreCtrlPoint.position[1] = point.getY();
return true;
}
case TouchEvent.PRIMARY_POINT_UP:
break;
case TouchEvent.POINT_MOVE: {
break;
}
MmiPoint :表示是人机交互接口的一个Point,这里用来接收点击事件的点,需要import ohos.multimodalinput.event.MmiPoint;
然后在点击下去的这一下,指定路径mPath的moveTo目标为当前事件点击获得的点。
同时也设置了两个预制缓存点的坐标为当前点击的点。
抬起的操作,我们这里暂时不做处理。
直接来处理下移动分支下的操作:
case TouchEvent.POINT_MOVE: {
MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
Point currCtrlPoint = new Point((point.getX() + mPrePoint.position[0]) / 2,
(point.getY() + mPrePoint.position[1]) / 2);
mPath.cubicTo(mPrePoint, mPreCtrlPoint, currCtrlPoint);
mPreCtrlPoint.position[0] = currCtrlPoint.position[0];
mPreCtrlPoint.position[1] = currCtrlPoint.position[1];
mPrePoint.position[0] = point.getX();
mPrePoint.position[1] = point.getY();
invalidate();
break;
> 解析:
同样用MmiPoint来接收点击输入,然后先说下mPath.cubicTo:使用path的cubicTo方法来实现三次贝塞尔曲线,就是说两个点之间的线有两个控制点。这样可以让曲线更加的平滑,它需要输入三个点的参数,所以,我们之前定义了两个Point变量,这里就需要用上了,整体上的原理就是,先把点击获得第一个点传入到曲线函数中,然后计算当前点击的位置加上第一个点的二分之一偏移量来细化得到一个更小的值来作为第三个参数,而第二个参数,我们让缓存的另一个点直接接收当前点击的点的值,然后传入到第二个参数中,最后又更新当前位置给第一个点,这样第一个点传入(旧的点),加上拖动后的当前点(新点),在当前点的二分偏移量的点,构成了三点传给了曲线函数,最后重新更新旧的点,让旧点变成一个新的位置,再次拖动的时候,就全部有了新的值,形成一个闭环。
invalidate()函数表示申请重新绘制(刷新UI)。
至此,我们就完成了点绘制(画笔)的计算方法。
下一步,我们要实现让画笔的这些点和线呈现到画板(Canvas)上:
追加实现Component.DrawTask的方法:
然后根据提示实现onDraw方法
在onDraw方法中添加如下实现:
canvas.drawPath(mPath, mPaint);
使用canvas的实例方法drawPath来将画笔和路径传入实现绘制。
到这里还没完,我们还需要做一些初始化的工作,我们写一个Init函数:
private void Init()
{
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(5f);
mPaint.setStyle(Paint.Style.STROKE_STYLE);
addDrawTask(this::onDraw);
setTouchEventListener(this::onTouchEvent);
}
这里将画笔实例化,并设置颜色、粗细及样式。然后添加绘制任务addDrawTask、设置点击事件的监听setTouchEventListener,这俩函数分别是画布和事件监听内置的函数,并非自定义的。
最后,我们需要在Draw的构造函数中调用这个 Init()方法,这样就可以在使用new创建这个Draw组件实例时自动初始化。
(三)调用工具
最后时调用我们写的这个绘画工具。
回到slice目录,并打开MainAbilitySlice文件
定义各一个方向布局:
private DirectionalLayout directionalLayout = new DirectionalLayout(this);
在onStart方法中,创建一个布局配置,并将配置指定给方向布局:
LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
directionalLayout.setLayoutConfig(config);
接着创建刚才写好的Draw组件,需要添加import com.qibiao.drawdemo.Draw;
Draw draw = new Draw(this);
设置布局配置
draw.setLayoutConfig(config);
创建背景元素(这里设置为黑色,黑板嚒~)
ShapeElement element = new ShapeElement();
element.setRgbColor(new RgbColor(0, 0, 0));
设置背景元素
draw.setBackground(element);
将组件添加到布局中
directionalLayout.addComponent(draw);
设置UI内容:
super.setUIContent(directionalLayout);
完整代码如下:
(四)运行效果
运行一个TV的远程模拟器,然后run:
手写一个:你好,鸿蒙
看起来还不错(别在意细节~)。
不过,还不够完美,我们再给他弄个擦除的功能。
回到Draw
添加一个重置的方法:
然后再到MainAbilitySlice中添加一个按钮,并调用clear方法:
再次运行已经支持擦除。
(五)完整代码
ok,本篇已经务必尽量精简了,最后放上代码链接(已开源):