本篇wiki将通过新增一个MyCircle组件为例,向大家展示新增一个Ace2.0组件的全流程。
可点击的展示类组件,展示一个圆,支持设置半径、边缘宽度和边缘颜色,可以通过点击事件获得当前圆的半径和边缘宽度。
手机/平板 | 智慧屏 | 智能穿戴 |
---|---|---|
支持 | 支持 | 支持 |
无
接口名称 | 参数 | 默认值 | 参数描述 |
---|---|---|---|
MyCircle | - | - | 展示一个圆,支持设置半径、边缘宽度和边缘颜色,可以通过点击事件获得当前圆的半径和边缘宽度。 |
名称 | 属性类型 | 默认值 | 描述 |
---|---|---|---|
circleRadius | Length | 20 | 默认半径。 |
circleEdge | {edgeColor?: Color, edgeWidth?: Length} | {edgeColor: Red, edgeWidth: 1} | 默认边缘颜色和宽度。 |
名称 | 参数类型 | 描述 |
---|---|---|
onCircleClick | (event: {radius: number, edgeWidth: number}) => void | 点击MyCircle组件时触发该回调,返回当前circle的半径和边缘宽度,单位是px。 |
@Entry
@Component
struct my_test_page {
@State radiusOfMyCircle: number = -1
@State edgeWidthOfMyCircle: number = -1
build() {
Column() {
Text("MyCircle的半径为:" + this.radiusOfMyCircle)
.fontSize(20)
Text("MyCircle的边缘宽度为:" + this.edgeWidthOfMyCircle)
.fontSize(20)
MyCircle()
.circleRadius('40px')
.circleEdge({ edgeColor: 'rgb(255, 0, 0)', edgeWidth: 2})
.onCircleClick((event: {radius: number, edgeWidth: number}) => {
this.radiusOfMyCircle = event.radius
this.edgeWidthOfMyCircle = event.edgeWidth
})
}.alignItems(HorizontalAlign.Center)
.width(500)
.height(2000)
}
}
该界面最终效果如下图所示:
ets
的属性解析ets-loader
中增加新组件的属性定义component_map.ts
中增加MyCircle
定义文件路径为:REPO_ROOT\foundation\ace\huawei_proprietary\tools\ets-loader\compiler\src\component_map.ts
const COMPONENT_MAP: any = {
/* .............................. */
/* definition of other components */
/* .............................. */
// add definition of MyCircle
MyCircle: {
atomic: true,
attrs: [
'circleRadius', 'circleEdge', 'onCircleClick'
]
}
};
mycircle.d.ts
**文件文件路径为:REPO_ROOT\foundation\ace\huawei_proprietary\tools\ets-loader\compiler\declarations\mycircle.d.ts
interface MyCircle extends CommonMethod<MyCircle> {
(): MyCircle;
circleRadius(value: string | number): MyCircle;
circleEdge(value: {edgeColor?: Color | string | number, edgeWidth?: string | number}): MyCircle;
onCircleClick(callback: (event: {radius: number, edgeWidth: number}) => void): MyCircle;
}
declare const MyCircle: MyCircle;
circleRadius(value: string | number)
:
因为circleRadius
的属性类型为Length
,所以这个属性要支持纯数字(.circleRadius(40)
)以及带单位的字符串(.circleRadius('40px')
)写法。
circleEdge(value: {edgeColor?: Color | string | number, edgeWidth?: string | number})
:
其中,edgeColor
后的问号表示该属性并未必填项,与属性方法中的定义一致。
只有在ets-loader
中增加了新组件的属性定义,才能正常编译带有新组件界面的ets文件。
JSMyCircle
类js_mycircle.h
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\jsview\js_mycircle.h
class JSMyCircle : public JSViewAbstract, public JSInteractableView {
public:
static void Create();
static void JSBind(BindingTarget globalObj);
// parse and set attributes to MyCircleComponent
static void SetCircleRadius(const JSCallbackInfo& info);
static void SetCircleEdge(const JSCallbackInfo& info);
// parse and set click event to MyCircleComponent
static void SetCircleClick(const JSCallbackInfo& info);
};
JSMyCircle
多重继承自JSViewAbstract
和JSInteractableView
,主要功能是解析并注册MyCircle组件的属性和事件。
JSViewAbstract
:绘制相关属性解析和注册的基类。
JSInteractableView
:交互相关事件解析和注册的基类。
// TODO:增加JSViewAbstract、JSInteractableView类说明
js_mycircle.cpp
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\jsview\js_mycircle.cpp
一、 在JSMyCircle::JSBind
中绑定相关函数
void JSMyCircle::JSBind(BindingTarget globalObj)
{
// Step1: indispensable step
// Declare [MyCircle] and bind [Create] function to start parsing MyCircle component
JSClass<JSMyCircle>::Declare("MyCircle");
MethodOptions opt = MethodOptions::NONE;
JSClass<JSMyCircle>::StaticMethod("create", &JSMyCircle::Create, opt);
// Step2: bind funcitons that parse attributes and set to MyCircleComponent
JSClass<JSMyCircle>::StaticMethod("circleRadius", &JSMyCircle::SetCircleRadius);
JSClass<JSMyCircle>::StaticMethod("circleEdge", &JSMyCircle::SetCircleEdge);
// Step3: bind functions that parse event and set to MyCircleComponent
JSClass<JSMyCircle>::StaticMethod("onCircleClick", &JSMyCircle::SetCircleClick);
// Step4: indispensable step
// Declare that this is a subclass of JSViewAbstract
JSClass<JSMyCircle>::Inherit<JSViewAbstract>();
JSClass<JSMyCircle>::Bind<>(globalObj);
}
二、创建MyCircleComponent
用以设置相关属性
void JSMyCircle::Create()
{
RefPtr<Component> myCircleComponent = AceType::MakeRefPtr<OHOS::Ace::MyCircleComponent>();
ViewStackProcessor::GetInstance()->Push(myCircleComponent);
}
三、解析属性样式
void JSMyCircle::SetCircleRadius(const JSCallbackInfo& info)
{
if (info.Length() < 1) {
LOGE("The arg is wrong, it is supposed to have at least 1 arguments");
return;
}
if (!info[0]->IsNumber() && !info[0]->IsString()) {
LOGE("Arg is not Number or String.");
return;
}
auto myCircle = AceType::DynamicCast<MyCircleComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
if (!myCircle) {
LOGE("MyCircleComponent is null.");
return;
}
if (info[0]->IsNumber()) {
// when the [circleRadius] is set with a pure number, use [vp] unit as default
myCircle->SetCircleRadius(Dimension(info[0]->ToNumber<double>(), DimensionUnit::VP));
} else {
myCircle->SetCircleRadius(StringUtils::StringToDimension(info[0]->ToString(), true));
}
}
void JSMyCircle::SetCircleEdge(const JSCallbackInfo& info)
{
if (info.Length() < 1) {
LOGE("The arg is wrong, it is supposed to have atleast 1 argument.");
return;
}
if (!info[0]->IsObject()) {
LOGE("Arg is not a object.");
return;
}
auto myCircle = AceType::DynamicCast<MyCircleComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
if (!myCircle) {
LOGE("MyCircleComponent is null.");
return;
}
JSRef<JSObject> edgeSettingObj = JSRef<JSObject>::Cast(info[0]);
// parse [edgeWidth] in [circleEdge]
JSRef<JSVal> edgeWidth = edgeSettingObj->GetProperty("edgeWidth");
if (edgeWidth->IsNumber()) {
// when the [edgeWidth] is set with a pure number, use [vp] unit as default
myCircle->SetEdgeWidth(Dimension(edgeWidth->ToNumber<double>(), DimensionUnit::VP));
} else {
myCircle->SetEdgeWidth(StringUtils::StringToDimension(edgeWidth->ToString(), true));
}
// parse [edgeColor] in [circleEdge]
JSRef<JSVal> colorValue = edgeSettingObj->GetProperty("edgeColor");
if (colorValue->IsString()) {
myCircle->SetEdgeColor(Color::FromString(colorValue->ToString()));
} else if (colorValue->IsNumber()) {
myCircle->SetEdgeColor(Color(ColorAlphaAdapt(colorValue->ToNumber<uint32_t>())));
}
}
四、解析组件事件
JSRef<JSVal> MyCircleClickEventToJSValue(const MyCircleClickEvent& eventInfo)
{
JSRef<JSObject> obj = JSRef<JSObject>::New();
obj->SetProperty("radius", eventInfo.GetRadius());
obj->SetProperty("edgeWidth", eventInfo.GetEdgeWidth());
return JSRef<JSVal>::Cast(obj);
}
void JSMyCircle::SetCircleClick(const JSCallbackInfo& info)
{
if (!info[0]->IsFunction()) {
LOGE("The arg is not a function.");
return;
}
auto circleClickFunc = AceType::MakeRefPtr<JsEventFunction<MyCircleClickEvent, 1>>(
JSRef<JSFunc>::Cast(info[0]), MyCircleClickEventToJSValue);
auto myCircle = AceType::DynamicCast<MyCircleComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
if (!myCircle) {
LOGE("myCircle component is null");
return;
}
myCircle->SetCircleClickEvent(EventMarker([execCtx = info.GetExecutionContext(), func = std::move(circleClickFunc)]
(const BaseEventInfo* info) {
JAVASCRIPT_EXECUTION_SCOPE(execCtx);
auto eventInfo = TypeInfoHelper::DynamicCast<MyCircleClickEvent>(info);
func->Execute(*eventInfo);
}));
}
JSMyCircle::JSBind()
到JsRegisterViews
中一、添加到REPO_ROOT\foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\engine\quickjs\qjs_view_register.cpp
void JsRegisterViews(BindingTarget global)
{
JSContext* ctx = QJSContext::Current();
QJSUtils::DefineGlobalFunction(ctx, JsLoadDocument, "loadDocument", 1);
QJSUtils::DefineGlobalFunction(ctx, JsDumpMemoryStats, "dumpMemoryStats", 1);
/* ......................... */
/* add JSXXX::JSBind(global) */
/* ......................... */
// add JSMyCircle::JSBind(global)
JSMyCircle::JSBind(global);
/* ......................... */
/* add JSXXX::JSBind(global) */
/* ......................... */
LOGD("View classes and jsCreateDocuemnt, registerObservableObject functions registered.");
}
二、添加到REPO_ROOT\foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\engine\v8\v8_view_register.cpp
void JsRegisterViews(BindingTarget global)
{
JSContext* ctx = QJSContext::Current();
QJSUtils::DefineGlobalFunction(ctx, JsLoadDocument, "loadDocument", 1);
QJSUtils::DefineGlobalFunction(ctx, JsDumpMemoryStats, "dumpMemoryStats", 1);
/* ............................ */
/* add JSXXX::JSBind(globalObj) */
/* ............................ */
// add JSMyCircle::JSBind(global)
JSMyCircle::JSBind(globalObj);
/* ............................ */
/* add JSXXX::JSBind(globalObj) */
/* ............................ */
LOGD("View classes and jsCreateDocuemnt, registerObservableObject functions registered.");
}
组件在后端的布局和绘制,需要相应地新增以下几个类:MyCircleComponent
、MyCircleElement
、RenderMyCircle
、FlutterRenderMyCircle
和MyCircleDeclaration
。
在后端引擎中,Component
树、Element
树和Render
树为后端引擎维持和更新UI最为核心的三棵树。这三棵树之间的更新关系大致如下图所示:
MyCircleDeclaration
类负责保存组件属性样式和事件,JSMyCircle
从ets
中解析出来的内容会通过MyCircleComponent
保存到MyCircleDeclaration
中。
MyCircleDeclaration
类DeclarationConstants
中新增MyCircleDeclaration
的属性、样式、方法定义一、添加到REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\declaration\common\declaration_constants.h
class DeclarationConstants {
public:
/* ............................................. */
/* definition of declaration of other components */
/* ............................................. */
// default value of mycircle
static const std::shared_ptr<MyCircleAttribute> DEFAULT_MYCIRCLE_ATTR;
static const std::shared_ptr<MyCircleStyle> DEFAULT_MYCIRCLE_STYLE;
static const std::shared_ptr<MyCircleEvent> DEFAULT_MYCIRCLE_EVENT;
};
二、添加到REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\declaration\common\declaration_constants.cpp
namespace OHOS::Ace {
/* ................................................. */
/* implementation of declaration of other components */
/* ................................................. */
// default value of mycircle
const std::shared_ptr<MyCircleAttribute> DeclarationConstants::DEFAULT_MYCIRCLE_ATTR =
std::make_shared<MyCircleAttribute>();
const std::shared_ptr<MyCircleStyle> DeclarationConstants::DEFAULT_MYCIRCLE_STYLE = std::make_shared<MyCircleStyle>();
const std::shared_ptr<MyCircleEvent> DeclarationConstants::DEFAULT_MYCIRCLE_EVENT = std::make_shared<MyCircleEvent>();
} // namespace OHOS::Ace
mycircle_declaration.h
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\declaration\mycircle\mycircle_declaration.h
struct MyCircleAttribute : Attribute {
Dimension circleRadius;
};
struct MyCircleStyle : Style {
Dimension edgeWidth = Dimension(1);
Color edgeColor = Color::RED;
};
struct MyCircleEvent : Event {
EventMarker circleClickEvent;
};
class MyCircleDeclaration : public Declaration {
DECLARE_ACE_TYPE(MyCircleDeclaration, Declaration);
public:
MyCircleDeclaration() = default;
~MyCircleDeclaration() override = default;
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;
protected:
void InitSpecialized() override;
};
mycircle_declaration.cpp
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\declaration\mycircle\mycircle_declaration.cpp
一、MyCircleDeclaration::InitSpecialized()
void MyCircleDeclaration::InitSpecialized()
{
AddSpecializedAttribute(DeclarationConstants::DEFAULT_MYCIRCLE_ATTR);
AddSpecializedStyle(DeclarationConstants::DEFAULT_MYCIRCLE_STYLE);
AddSpecializedEvent(DeclarationConstants::DEFAULT_MYCIRCLE_EVENT);
}
注意,若是有自定义的Attribute
、Style
或者Event
,必须重写InitSpecialized()
函数,否则会导致crash。
二、MyCircleAttribute
的Set
和Get
void MyCircleDeclaration::SetCircleRadius(const Dimension& circleRadius)
{
auto& attribute = MaybeResetAttribute<MyCircleAttribute>(AttributeTag::SPECIALIZED_ATTR);
attribute.circleRadius = circleRadius;
}
const Dimension& MyCircleDeclaration::GetCircleRadius() const
{
auto& attribute = static_cast<MyCircleAttribute&>(GetAttribute(AttributeTag::SPECIALIZED_ATTR));
return attribute.circleRadius;
}
三、MyCircleStyle
的Set
和Get
void MyCircleDeclaration::SetEdgeWidth(const Dimension& edgeWidth)
{
auto& style = MaybeResetStyle<MyCircleStyle>(StyleTag::SPECIALIZED_STYLE);
style.edgeWidth = edgeWidth;
}
void MyCircleDeclaration::SetEdgeColor(const Color& edgeColor)
{
auto& style = MaybeResetStyle<MyCircleStyle>(StyleTag::SPECIALIZED_STYLE);
style.edgeColor = edgeColor;
}
const Dimension& MyCircleDeclaration::GetEdgeWidth() const
{
auto& style = static_cast<MyCircleStyle&>(GetStyle(StyleTag::SPECIALIZED_STYLE));
return style.edgeWidth;
}
const Color& MyCircleDeclaration::GetEdgeColor() const
{
auto& style = static_cast<MyCircleStyle&>(GetStyle(StyleTag::SPECIALIZED_STYLE));
return style.edgeColor;
}
四、MyCircleEvent
的Set
和Get
void MyCircleDeclaration::SetCircleClickEvent(const EventMarker& circleClickEvent)
{
auto& event = MaybeResetEvent<MyCircleEvent>(EventTag::SPECIALIZED_EVENT);
event.circleClickEvent = circleClickEvent;
}
const EventMarker& MyCircleDeclaration::GetCircleClickEvent() const
{
auto& event = static_cast<MyCircleEvent&>(GetEvent(EventTag::SPECIALIZED_EVENT));
return event.circleClickEvent;
}
MyCircleComponent
类mycircle_component.h
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\mycircle_component.h
class ACE_EXPORT MyCircleComponent : public RenderComponent {
DECLARE_ACE_TYPE(MyCircleComponent, RenderComponent);
public:
MyCircleComponent();
~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:
RefPtr<MyCircleDeclaration> declaration_;
};
mycircle_component.cpp
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\mycircle_component.cpp
一、构造时初始化declaration
MyCircleComponent::MyCircleComponent()
{
// Init declaration while component is constructing
if (!declaration_) {
declaration_ = AceType::MakeRefPtr<MyCircleDeclaration>();
declaration_->Init();
}
}
二、将MyCircleComponent
的Set
和Get
接口桥接到MyCircleDeclaration
上
void MyCircleComponent::SetCircleRadius(const Dimension& circleRadius)
{
declaration_->SetCircleRadius(circleRadius);
}
void MyCircleComponent::SetEdgeWidth(const Dimension& edgeWidth)
{
declaration_->SetEdgeWidth(edgeWidth);
}
void MyCircleComponent::SetEdgeColor(const Color& edgeColor)
{
declaration_->SetEdgeColor(edgeColor);
}
void MyCircleComponent::SetCircleClickEvent(const EventMarker& circleClickEvent)
{
declaration_->SetCircleClickEvent(circleClickEvent);
}
const Dimension& MyCircleComponent::GetCircleRadius() const
{
return declaration_->GetCircleRadius();
}
const Dimension& MyCircleComponent::GetEdgeWidth() const
{
return declaration_->GetEdgeWidth();
}
const Color& MyCircleComponent::GetEdgeColor() const
{
return declaration_->GetEdgeColor();
}
const EventMarker& MyCircleComponent::GetCircleClickEvent() const
{
return declaration_->GetCircleClickEvent();
}
三、实现CreateRenderNode
和CreateElement
函数
RefPtr<RenderNode> MyCircleComponent::CreateRenderNode()
{
return RenderMyCircle::Create();
}
RefPtr<Element> MyCircleComponent::CreateElement()
{
return AceType::MakeRefPtr<MyCircleElement>();
}
MyCircleElement
类mycircle_element.h
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\mycircle_element.h
class MyCircleElement : public RenderElement {
DECLARE_ACE_TYPE(MyCircleElement, RenderElement);
public:
MyCircleElement() = default;
~MyCircleElement() override = default;
};
该组件在element
层不涉及更多操作,只需要定义MyCircleElement
类即可。
RenderMyCircle
类render_mycircle.h
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\render_mycircle.h
using CircleClickCallback = std::function<void(const std::shared_ptr<BaseEventInfo>&)>;
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;
CircleClickCallback circleClickCallback_;
RefPtr<ClickRecognizer> clickRecognizer_;
};
render_mycircle.cpp
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\render_mycircle.cpp
一、处理点击事件
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)
{
circleClickCallback_(std::make_shared<MyCircleClickEvent>(NormalizeToPx(circleRadius_), NormalizeToPx(edgeWidth_)));
}
1、创建一个ClickRecognizer
;
2、重写OnTouchTestHit
函数,注册RenderMyCircle
的ClickRecognizer
,这样在接收到点击事件时即可触发创建ClickRecognizer
时添加的事件回调;
3、实现在接收到点击事件之后的处理逻辑HandleMyCircleClickEvent
二、重写Update
函数
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();
circleClickCallback_ = AceAsyncEvent<void(const std::shared_ptr<BaseEventInfo>&)>::Create(
myCircleComponent->GetCircleClickEvent(), context_);
// call [MarkNeedLayout] to do [PerformLayout] with new params
MarkNeedLayout();
}
Update
函数负责从MyCircleComponent
获取所有绘制、布局和事件相关的属性更新。
三、重写PerformLayout
函数
void RenderMyCircle::PerformLayout()
{
double realSize = NormalizeToPx(edgeWidth_) + 2 * NormalizeToPx(circleRadius_);
Size layoutSizeAfterConstrain = GetLayoutParam().Constrain(Size(realSize, realSize));
SetLayoutSize(layoutSizeAfterConstrain);
}
PerformLayout
函数负责计算布局信息,并且调用SetLayoutSize
函数设置自己所需要的布局大小。
FlutterRenderMyCircle
类flutter_render_mycircle.h
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\flutter_render_mycircle.h
class FlutterRenderMyCircle final : public RenderMyCircle {
DECLARE_ACE_TYPE(FlutterRenderMyCircle, RenderMyCircle);
public:
FlutterRenderMyCircle() = default;
~FlutterRenderMyCircle() override = default;
void Paint(RenderContext& context, const Offset& offset) override;
};
flutter_render_mycircle.cpp
文件路径:REPO_ROOT\foundation\ace\ace_engine\frameworks\core\components\mycircle\flutter_render_mycircle.cpp
一、实现RenderMyCircle::Create()
函数
RefPtr<RenderNode> RenderMyCircle::Create()
{
return AceType::MakeRefPtr<FlutterRenderMyCircle>();
}
RenderMyCircle::Create()
在基类RenderMyCircle
中定义,因为我们当前使用的是flutter
引擎,所以在flutter_render_mycircle.cpp
里面实现,返回在flutter
引擎上自渲染的FlutterRenderMyCircle
类。
二、重写Paint
函数
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
组件进行进一步的验证。(以下验证工作都假设在Windows
环境下的IDE
预览器上进行。)
验证工作主要由以下几个步骤组成:
- 编译
IDE工具
所需的sdk
文件和预览器所需的dll
文件。(编译命令已放在步骤后)sdk
主要包含ets
相关文件的修改;dll
文件包含将代码进行打包后生成的文件。- 将
步骤1
中编出的sdk
与dll
文件在IDE工具
所在的目录下进行替换。- 编写示例项中的代码,并在右侧工具栏选择
Previewer
选项打开预览器插件观察效果。(建议使用悬浮窗口便于观察,可以采用鼠标对上述组件进行点击。)- 如果需要更改代码并观察效果,首先需要关闭
Previewer
插件以防止该插件因为占用而替换dll
文件失败,然后重复完成上述步骤1
和步骤2
即可。
编译命令:
参考官方指导,下载OpenHarmony完整代码,需要在Linux的环境下执行以下命令进行交叉编译:
# 编译dll文件
./build.sh --export-para PYCACHE_ENABLE:true --product-name Hi3516DV300 --ccache
# 编译sdk命令
./build.sh --product-name ohos-sdk
获取编译产物:
在out/sdk/ohos-sdk/windows/previewer
目录下拷贝所有文件,包含多个exe
及dll
库文件。
在out/sdk/ohos-sdk/windows/ets/build-tools/ets-loader
目录下,包含sdk
相关文件。
替换sdk
与dll
文件:
替换sdk
文件:将out/sdk/ohos-sdk/windows/ets/build-tools/ets-loader
下的文件替换至Openharmony SDK
所在目录的路径为[Openharmony SDK]\[sdk对应版本号]的目录下
,替换完成后进入build-tools\ets-loader\
,执行npm install
即可。
替换dll
文件:将out/sdk/ohos-sdk/windows/previewer/common/bin
下的libace_engine_windows.dll
文件替换至Openharmony SDK
所在目录的路径为[Openharmony SDK]\[sdk对应版本号]\previewer\common\bin
目录下对应的文件即可。
准备应用程序:
新建IDE
工程,选择[Standard]Empty Ability
,在index.dts
文件中加入示例代码。
运行:
在IDE工具
右侧选择Previewer
选项进行预览操作。如若需要修改代码查看效果,编写完代码重复编译命令、获取编译产物、替换sdk
与dll
文件、运行步骤即可。
到这里,新增一个MyCircle
组件所需的所有步骤都已经完成,我们可以展示一个圆,支持设置半径、边缘宽度和边缘颜色,可以通过点击事件获得当前圆的半径和边缘宽度。
当然MyCircle
组件是比较简单的示例组件,Ace开发框架支持更多更复杂的组件开发,比如提供单行文本输入组件TextInput
、提供日历展示的Calendar
组件等,更多的用法期待你来探索~
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。