2020征文-TV10分鐘鴻蒙應用實戰(zhàn)開發(fā):鴻蒙手繪板 (含源代碼)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
前言:

今天是鴻蒙的手機beta發(fā)布活動,很榮幸受邀來到現(xiàn)場,一會兒可以給大家上個靚照~。
本篇旨在通過實踐一些樣例,讓開發(fā)者們快速提高腎上腺素,歡樂的加入鴻蒙應用開發(fā)之旅。整篇就是一個完整的實操樣例,我也盡量在一片中把內(nèi)容都講清楚。
基礎的一些知識點,可以訪問我另一個系列:《鴻蒙OS應用開發(fā)實踐》
正文
(一)創(chuàng)建項目
1.創(chuàng)建一個新的TV項目:

2.創(chuàng)建一個新的Java類:

命名為Draw:

這個作為我們的繪畫的核心組件,所以我們讓他繼承Component,方便后面的調(diào)用。需要注意的是,這里導入包名的時候,我們選擇第一個:ohos.agp.components包。

完成后,依然會報錯,提示我們需要創(chuàng)建構(gòu)造函數(shù):

同樣默認會有很多構(gòu)造方法,我們選擇第一個(單個參數(shù))即可。

(二)實現(xiàn)繪畫工具
這樣一個基礎的組件類就創(chuàng)建好了,接著我們構(gòu)思下一畫板工具里需要哪些元素:

畫筆:用于畫出各種點和線。
畫板:用于展現(xiàn)我們到底花了什么,它是內(nèi)容的載體。
所以,根據(jù)以上這些元素,在接下來我們需要在代碼里定義和創(chuàng)建一些內(nèi)容:
- 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;
所以上面的代碼,我們先定義了一些等待使用的工具變量。
現(xiàn)在我們?nèi)鄙倭艘粋€東西,那就是如何交互?一般的,繪圖這樣的,我們要么鼠標,要么觸屏,要么就是電子繪筆等。這里我們使用鴻蒙觸摸組件來實現(xiàn)。
在代碼中去實現(xiàn)Component.TouchEventListener方法:

實現(xiàn)onTouchEvent()方法:

onTouchEvent包含兩個參數(shù):Component表示當前接收的組件,TouchEvent表示當前的觸摸事件。

通過getAction實例方法可以獲取TouchEvent的狀態(tài):
- TouchEvent.PRIMARY_POINT_DOWN : 按下狀態(tài)
- TouchEvent.PRIMARY_POINT_UP :點按狀態(tài)抬起
- TouchEvent.POINT_MOVE: 點按拖動
我們需要在按下的時候開始記錄點的位置,拖動的時候記錄下整個軌跡,而抬起的時候則不做任何事情。
所以,在onTouchEvent事件函數(shù)中,我們的代碼這樣寫:
- 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方法來實現(xiàn)三次貝塞爾曲線,就是說兩個點之間的線有兩個控制點。這樣可以讓曲線更加的平滑,它需要輸入三個點的參數(shù),所以,我們之前定義了兩個Point變量,這里就需要用上了,整體上的原理就是,先把點擊獲得第一個點傳入到曲線函數(shù)中,然后計算當前點擊的位置加上第一個點的二分之一偏移量來細化得到一個更小的值來作為第三個參數(shù),而第二個參數(shù),我們讓緩存的另一個點直接接收當前點擊的點的值,然后傳入到第二個參數(shù)中,最后又更新當前位置給第一個點,這樣第一個點傳入(舊的點),加上拖動后的當前點(新點),在當前點的二分偏移量的點,構(gòu)成了三點傳給了曲線函數(shù),最后重新更新舊的點,讓舊點變成一個新的位置,再次拖動的時候,就全部有了新的值,形成一個閉環(huán)。
invalidate()函數(shù)表示申請重新繪制(刷新UI)。
至此,我們就完成了點繪制(畫筆)的計算方法。
下一步,我們要實現(xiàn)讓畫筆的這些點和線呈現(xiàn)到畫板(Canvas)上:

追加實現(xiàn)Component.DrawTask的方法:
然后根據(jù)提示實現(xiàn)onDraw方法
在onDraw方法中添加如下實現(xiàn):
- canvas.drawPath(mPath, mPaint);
使用canvas的實例方法drawPath來將畫筆和路徑傳入實現(xiàn)繪制。
到這里還沒完,我們還需要做一些初始化的工作,我們寫一個Init函數(shù):
- 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、設置點擊事件的監(jiān)聽setTouchEventListener,這倆函數(shù)分別是畫布和事件監(jiān)聽內(nèi)置的函數(shù),并非自定義的。
最后,我們需要在Draw的構(gòu)造函數(shù)中調(diào)用這個 Init()方法,這樣就可以在使用new創(chuàng)建這個Draw組件實例時自動初始化。
(三)調(diào)用工具
最后時調(diào)用我們寫的這個繪畫工具。
回到slice目錄,并打開MainAbilitySlice文件

定義各一個方向布局:
- private DirectionalLayout directionalLayout = new DirectionalLayout(this);
在onStart方法中,創(chuàng)建一個布局配置,并將配置指定給方向布局:
- LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
- directionalLayout.setLayoutConfig(config);
接著創(chuàng)建剛才寫好的Draw組件,需要添加import com.qibiao.drawdemo.Draw;
- Draw draw = new Draw(this);
設置布局配置
- draw.setLayoutConfig(config);
創(chuàng)建背景元素(這里設置為黑色,黑板嚒~)
- ShapeElement element = new ShapeElement();
- element.setRgbColor(new RgbColor(0, 0, 0));
設置背景元素
- draw.setBackground(element);
將組件添加到布局中
- directionalLayout.addComponent(draw);
設置UI內(nèi)容:
- super.setUIContent(directionalLayout);
完整代碼如下:
(四)運行效果
運行一個TV的遠程模擬器,然后run:
手寫一個:你好,鴻蒙
看起來還不錯(別在意細節(jié)~)。
不過,還不夠完美,我們再給他弄個擦除的功能。
回到Draw
添加一個重置的方法:

然后再到MainAbilitySlice中添加一個按鈕,并調(diào)用clear方法:

再次運行已經(jīng)支持擦除。
(五)完整代碼
ok,本篇已經(jīng)務必盡量精簡了,最后放上代碼鏈接(已開源):
https://gitee.com/doufx/draw-component
©著作權(quán)歸作者和HarmonyOS技術(shù)社區(qū)共同所有,如需轉(zhuǎn)載,請注明出處,否則將追究法律責任。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz