mirror of
https://gitee.com/openharmony/arkui_ace_engine
synced 2024-12-02 12:29:44 +00:00
606 lines
18 KiB
Markdown
606 lines
18 KiB
Markdown
# JS UI开发框架新增组件开发指南:以新增MyCircle组件为例
|
||
|
||
本篇wiki将通过新增一个MyCircle组件为例,向大家展示新增一个JS UI组件的全流程。
|
||
|
||
完整的patch链接:https://gitee.com/theretherehuh/ace_ace_engine/pulls/1/files
|
||
|
||
|
||
|
||
### mycircle
|
||
|
||
可点击的展示类组件,展示一个圆,支持设置半径、边缘宽度和边缘颜色,可以通过点击事件获得当前圆的半径和边缘宽度。
|
||
|
||
#### 支持设备
|
||
|
||
| 手机 | 智慧屏 | 智能穿戴 | 轻量级智能穿戴 | 轻车机 |
|
||
| ---- | ------ | -------- | -------------- | ------ |
|
||
| 支持 | 支持 | 支持 | 支持 | 支持 |
|
||
|
||
#### 子组件
|
||
|
||
无
|
||
|
||
#### 属性
|
||
|
||
| 名称 | 属性类型 | 默认值 | 必填 | 描述 |
|
||
| ------------ | -------- | ------ | ---- | ---------- |
|
||
| circleradius | length | 20vp | 否 | 默认半径。 |
|
||
|
||
#### 样式
|
||
|
||
| 名称 | 属性类型 | 默认值 | 必填 | 描述 |
|
||
| ---------- | ------------ | ------- | ---- | -------------------- |
|
||
| circleedge | length color | 2vp red | 否 | 默认边缘颜色和宽度。 |
|
||
|
||
#### 事件
|
||
|
||
| 名称 | 参数类型 | 描述 |
|
||
| ----------- | ----------------------------------------------------- | ------------------------------------------------------------ |
|
||
| circleclick | {radius: circle radius, edgewidth: circle edge width} | 点击MyCircle组件时触发该回调,返回当前circle的半径和边缘宽度,单位是px。 |
|
||
|
||
#### 示例
|
||
|
||
```typescript
|
||
<!-- xxx.hml -->
|
||
<div style="flex-direction: column;align-items: center;">
|
||
<text>"MyCircle的半径为:{{radiusOfMyCircle}}"</text>
|
||
<text>"MyCircle的边缘宽度为:{{edgeWidthOfMyCircle}}"</text>
|
||
<mycircle circleradius="40vp" style="circleedge: 2vp red;" @circleclick="onCircleClick"> </mycircle>
|
||
</div>
|
||
```
|
||
|
||
```js
|
||
// xxx.js
|
||
export default{
|
||
data:{
|
||
radiusOfMyCircle: -1,
|
||
edgeWidthOfMyCircle: -1,
|
||
},
|
||
onCircleClick(event) {
|
||
this.radiusOfMyCircle = event.radius
|
||
this.edgeWidthOfMyCircle = event.edgewidth
|
||
}
|
||
}
|
||
```
|
||
|
||
该界面最终效果如下图所示:
|
||
|
||
![](https://gitee.com/theretherehuh/ace_ace_engine/raw/resources/mycircle.gif)
|
||
|
||
|
||
|
||
### 1. `js`的界面解析
|
||
|
||
#### 1.1 `dom_type`中增加新组件的属性定义
|
||
|
||
##### 1.1.1 在`dom_type.h`中增加`MyCircle`的属性定义
|
||
|
||
文件路径为:`frameworks\bridge\common\dom\dom_type.h`
|
||
|
||
```c++
|
||
// node tag defines
|
||
/* .................................... */
|
||
/* node tag defines of other components */
|
||
/* .................................... */
|
||
ACE_EXPORT extern const char DOM_NODE_TAG_MYCIRCLE[];
|
||
|
||
/* ........................... */
|
||
/* defines of other components */
|
||
/* ........................... */
|
||
|
||
// mycircle defines
|
||
ACE_EXPORT extern const char DOM_MYCIRCLE_CIRCLE_EDGE[];
|
||
ACE_EXPORT extern const char DOM_MYCIRCLE_CIRCLE_RADIUS[];
|
||
ACE_EXPORT extern const char DOM_MYCIRCLE_CIRCLE_CLICK[];
|
||
```
|
||
|
||
|
||
|
||
##### 1.1.2 在`dom_type.cpp`中增加`MyCircle`的属性值
|
||
|
||
文件路径为:`frameworks\bridge\common\dom\dom_type.cpp`
|
||
|
||
```c++
|
||
// node tag defines
|
||
/* .................................... */
|
||
/* node tag defines of other components */
|
||
/* .................................... */
|
||
const char DOM_NODE_TAG_MYCIRCLE[] = "mycircle";
|
||
|
||
/* ........................... */
|
||
/* defines of other components */
|
||
/* ........................... */
|
||
|
||
// mycircle defines
|
||
const char DOM_MYCIRCLE_CIRCLE_EDGE[] = "circleedge";
|
||
const char DOM_MYCIRCLE_CIRCLE_RADIUS[] = "circleradius";
|
||
const char DOM_MYCIRCLE_CIRCLE_CLICK[] = "circleclick";
|
||
```
|
||
|
||
|
||
|
||
#### 1.2 新增`DOMMyCircle`类
|
||
|
||
##### 1.2.1 新增`dom_mycircle.h`
|
||
|
||
文件路径:`frameworks\bridge\common\dom\dom_mycircle.h`
|
||
|
||
```c++
|
||
class DOMMyCircle final : public DOMNode {
|
||
DECLARE_ACE_TYPE(DOMMyCircle, DOMNode);
|
||
|
||
public:
|
||
DOMMyCircle(NodeId nodeId, const std::string& nodeName);
|
||
~DOMMyCircle() override = default;
|
||
|
||
RefPtr<Component> GetSpecializedComponent() override
|
||
{
|
||
return myCircleChild_;
|
||
}
|
||
|
||
protected:
|
||
bool SetSpecializedAttr(const std::pair<std::string, std::string>& attr) override;
|
||
bool SetSpecializedStyle(const std::pair<std::string, std::string>& style) override;
|
||
bool AddSpecializedEvent(int32_t pageId, const std::string& event) override;
|
||
|
||
private:
|
||
RefPtr<MyCircleComponent> myCircleChild_;
|
||
};
|
||
```
|
||
|
||
`DOMMyCircle`继承自`DOMNode`,主要功能是解析界面并生成相应的`Component`节点。
|
||
|
||
|
||
|
||
##### 1.2.2 新增`dom_mycircle.cpp`
|
||
|
||
文件路径:`frameworks\bridge\common\dom\dom_mycircle.cpp`
|
||
|
||
**一、组件属性的解析:`SetSpecializedAttr`**
|
||
|
||
```c++
|
||
bool DOMMyCircle::SetSpecializedAttr(const std::pair<std::string, std::string>& attr)
|
||
{
|
||
if (attr.first == DOM_MYCIRCLE_CIRCLE_RADIUS) { // "circleradius"
|
||
myCircleChild_->SetCircleRadius(StringToDimension(attr.second));
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
```
|
||
|
||
这个方法由框架流程调用,我们只需要在这个方法里面实现对应属性的解析,并且设置到`MyCircleComponent`中。
|
||
|
||
如上代码中,入参`attr`的格式形如`<"circleradius", "40vp">`,则我们只需要判断`attr.first`为`"circleradius"`时,将`attr.second`转换为`Dimension`格式,设置到`MyCircleComponent`中即可。设置完成后,返回`true`。
|
||
|
||
|
||
|
||
**二、组件样式的解析:`SetSpecializedStyle`**
|
||
|
||
```c++
|
||
bool DOMMyCircle::SetSpecializedStyle(const std::pair<std::string, std::string>& style)
|
||
{
|
||
if (style.first == DOM_MYCIRCLE_CIRCLE_EDGE) { // "circleedge"
|
||
std::vector<std::string> edgeStyles;
|
||
// The value of [circleedge] is like "2vp red" or "2vp". To parse style value like this, we need 3 steps.
|
||
// Step1: Split the string value by ' ' to get vectors like ["2vp", "red"].
|
||
StringUtils::StringSpliter(style.second, ' ', edgeStyles);
|
||
Dimension edgeWidth(1, DimensionUnit::VP);
|
||
Color edgeColor(Color::RED);
|
||
|
||
// Step2: Parse edge color and edge width accordingly.
|
||
switch(edgeStyles.size()) {
|
||
case 0: // the value is empty
|
||
LOGW("Value for circle edge is empty, using default setting.");
|
||
break;
|
||
case 1: // case when only edge width is set
|
||
// It should be guaranteed by the tool chain when generating js-bundle that the only value is a
|
||
// number type for edge width rather than a color type for edge color.
|
||
edgeWidth = StringUtils::StringToDimension(edgeStyles[0]);
|
||
break;
|
||
case 2: // case when edge width and edge color are both set
|
||
edgeWidth = StringUtils::StringToDimension(edgeStyles[0]);
|
||
edgeColor = Color::FromString(edgeStyles[1]);
|
||
break;
|
||
default:
|
||
LOGW("There are more than 2 values for circle edge, please check. The value is %{private}s",
|
||
style.second.c_str());
|
||
break;
|
||
}
|
||
|
||
// Step3: Set edge color and edge width to [mycircleStyle].
|
||
myCircleChild_->SetEdgeWidth(edgeWidth);
|
||
myCircleChild_->SetEdgeColor(edgeColor);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
```
|
||
|
||
这个方法由框架流程调用,我们只需要在这个方法里面实现对应样式的解析,并且保存到`MyCircleComponent`中。
|
||
|
||
如上代码中,入参`style`的格式形如`<"circleedge", "2vp red">`,则我们只需要判断`style.first`为`"circleedge"`时,将`style.second`进行解析,设置到`MyCircleComponent`中即可。设置完成后,返回`true`。
|
||
|
||
|
||
|
||
**三、组件事件的解析:`SetSpecializedEvent`**
|
||
|
||
```c++
|
||
bool DOMMyCircle::AddSpecializedEvent(int32_t pageId, const std::string& event)
|
||
{
|
||
if (event == DOM_MYCIRCLE_CIRCLE_CLICK) { // "circleclick"
|
||
myCircleChild_->SetCircleClickEvent(EventMarker(GetNodeIdForEvent(), event, pageId));
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
```
|
||
|
||
这个方法由框架流程调用,我们只需要在这个方法里面实现对应事件的解析,并且保存到`MyCircleComponent`中。
|
||
|
||
如上代码中,只要判断入参`event`的值为`"circleclick"`,则我们只需要使用`eventId`和`pageId`构造一个`EventMarker`,并设置到`MyCircleComponent`中即可。设置完成后,返回`true`。
|
||
|
||
|
||
|
||
#### 1.3 在`dom_document.cpp`里增加`mycircle`组件
|
||
|
||
文件路径:`frameworks\bridge\common\dom\dom_document.cpp`
|
||
|
||
```
|
||
RefPtr<DOMNode> DOMDocument::CreateNodeWithId(const std::string& tag, NodeId nodeId, int32_t itemIndex)
|
||
{
|
||
// code block
|
||
static const LinearMapNode<RefPtr<DOMNode>(*)(NodeId, const std::string&, int32_t)> domNodeCreators[] = {
|
||
// DomNodeCreators of other components
|
||
{ DOM_NODE_TAG_MENU, &DOMNodeCreator<DOMMenu> },
|
||
// "mycircle" must be inserted between "menu" and "navigation-bar"
|
||
{ DOM_NODE_TAG_MYCIRCLE, &DOMNodeCreator<DOMMyCircle> },
|
||
{ DOM_NODE_TAG_NAVIGATION_BAR, &DOMNodeCreator<DomNavigationBar> },
|
||
// DomNodeCreators of other components
|
||
};
|
||
// code block
|
||
return domNode;
|
||
}
|
||
```
|
||
|
||
这里尤其要注意一点,`domNodeCreators[]`是一个线性表,添加`{ DOM_NODE_TAG_MYCIRCLE, &DOMNodeCreator<DOMMyCircle> }`的地方必须要符合字母序。
|
||
|
||
```c++
|
||
DOM_NODE_TAG_MENU[] = "menu",
|
||
DOM_NODE_TAG_NAVIGATION_BAR[] = "navigation-bar",
|
||
DOM_NODE_TAG_MYCIRCLE[] = "mycircle"
|
||
```
|
||
|
||
所以`DOM_NODE_TAG_MYCIRCLE`的记录必须添加在`"menu"`之后,`"navigation-bar"`之前。
|
||
|
||
|
||
|
||
### 2. 后端的布局和绘制
|
||
|
||
组件在后端的布局和绘制,需要相应地新增以下几个类:`MyCircleComponent`、`MyCircleElement`、`RenderMyCircle`、`FlutterRenderMyCircle`。
|
||
|
||
在后端引擎中,`Component`树、`Element`树和`Render`树为后端引擎维持和更新UI最为核心的三棵树。
|
||
|
||
|
||
|
||
#### 2.1 新增`MyCircleComponent`类
|
||
|
||
##### 2.1.1 新增`mycircle_component.h`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\mycircle_component.h`
|
||
|
||
```c++
|
||
class ACE_EXPORT MyCircleComponent : public RenderComponent {
|
||
DECLARE_ACE_TYPE(MyCircleComponent, RenderComponent);
|
||
|
||
public:
|
||
MyCircleComponent() = default;
|
||
~MyCircleComponent() override = default;
|
||
|
||
RefPtr<RenderNode> CreateRenderNode() override;
|
||
RefPtr<Element> CreateElement() override;
|
||
|
||
void SetCircleRadius(const Dimension& circleRadius);
|
||
void SetEdgeWidth(const Dimension& edgeWidth);
|
||
void SetEdgeColor(const Color& edgeColor);
|
||
void SetCircleClickEvent(const EventMarker& circleClickEvent);
|
||
|
||
const Dimension& GetCircleRadius() const;
|
||
const Dimension& GetEdgeWidth() const;
|
||
const Color& GetEdgeColor() const;
|
||
const EventMarker& GetCircleClickEvent() const;
|
||
|
||
private:
|
||
Dimension circleRadius_ = 20.0_vp;
|
||
Dimension edgeWidth_ = 2.0_vp;
|
||
Color edgeColor_ = Color::RED;
|
||
EventMarker circleClickEvent_;
|
||
};
|
||
```
|
||
|
||
|
||
|
||
##### 2.1.2 新增`mycircle_component.cpp`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\mycircle_component.cpp`
|
||
|
||
**一、提供`Set`接口**
|
||
|
||
```c++
|
||
void MyCircleComponent::SetCircleRadius(const Dimension& circleRadius)
|
||
{
|
||
circleRadius_ = circleRadius;
|
||
}
|
||
|
||
void MyCircleComponent::SetEdgeWidth(const Dimension& edgeWidth)
|
||
{
|
||
edgeWidth_ = edgeWidth;
|
||
}
|
||
|
||
void MyCircleComponent::SetEdgeColor(const Color& edgeColor)
|
||
{
|
||
edgeColor_ = edgeColor;
|
||
}
|
||
|
||
void MyCircleComponent::SetCircleClickEvent(const EventMarker& circleClickEvent)
|
||
{
|
||
circleClickEvent_ = circleClickEvent;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
**二、提供`Get`接口**
|
||
|
||
```c++
|
||
const Dimension& MyCircleComponent::GetCircleRadius() const
|
||
{
|
||
return circleRadius_;
|
||
}
|
||
|
||
const Dimension& MyCircleComponent::GetEdgeWidth() const
|
||
{
|
||
return edgeWidth_;
|
||
}
|
||
|
||
const Color& MyCircleComponent::GetEdgeColor() const
|
||
{
|
||
return edgeColor_;
|
||
}
|
||
|
||
const EventMarker& MyCircleComponent::GetCircleClickEvent() const
|
||
{
|
||
return circleClickEvent_;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
**三、实现`CreateRenderNode`和`CreateElement`函数**
|
||
|
||
```c++
|
||
RefPtr<RenderNode> MyCircleComponent::CreateRenderNode()
|
||
{
|
||
return RenderMyCircle::Create();
|
||
}
|
||
|
||
RefPtr<Element> MyCircleComponent::CreateElement()
|
||
{
|
||
return AceType::MakeRefPtr<MyCircleElement>();
|
||
}
|
||
```
|
||
|
||
|
||
|
||
#### 2.2 新增`MyCircleElement`类
|
||
|
||
##### 2.2.1 新增`mycircle_element.h`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\mycircle_element.h`
|
||
|
||
```c++
|
||
class MyCircleElement : public RenderElement {
|
||
DECLARE_ACE_TYPE(MyCircleElement, RenderElement);
|
||
|
||
public:
|
||
MyCircleElement() = default;
|
||
~MyCircleElement() override = default;
|
||
};
|
||
```
|
||
|
||
该组件在`element`层不涉及更多操作,只需要定义`MyCircleElement`类即可。
|
||
|
||
|
||
|
||
#### 2.3 新增`RenderMyCircle`类
|
||
|
||
##### 2.3.1 新增`render_mycircle.h`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\render_mycircle.h`
|
||
|
||
```c++
|
||
using CallbackForJS = std::function<void(const std::string&)>;
|
||
|
||
class RenderMyCircle : public RenderNode {
|
||
DECLARE_ACE_TYPE(RenderMyCircle, RenderNode);
|
||
|
||
public:
|
||
static RefPtr<RenderNode> Create();
|
||
|
||
void Update(const RefPtr<Component>& component) override;
|
||
void PerformLayout() override;
|
||
void HandleMyCircleClickEvent(const ClickInfo& info);
|
||
|
||
protected:
|
||
RenderMyCircle();
|
||
void OnTouchTestHit(
|
||
const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result) override;
|
||
|
||
Dimension circleRadius_;
|
||
Dimension edgeWidth_ = Dimension(1);
|
||
Color edgeColor_ = Color::RED;
|
||
CallbackForJS callbackForJS_; // callback for js frontend
|
||
RefPtr<ClickRecognizer> clickRecognizer_;
|
||
};
|
||
```
|
||
|
||
|
||
|
||
##### 2.3.2 新增`render_mycircle.cpp`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\render_mycircle.cpp`
|
||
|
||
**一、处理点击事件**
|
||
|
||
```c++
|
||
RenderMyCircle::RenderMyCircle()
|
||
{
|
||
clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
|
||
clickRecognizer_->SetOnClick([wp = WeakClaim(this)](const ClickInfo& info) {
|
||
auto myCircle = wp.Upgrade();
|
||
if (!myCircle) {
|
||
LOGE("WeakPtr of RenderMyCircle fails to be upgraded, stop handling click event.");
|
||
return;
|
||
}
|
||
myCircle->HandleMyCircleClickEvent(info);
|
||
});
|
||
}
|
||
|
||
void RenderMyCircle::OnTouchTestHit(
|
||
const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
|
||
{
|
||
clickRecognizer_->SetCoordinateOffset(coordinateOffset);
|
||
result.emplace_back(clickRecognizer_);
|
||
}
|
||
|
||
void RenderMyCircle::HandleMyCircleClickEvent(const ClickInfo& info)
|
||
{
|
||
if (callbackForJS_) {
|
||
auto result = std::string("\"circleclick\",{\"radius\":")
|
||
.append(std::to_string(NormalizeToPx(circleRadius_)))
|
||
.append(",\"edgewidth\":")
|
||
.append(std::to_string(NormalizeToPx(edgeWidth_)))
|
||
.append("}");
|
||
callbackForJS_(result);
|
||
}
|
||
}
|
||
```
|
||
|
||
1、创建一个`ClickRecognizer`;
|
||
|
||
2、重写`OnTouchTestHit`函数,注册`RenderMyCircle`的`ClickRecognizer`,这样在接收到点击事件时即可触发创建`ClickRecognizer`时添加的事件回调;
|
||
|
||
3、实现在接收到点击事件之后的处理逻辑`HandleMyCircleClickEvent`
|
||
|
||
|
||
|
||
**二、重写`Update`函数**
|
||
|
||
```c++
|
||
void RenderMyCircle::Update(const RefPtr<Component>& component)
|
||
{
|
||
const auto& myCircleComponent = AceType::DynamicCast<MyCircleComponent>(component);
|
||
if (!myCircleComponent) {
|
||
LOGE("MyCircleComponent is null!");
|
||
return;
|
||
}
|
||
circleRadius_ = myCircleComponent->GetCircleRadius();
|
||
edgeWidth_ = myCircleComponent->GetEdgeWidth();
|
||
edgeColor_ = myCircleComponent->GetEdgeColor();
|
||
callbackForJS_ =
|
||
AceAsyncEvent<void(const std::string&)>::Create(myCircleComponent->GetCircleClickEvent(), context_);
|
||
|
||
// call [MarkNeedLayout] to do [PerformLayout] with new params
|
||
MarkNeedLayout();
|
||
}
|
||
```
|
||
|
||
`Update`函数负责从`MyCircleComponent`获取所有绘制、布局和事件相关的属性更新。
|
||
|
||
|
||
|
||
**三、重写`PerformLayout`函数**
|
||
|
||
```c++
|
||
void RenderMyCircle::PerformLayout()
|
||
{
|
||
double realSize = NormalizeToPx(edgeWidth_) + 2 * NormalizeToPx(circleRadius_);
|
||
Size layoutSizeAfterConstrain = GetLayoutParam().Constrain(Size(realSize, realSize));
|
||
SetLayoutSize(layoutSizeAfterConstrain);
|
||
}
|
||
```
|
||
|
||
`PerformLayout`函数负责计算布局信息,并且调用`SetLayoutSize`函数设置自己所需要的布局大小。
|
||
|
||
|
||
|
||
#### 2.4 新增`FlutterRenderMyCircle`类
|
||
|
||
##### 2.4.1 新增`flutter_render_mycircle.h`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\flutter_render_mycircle.h`
|
||
|
||
```c++
|
||
class FlutterRenderMyCircle final : public RenderMyCircle {
|
||
DECLARE_ACE_TYPE(FlutterRenderMyCircle, RenderMyCircle);
|
||
|
||
public:
|
||
FlutterRenderMyCircle() = default;
|
||
~FlutterRenderMyCircle() override = default;
|
||
|
||
void Paint(RenderContext& context, const Offset& offset) override;
|
||
};
|
||
```
|
||
|
||
|
||
|
||
##### 2.4.2 新增`flutter_render_mycircle.cpp`
|
||
|
||
文件路径:`frameworks\core\components\mycircle\flutter_render_mycircle.cpp`
|
||
|
||
**一、实现`RenderMyCircle::Create()`函数**
|
||
|
||
```c++
|
||
RefPtr<RenderNode> RenderMyCircle::Create()
|
||
{
|
||
return AceType::MakeRefPtr<FlutterRenderMyCircle>();
|
||
}
|
||
```
|
||
|
||
`RenderMyCircle::Create()`在基类`RenderMyCircle`中定义,因为我们当前使用的是`flutter`引擎,所以在`flutter_render_mycircle.cpp`里面实现,返回在`flutter`引擎上自渲染的`FlutterRenderMyCircle`类。
|
||
|
||
|
||
|
||
**二、重写`Paint`函数**
|
||
|
||
```c++
|
||
void FlutterRenderMyCircle::Paint(RenderContext& context, const Offset& offset)
|
||
{
|
||
auto canvas = ScopedCanvas::Create(context);
|
||
if (!canvas) {
|
||
LOGE("Paint canvas is null");
|
||
return;
|
||
}
|
||
SkPaint skPaint;
|
||
skPaint.setAntiAlias(true);
|
||
skPaint.setStyle(SkPaint::Style::kStroke_Style);
|
||
skPaint.setColor(edgeColor_.GetValue());
|
||
skPaint.setStrokeWidth(NormalizeToPx(edgeWidth_));
|
||
|
||
auto paintRadius = GetLayoutSize().Width() / 2.0;
|
||
canvas->canvas()->drawCircle(offset.GetX() + paintRadius, offset.GetY() + paintRadius,
|
||
NormalizeToPx(circleRadius_), skPaint);
|
||
}
|
||
```
|
||
|
||
`Paint`函数负责调用canvas相应接口去进行绘制,这一步可以认为是新增组件的最后一步,直接决定在屏幕上绘制什么样的UI界面。
|
||
|
||
|
||
|
||
### 小结
|
||
|
||
到这里,新增一个`MyCircle`组件所需的所有步骤都已经完成,我们可以展示一个圆,支持设置半径、边缘宽度和边缘颜色,可以通过点击事件获得当前圆的半径和边缘宽度。
|
||
|
||
当然`MyCircle`组件是比较简单的示例组件,JS UI开发框架支持更多更复杂的组件开发,比如提供单行文本输入组件`TextInput`、提供日历展示的`Calendar`组件等,更多的用法期待你来探索~
|
||
|